diff --git a/ChangeLog b/ChangeLog index d4a98c8..56f1b09 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2007-08-19 Mathias Hasselmann + + * gobject/valacodegenerator.vala: also use _vala_array_free to + release string arrays as g_strfreev cannot handle Vala's sized + arrays, which have a _length variable, but no NULL sentinel. + * gobject/valacodegeneratorsourcefile.vala: enhance _vala_array_free + to handle unsized (NULL sentinel terminated) arrays. refactor the + class to have injection of _vala_array_free and _vala_array_move + in separate methods for code readability. + * tests/test-022.*: test behaviour of _vala_array_free. currently + fails because Vala forgets to release old element members when + assigning a new value. + 2007-08-18 Mathias Hasselmann * gobject/valacodegenerator.vala: terminate array initializer diff --git a/gobject/valacodegenerator.vala b/gobject/valacodegenerator.vala index fd4a7db..089d5e4 100644 --- a/gobject/valacodegenerator.vala +++ b/gobject/valacodegenerator.vala @@ -990,9 +990,7 @@ public class Vala.CodeGenerator : CodeVisitor { ccall.add_argument (new CCodeConstant ("TRUE")); } else if (type.data_type is Array) { var arr = (Array) type.data_type; - if (arr.element_type == string_type.data_type) { - ccall.call = new CCodeIdentifier ("g_strfreev"); - } else if (arr.element_type == null || arr.element_type.is_reference_type ()) { + if (arr.element_type == null || arr.element_type.is_reference_type ()) { requires_array_free = true; bool first = true; diff --git a/gobject/valacodegeneratorsourcefile.vala b/gobject/valacodegeneratorsourcefile.vala index 579e79a..ce7d7c1 100644 --- a/gobject/valacodegeneratorsourcefile.vala +++ b/gobject/valacodegeneratorsourcefile.vala @@ -29,6 +29,113 @@ public class Vala.CodeGenerator { return new CCodeIncludeDirective (filename, context.library == null); } + private CCodeForStatement create_vala_array_free_loop (bool have_length) { + var cbody = new CCodeBlock (); + var cptrarray = new CCodeCastExpression (new CCodeIdentifier ("array"), "gpointer*"); + var cea = new CCodeElementAccess (cptrarray, new CCodeIdentifier ("i")); + + var cfreecall = new CCodeFunctionCall (new CCodeIdentifier ("destroy_func")); + cfreecall.add_argument (cea); + + CCodeExpression cforcond; + + if (have_length) { + var cfreecond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, cea, new CCodeConstant ("NULL")); + cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("array_length")); + cbody.add_statement (new CCodeIfStatement (cfreecond, new CCodeExpressionStatement (cfreecall))); + } else { + cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, cea, new CCodeConstant ("NULL")); + cbody.add_statement (new CCodeExpressionStatement (cfreecall)); + } + + var cfor = new CCodeForStatement (cforcond, cbody); + cfor.add_initializer (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"))); + cfor.add_iterator (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("i"), new CCodeConstant ("1")))); + + return cfor; + } + + private void inject_vala_array_free () { + var fun = new CCodeFunction ("_vala_array_free", "void"); + fun.modifiers = CCodeModifiers.STATIC; + fun.add_parameter (new CCodeFormalParameter ("array", "gpointer")); + fun.add_parameter (new CCodeFormalParameter ("array_length", "gint")); + fun.add_parameter (new CCodeFormalParameter ("destroy_func", "GDestroyNotify")); + source_type_member_declaration.append (fun.copy ()); + + var cdofree = new CCodeBlock (); + + var citdecl = new CCodeDeclaration ("int"); + citdecl.add_declarator (new CCodeVariableDeclarator ("i")); + cdofree.add_statement (citdecl); + + var clencheck = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN_OR_EQUAL, new CCodeIdentifier ("array_length"), new CCodeConstant ("0")); + var ciflen = new CCodeIfStatement (clencheck, create_vala_array_free_loop (true), create_vala_array_free_loop (false)); + cdofree.add_statement (ciflen); + + var ccondarr = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("array"), new CCodeConstant ("NULL")); + var ccondfunc = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("destroy_func"), new CCodeConstant ("NULL")); + var cif = new CCodeIfStatement (new CCodeBinaryExpression (CCodeBinaryOperator.AND, ccondarr, ccondfunc), cdofree); + fun.block = new CCodeBlock (); + fun.block.add_statement (cif); + + var carrfree = new CCodeFunctionCall (new CCodeIdentifier ("g_free")); + carrfree.add_argument (new CCodeIdentifier ("array")); + fun.block.add_statement (new CCodeExpressionStatement (carrfree)); + + source_type_member_definition.append (fun); + } + + private void inject_vala_array_move () { + string_h_needed = true; + + // assumes that overwritten array elements are null before invocation + // FIXME will leak memory if that's not the case + var fun = new CCodeFunction ("_vala_array_move", "void"); + fun.modifiers = CCodeModifiers.STATIC; + fun.add_parameter (new CCodeFormalParameter ("array", "gpointer")); + fun.add_parameter (new CCodeFormalParameter ("element_size", "gsize")); + fun.add_parameter (new CCodeFormalParameter ("src", "gint")); + fun.add_parameter (new CCodeFormalParameter ("dest", "gint")); + fun.add_parameter (new CCodeFormalParameter ("length", "gint")); + source_type_member_declaration.append (fun.copy ()); + + var array = new CCodeIdentifier ("array"); + var element_size = new CCodeIdentifier ("element_size"); + var length = new CCodeIdentifier ("length"); + var src = new CCodeIdentifier ("src"); + var dest = new CCodeIdentifier ("dest"); + var src_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, src, element_size)); + var dest_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, dest, element_size)); + var dest_end_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeParenthesizedExpression (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, dest, length)), element_size)); + + fun.block = new CCodeBlock (); + + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_memmove")); + ccall.add_argument (dest_address); + ccall.add_argument (src_address); + ccall.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length, element_size)); + fun.block.add_statement (new CCodeExpressionStatement (ccall)); + + var czero1 = new CCodeFunctionCall (new CCodeIdentifier ("memset")); + czero1.add_argument (src_address); + czero1.add_argument (new CCodeConstant ("0")); + czero1.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeParenthesizedExpression (new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, dest, src)), element_size)); + var czeroblock1 = new CCodeBlock (); + czeroblock1.add_statement (new CCodeExpressionStatement (czero1)); + + var czero2 = new CCodeFunctionCall (new CCodeIdentifier ("memset")); + czero2.add_argument (dest_end_address); + czero2.add_argument (new CCodeConstant ("0")); + czero2.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeParenthesizedExpression (new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, src, dest)), element_size)); + var czeroblock2 = new CCodeBlock (); + czeroblock2.add_statement (new CCodeExpressionStatement (czero2)); + + fun.block.add_statement (new CCodeIfStatement (new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, src, dest), czeroblock1, czeroblock2)); + + source_type_member_definition.append (fun); + } + public override void visit_source_file (SourceFile! source_file) { header_begin = new CCodeFragment (); header_type_declaration = new CCodeFragment (); @@ -111,95 +218,10 @@ public class Vala.CodeGenerator { source_begin.append (new CCodeMacroReplacement ("VALA_FREE_CHECKED(o,f)", "((o) == NULL ? NULL : ((o) = (f (o), NULL)))")); } if (requires_array_free) { - var fun = new CCodeFunction ("_vala_array_free", "void"); - fun.modifiers = CCodeModifiers.STATIC; - fun.add_parameter (new CCodeFormalParameter ("array", "gpointer")); - fun.add_parameter (new CCodeFormalParameter ("array_length", "gint")); - fun.add_parameter (new CCodeFormalParameter ("destroy_func", "GDestroyNotify")); - source_type_member_declaration.append (fun.copy ()); - - var cdofree = new CCodeBlock (); - - var citdecl = new CCodeDeclaration ("int"); - citdecl.add_declarator (new CCodeVariableDeclarator ("i")); - cdofree.add_statement (citdecl); - - var cbody = new CCodeBlock (); - - var cptrarray = new CCodeCastExpression (new CCodeIdentifier ("array"), "gpointer*"); - var cea = new CCodeElementAccess (cptrarray, new CCodeIdentifier ("i")); - var cfreecond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, cea, new CCodeConstant ("NULL")); - var cfreecall = new CCodeFunctionCall (new CCodeIdentifier ("destroy_func")); - cfreecall.add_argument (cea); - cbody.add_statement (new CCodeIfStatement (cfreecond, new CCodeExpressionStatement (cfreecall))); - - var cforcond = new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("i"), new CCodeIdentifier ("array_length")); - - var cfor = new CCodeForStatement (cforcond, cbody); - cfor.add_initializer (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeConstant ("0"))); - cfor.add_iterator (new CCodeAssignment (new CCodeIdentifier ("i"), new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("i"), new CCodeConstant ("1")))); - cdofree.add_statement (cfor); - - var ccondarr = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("array"), new CCodeConstant ("NULL")); - var ccondfunc = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier ("destroy_func"), new CCodeConstant ("NULL")); - var cif = new CCodeIfStatement (new CCodeBinaryExpression (CCodeBinaryOperator.AND, ccondarr, ccondfunc), cdofree); - fun.block = new CCodeBlock (); - fun.block.add_statement (cif); - - var carrfree = new CCodeFunctionCall (new CCodeIdentifier ("g_free")); - carrfree.add_argument (new CCodeIdentifier ("array")); - fun.block.add_statement (new CCodeExpressionStatement (carrfree)); - - source_type_member_definition.append (fun); + inject_vala_array_free (); } if (requires_array_move) { - string_h_needed = true; - - // assumes that overwritten array elements are null before invocation - // FIXME will leak memory if that's not the case - var fun = new CCodeFunction ("_vala_array_move", "void"); - fun.modifiers = CCodeModifiers.STATIC; - fun.add_parameter (new CCodeFormalParameter ("array", "gpointer")); - fun.add_parameter (new CCodeFormalParameter ("element_size", "gsize")); - fun.add_parameter (new CCodeFormalParameter ("src", "gint")); - fun.add_parameter (new CCodeFormalParameter ("dest", "gint")); - fun.add_parameter (new CCodeFormalParameter ("length", "gint")); - source_type_member_declaration.append (fun.copy ()); - - var array = new CCodeIdentifier ("array"); - var element_size = new CCodeIdentifier ("element_size"); - var length = new CCodeIdentifier ("length"); - var src = new CCodeIdentifier ("src"); - var dest = new CCodeIdentifier ("dest"); - var src_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, src, element_size)); - var dest_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, dest, element_size)); - var dest_end_address = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, array, new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeParenthesizedExpression (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, dest, length)), element_size)); - - fun.block = new CCodeBlock (); - - var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_memmove")); - ccall.add_argument (dest_address); - ccall.add_argument (src_address); - ccall.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, length, element_size)); - fun.block.add_statement (new CCodeExpressionStatement (ccall)); - - var czero1 = new CCodeFunctionCall (new CCodeIdentifier ("memset")); - czero1.add_argument (src_address); - czero1.add_argument (new CCodeConstant ("0")); - czero1.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeParenthesizedExpression (new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, dest, src)), element_size)); - var czeroblock1 = new CCodeBlock (); - czeroblock1.add_statement (new CCodeExpressionStatement (czero1)); - - var czero2 = new CCodeFunctionCall (new CCodeIdentifier ("memset")); - czero2.add_argument (dest_end_address); - czero2.add_argument (new CCodeConstant ("0")); - czero2.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeParenthesizedExpression (new CCodeBinaryExpression (CCodeBinaryOperator.MINUS, src, dest)), element_size)); - var czeroblock2 = new CCodeBlock (); - czeroblock2.add_statement (new CCodeExpressionStatement (czero2)); - - fun.block.add_statement (new CCodeIfStatement (new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, src, dest), czeroblock1, czeroblock2)); - - source_type_member_definition.append (fun); + inject_vala_array_move (); } if (string_h_needed) { diff --git a/tests/test-022.out b/tests/test-022.out index 48a7780..faa113f 100644 --- a/tests/test-022.out +++ b/tests/test-022.out @@ -1 +1,3 @@ One dimensional array creation and assignment: 1 2 3 4 5 6 7 8 9 10 11 +String array creation and assignment: 1 2 3 4 5 6 7 8 9 10 11 +Object array creation and assignment: 1 2 3 4 5 6 7 8 9 10 11 c b a b a c a b c diff --git a/tests/test-022.vala b/tests/test-022.vala index 805d9bd..9b2f4f8 100644 --- a/tests/test-022.vala +++ b/tests/test-022.vala @@ -1,9 +1,18 @@ using GLib; class Maman.Foo { - static int main (string[] args) { + public Foo (construct string bar) { + } + + public ~Foo () { + stdout.printf (" %s", _bar); + } + + public string bar { get; set construct; } + + static void test_integer_array () { stdout.printf ("One dimensional array creation and assignment: 1"); - + int[] a = new int[4] {1,2}; stdout.printf (" 2"); @@ -35,7 +44,93 @@ class Maman.Foo { stdout.printf (" %d", a.length); stdout.printf (" 11\n"); + } + + [NoArrayLength ()] + static string[] create_unsized_string_array () { + return new string[] { "a", "b", "c" }; + } + + static void test_string_array () { + stdout.printf ("String array creation and assignment: 1"); + + var a = new string[3] { "a", "b", "c" }; + var b = new string[] { "a", "b", "c" }; + var c = create_unsized_string_array (); + + if (3 == a.length) { + stdout.printf (" 2"); + } + if (3 == b.length) { + stdout.printf (" 3"); + } + if (-1 == c.length) { + stdout.printf (" 4"); + } + if (null == c[3]) { + stdout.printf (" 5"); + } + + for (int i = 0; i < a.length; ++i) { + if (a[i] == b[i]) { + stdout.printf (" %d", i * 2 + 6); + } + if (a[i] == c[i]) { + stdout.printf (" %d", i * 2 + 7); + } + } + + a[2] = null; + b[1] = null; + + stdout.printf ("\n"); + } + + [NoArrayLength ()] + static Foo[] create_unsized_object_array () { + return new Foo[] { new Foo ("a"), new Foo ("b"), new Foo ("c") }; + } + + static void test_object_array () { + stdout.printf ("Object array creation and assignment: 1"); + + do { + var a = new Foo[3] { new Foo ("a"), new Foo ("b"), new Foo ("c") }; + var b = new Foo[] { new Foo ("a"), new Foo ("b"), new Foo ("c") }; + var c = create_unsized_object_array (); + + if (3 == a.length) { + stdout.printf (" 2"); + } + if (3 == b.length) { + stdout.printf (" 3"); + } + if (-1 == c.length) { + stdout.printf (" 4"); + } + if (null == c[3]) { + stdout.printf (" 5"); + } + + for (int i = 0; i < a.length; ++i) { + if (a[i].bar == b[i].bar) { + stdout.printf (" %d", i * 2 + 6); + } + if (a[i].bar == c[i].bar) { + stdout.printf (" %d", i * 2 + 7); + } + } + + a[2] = null; + b[1] = null; + } while (false); + + stdout.printf ("\n"); + } - return 0; + static void main (string[] args) { + test_integer_array (); + test_string_array (); + test_object_array (); } }