From: Juerg Billeter Date: Sat, 19 Apr 2008 06:35:09 +0000 (+0000) Subject: Add CCodeCreationMethodBinding and CCodeMethodBinding classes X-Git-Tag: VALA_0_3_1~41 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=61461e6900b21338752a906f4d6ce2825f88c207;p=platform%2Fupstream%2Fvala.git Add CCodeCreationMethodBinding and CCodeMethodBinding classes 2008-04-19 Juerg Billeter * gobject/Makefile.am, gobject/valaccodecreationmethodbinding.vala, gobject/valaccodegenerator.vala, gobject/valaccodemethodbinding.vala, vala/valacreationmethod.vala: Add CCodeCreationMethodBinding and CCodeMethodBinding classes svn path=/trunk/; revision=1261 --- diff --git a/ChangeLog b/ChangeLog index 7970b92..8f465da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2008-04-19 Jürg Billeter + * gobject/Makefile.am, gobject/valaccodecreationmethodbinding.vala, + gobject/valaccodegenerator.vala, gobject/valaccodemethodbinding.vala, + vala/valacreationmethod.vala: + + Add CCodeCreationMethodBinding and CCodeMethodBinding classes + +2008-04-19 Jürg Billeter + * vala/valaclass.vala, vala/valainterface.vala, gobject/Makefile.am, gobject/valaccodeassignmentbinding.vala, gobject/valaccodebinding.vala, gobject/valaccodeclassbinding.vala, diff --git a/gobject/Makefile.am b/gobject/Makefile.am index 1787883..7ab1cc7 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -17,12 +17,12 @@ libvala_la_VALASOURCES = \ valaccodebinding.vala \ valaccodeclassbinding.vala \ valaccodecompiler.vala \ + valaccodecreationmethodbinding.vala \ valaccodeelementaccessbinding.vala \ valaccodeexpressionbinding.vala \ valaccodegenerator.vala \ valaccodegeneratorinvocationexpression.vala \ valaccodegeneratormemberaccess.vala \ - valaccodegeneratormethod.vala \ valaccodegeneratorsignal.vala \ valaccodegeneratorsourcefile.vala \ valaccodegeneratorstruct.vala \ diff --git a/gobject/valaccodecreationmethodbinding.vala b/gobject/valaccodecreationmethodbinding.vala new file mode 100644 index 0000000..a76ac53 --- /dev/null +++ b/gobject/valaccodecreationmethodbinding.vala @@ -0,0 +1,58 @@ +/* valaccodecreationmethodbinding.vala + * + * Copyright (C) 2007-2008 Jürg Billeter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Jürg Billeter + */ + +using GLib; +using Gee; + +/** + * The link between a creation method and generated code. + */ +public class Vala.CCodeCreationMethodBinding : CCodeMethodBinding { + public CreationMethod creation_method { get; set; } + + public CCodeCreationMethodBinding (CCodeGenerator codegen, CreationMethod creation_method) { + this.creation_method = creation_method; + this.method = creation_method; + this.codegen = codegen; + } + + public override void emit () { + var m = creation_method; + + if (m.body != null && codegen.current_type_symbol is Class && codegen.current_class.is_subtype_of (codegen.gobject_type)) { + int n_params = 0; + foreach (Statement stmt in m.body.get_statements ()) { + if (!(stmt is ExpressionStatement) || ((ExpressionStatement) stmt).assigned_property () == null) { + m.error = true; + Report.error (stmt.source_reference, "class creation methods only allow property assignment statements"); + return; + } + if (((ExpressionStatement) stmt).assigned_property ().set_accessor.construction) { + n_params++; + } + } + m.n_construction_params = n_params; + } + + base.emit (); + } +} diff --git a/gobject/valaccodegenerator.vala b/gobject/valaccodegenerator.vala index d8e0bcf..40e91dd 100644 --- a/gobject/valaccodegenerator.vala +++ b/gobject/valaccodegenerator.vala @@ -28,7 +28,7 @@ using Gee; * Code visitor generating C Code. */ public class Vala.CCodeGenerator : CodeGenerator { - private CodeContext context; + public CodeContext context; public Symbol root_symbol; public Symbol current_symbol; @@ -77,12 +77,12 @@ public class Vala.CCodeGenerator : CodeGenerator { /* (constant) hash table with all C keywords */ public Gee.Set c_keywords; - private int next_temp_var_id = 0; + public int next_temp_var_id = 0; private int current_try_id = 0; private int next_try_id = 0; public bool in_creation_method = false; private bool in_constructor = false; - private bool current_method_inner_error = false; + public bool current_method_inner_error = false; public DataType bool_type; public DataType char_type; @@ -120,7 +120,7 @@ public class Vala.CCodeGenerator : CodeGenerator { public bool in_plugin = false; public string module_init_param_name; - private bool string_h_needed; + public bool string_h_needed; private bool requires_free_checked; private bool requires_array_free; private bool requires_array_move; @@ -693,6 +693,14 @@ public class Vala.CCodeGenerator : CodeGenerator { return (null != cparenthesized && is_pure_ccode_expression (cparenthesized.inner)); } + public override void visit_method (Method m) { + code_binding (m).emit (); + } + + public override void visit_creation_method (CreationMethod m) { + code_binding (m).emit (); + } + public override void visit_formal_parameter (FormalParameter p) { p.accept_children (this); @@ -2380,7 +2388,7 @@ public class Vala.CCodeGenerator : CodeGenerator { visit_expression (expr); } - private string get_array_length_cname (string array_cname, int dim) { + public string get_array_length_cname (string array_cname, int dim) { return "%s_length%d".printf (array_cname, dim); } @@ -2518,7 +2526,7 @@ public class Vala.CCodeGenerator : CodeGenerator { } } - private string get_delegate_target_cname (string delegate_cname) { + public string get_delegate_target_cname (string delegate_cname) { return "%s_target".printf (delegate_cname); } @@ -3573,6 +3581,71 @@ public class Vala.CCodeGenerator : CodeGenerator { return type; } + public CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) { + if ((type.data_type != null && type.data_type.is_reference_type ()) || type is PointerType || type is ArrayType) { + return new CCodeConstant ("NULL"); + } else if (type.data_type != null && type.data_type.get_default_value () != null) { + return new CCodeConstant (type.data_type.get_default_value ()); + } else if (type.data_type is Struct && initializer_expression) { + // 0-initialize struct with struct initializer { 0 } + // only allowed as initializer expression in C + var clist = new CCodeInitializerList (); + clist.append (new CCodeConstant ("0")); + return clist; + } else if (type.type_parameter != null) { + return new CCodeConstant ("NULL"); + } else if (type is ErrorType) { + return new CCodeConstant ("NULL"); + } + return null; + } + + private CCodeStatement create_property_type_check_statement (Property prop, bool check_return_type, Typesymbol t, bool non_null, string var_name) { + if (check_return_type) { + return create_type_check_statement (prop, prop.type_reference, t, non_null, var_name); + } else { + return create_type_check_statement (prop, new VoidType (), t, non_null, var_name); + } + } + + public CCodeStatement? create_type_check_statement (CodeNode method_node, DataType ret_type, Typesymbol t, bool non_null, string var_name) { + var ccheck = new CCodeFunctionCall (); + + if ((t is Class && ((Class) t).is_subtype_of (gobject_type)) || (t is Interface && !((Interface) t).declaration_only)) { + var ctype_check = new CCodeFunctionCall (new CCodeIdentifier (t.get_upper_case_cname ("IS_"))); + ctype_check.add_argument (new CCodeIdentifier (var_name)); + + CCodeExpression cexpr = ctype_check; + if (!non_null) { + var cnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL")); + + cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cnull, ctype_check); + } + ccheck.add_argument (cexpr); + } else if (!non_null) { + return null; + } else { + var cnonnull = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL")); + ccheck.add_argument (cnonnull); + } + + if (ret_type is VoidType) { + /* void function */ + ccheck.call = new CCodeIdentifier ("g_return_if_fail"); + } else { + ccheck.call = new CCodeIdentifier ("g_return_val_if_fail"); + + var cdefault = default_value_for_type (ret_type, false); + if (cdefault != null) { + ccheck.add_argument (cdefault); + } else { + return new CCodeExpressionStatement (new CCodeConstant ("0")); + } + } + + return new CCodeExpressionStatement (ccheck); + } + public override CodeBinding? create_namespace_binding (Namespace node) { return null; } @@ -3622,7 +3695,7 @@ public class Vala.CCodeGenerator : CodeGenerator { } public override CodeBinding? create_creation_method_binding (CreationMethod node) { - return null; + return new CCodeCreationMethodBinding (this, node); } public override CodeBinding? create_formal_parameter_binding (FormalParameter node) { diff --git a/gobject/valaccodegeneratormethod.vala b/gobject/valaccodegeneratormethod.vala deleted file mode 100644 index d0fb3f9..0000000 --- a/gobject/valaccodegeneratormethod.vala +++ /dev/null @@ -1,854 +0,0 @@ -/* valaccodegeneratormethod.vala - * - * Copyright (C) 2006-2008 Jürg Billeter, Raffaele Sandrini - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Author: - * Jürg Billeter - * Raffaele Sandrini - */ - -using GLib; -using Gee; - -public class Vala.CCodeGenerator { - public override void visit_method (Method m) { - Method old_method = current_method; - DataType old_return_type = current_return_type; - bool old_method_inner_error = current_method_inner_error; - int old_next_temp_var_id = next_temp_var_id; - current_symbol = m; - current_method = m; - current_return_type = m.return_type; - current_method_inner_error = false; - next_temp_var_id = 0; - - bool in_gtypeinstance_creation_method = false; - bool in_gobject_creation_method = false; - bool in_fundamental_creation_method = false; - - var creturn_type = current_return_type; - - if (m is CreationMethod) { - in_creation_method = true; - var cl = current_type_symbol as Class; - if (cl != null && cl.is_subtype_of (gtypeinstance_type)) { - in_gtypeinstance_creation_method = true; - if (cl.base_class == gtypeinstance_type) { - in_fundamental_creation_method = true; - } else if (cl.is_subtype_of (gobject_type)) { - in_gobject_creation_method = true; - } - } - - if (cl != null) { - creturn_type = new ClassType (cl); - } - } - - m.accept_children (this); - - if (m is CreationMethod) { - if (in_gobject_creation_method && m.body != null) { - var cblock = new CCodeBlock (); - - foreach (CodeNode stmt in m.body.get_statements ()) { - if (((ExpressionStatement) stmt).assigned_property ().set_accessor.construction) { - if (stmt.ccodenode is CCodeFragment) { - foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) { - cblock.add_statement (cstmt); - } - } else { - cblock.add_statement (stmt.ccodenode); - } - } - } - - add_object_creation (cblock, ((CreationMethod) m).n_construction_params > 0); - - foreach (CodeNode stmt in m.body.get_statements ()) { - if (!((ExpressionStatement) stmt).assigned_property ().set_accessor.construction) { - if (stmt.ccodenode is CCodeFragment) { - foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) { - cblock.add_statement (cstmt); - } - } else { - cblock.add_statement (stmt.ccodenode); - } - } - } - - m.body.ccodenode = cblock; - } - - in_creation_method = false; - } - - bool inner_error = current_method_inner_error; - - current_symbol = current_symbol.parent_symbol; - current_method = current_method; - current_return_type = old_return_type; - current_method_inner_error = old_method_inner_error; - next_temp_var_id = old_next_temp_var_id; - - if (current_type_symbol != null && current_type_symbol is Interface) { - var iface = (Interface) current_type_symbol; - if (iface.is_static) { - return; - } - } - - function = new CCodeFunction (m.get_real_cname (), creturn_type.get_cname ()); - m.ccodenode = function; - - if (m.is_inline) { - function.modifiers |= CCodeModifiers.INLINE; - } - - var cparam_map = new HashMap (direct_hash, direct_equal); - - CCodeFunctionDeclarator vdeclarator = null; - - if (m.instance || (m.parent_symbol is Struct && m is CreationMethod)) { - Typesymbol parent_type = find_parent_type (m); - DataType this_type; - if (parent_type is Class) { - this_type = new ClassType ((Class) parent_type); - } else if (parent_type is Interface) { - this_type = new InterfaceType ((Interface) parent_type); - } else { - this_type = new ValueType (parent_type); - } - - CCodeFormalParameter instance_param = null; - if (m.base_interface_method != null && !m.is_abstract && !m.is_virtual) { - var base_type = new InterfaceType ((Interface) m.base_interface_method.parent_symbol); - instance_param = new CCodeFormalParameter ("base", base_type.get_cname ()); - } else if (m.overrides) { - var base_type = new ClassType ((Class) m.base_method.parent_symbol); - instance_param = new CCodeFormalParameter ("base", base_type.get_cname ()); - } else { - if (m.parent_symbol is Struct && !((Struct) m.parent_symbol).is_simple_type ()) { - instance_param = new CCodeFormalParameter ("*self", this_type.get_cname ()); - } else { - instance_param = new CCodeFormalParameter ("self", this_type.get_cname ()); - } - } - cparam_map.set (get_param_pos (m.cinstance_parameter_position), instance_param); - - if (m.is_abstract || m.is_virtual) { - var vdecl = new CCodeDeclaration (creturn_type.get_cname ()); - vdeclarator = new CCodeFunctionDeclarator (m.vfunc_name); - vdecl.add_declarator (vdeclarator); - type_struct.add_declaration (vdecl); - } - } - - if (in_fundamental_creation_method) { - cparam_map.set (get_param_pos (0.1), new CCodeFormalParameter ("type", "GType")); - } - - if (in_gobject_creation_method) { - // memory management for generic types - int type_param_index = 0; - foreach (TypeParameter type_param in current_class.get_type_parameters ()) { - cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType")); - cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc")); - cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify")); - type_param_index++; - } - } - - var params = m.get_parameters (); - foreach (FormalParameter param in params) { - if (!param.no_array_length && param.type_reference is ArrayType) { - var array_type = (ArrayType) param.type_reference; - - var length_ctype = "int"; - if (param.direction != ParameterDirection.IN) { - length_ctype = "int*"; - } - - for (int dim = 1; dim <= array_type.rank; dim++) { - var cparam = new CCodeFormalParameter (get_array_length_cname (param.name, dim), length_ctype); - cparam_map.set (get_param_pos (param.carray_length_parameter_position + 0.01 * dim), cparam); - } - } - - cparam_map.set (get_param_pos (param.cparameter_position), (CCodeFormalParameter) param.ccodenode); - - if (param.type_reference is DelegateType) { - var deleg_type = (DelegateType) param.type_reference; - var d = deleg_type.delegate_symbol; - if (d.instance) { - var cparam = new CCodeFormalParameter (get_delegate_target_cname (param.name), "void*"); - cparam_map.set (get_param_pos (param.cdelegate_target_parameter_position), cparam); - } - } - } - - if (!m.no_array_length && creturn_type is ArrayType) { - // return array length if appropriate - var array_type = (ArrayType) creturn_type; - - for (int dim = 1; dim <= array_type.rank; dim++) { - var cparam = new CCodeFormalParameter (get_array_length_cname ("result", dim), "int*"); - cparam_map.set (get_param_pos (m.carray_length_parameter_position + 0.01 * dim), cparam); - } - } else if (creturn_type is DelegateType) { - // return delegate target if appropriate - var deleg_type = (DelegateType) creturn_type; - var d = deleg_type.delegate_symbol; - if (d.instance) { - var cparam = new CCodeFormalParameter (get_delegate_target_cname ("result"), "void*"); - cparam_map.set (get_param_pos (m.cdelegate_target_parameter_position), cparam); - } - } - - if (m.get_error_domains ().size > 0) { - var cparam = new CCodeFormalParameter ("error", "GError**"); - cparam_map.set (get_param_pos (-1), cparam); - } - - // append C parameters in the right order - int last_pos = -1; - int min_pos; - while (true) { - min_pos = -1; - foreach (int pos in cparam_map.get_keys ()) { - if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { - min_pos = pos; - } - } - if (min_pos == -1) { - break; - } - function.add_parameter (cparam_map.get (min_pos)); - if (vdeclarator != null) { - vdeclarator.add_parameter (cparam_map.get (min_pos)); - } - last_pos = min_pos; - } - - bool visible = !m.is_internal_symbol (); - - /* real function declaration and definition not needed - * for abstract methods */ - if (!m.is_abstract) { - if (visible && m.base_method == null && m.base_interface_method == null) { - /* public methods need function declaration in - * header file except virtual/overridden methods */ - header_type_member_declaration.append (function.copy ()); - } else { - /* declare all other functions in source file to - * avoid dependency on order within source file */ - function.modifiers |= CCodeModifiers.STATIC; - source_type_member_declaration.append (function.copy ()); - } - - /* Methods imported from a plain C file don't - * have a body, e.g. Vala.Parser.parse_file () */ - if (m.body != null) { - function.block = (CCodeBlock) m.body.ccodenode; - function.block.line = function.line; - - var cinit = new CCodeFragment (); - function.block.prepend_statement (cinit); - - if (m.parent_symbol is Class) { - var cl = (Class) m.parent_symbol; - if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) { - Method base_method; - ReferenceType base_expression_type; - if (m.overrides) { - base_method = m.base_method; - base_expression_type = new ClassType ((Class) base_method.parent_symbol); - } else { - base_method = m.base_interface_method; - base_expression_type = new InterfaceType ((Interface) base_method.parent_symbol); - } - var self_target_type = new ClassType (cl); - CCodeExpression cself = get_implicit_cast_expression (new CCodeIdentifier ("base"), base_expression_type, self_target_type); - - var cdecl = new CCodeDeclaration ("%s *".printf (cl.get_cname ())); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", cself)); - - cinit.append (cdecl); - } else if (m.instance) { - var ccheckstmt = create_method_type_check_statement (m, creturn_type, cl, true, "self"); - ccheckstmt.line = function.line; - cinit.append (ccheckstmt); - } - } - foreach (FormalParameter param in m.get_parameters ()) { - if (param.ellipsis) { - break; - } - - var t = param.type_reference.data_type; - if (t != null && t.is_reference_type ()) { - if (param.direction != ParameterDirection.OUT) { - var type_check = create_method_type_check_statement (m, creturn_type, t, (context.non_null && !param.type_reference.nullable), param.name); - if (type_check != null) { - type_check.line = function.line; - cinit.append (type_check); - } - } else { - // ensure that the passed reference for output parameter is cleared - var a = new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier (param.name)), new CCodeConstant ("NULL")); - cinit.append (new CCodeExpressionStatement (a)); - } - } - } - - if (inner_error) { - /* always separate error parameter and inner_error local variable - * as error may be set to NULL but we're always interested in inner errors - */ - var cdecl = new CCodeDeclaration ("GError *"); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("inner_error", new CCodeConstant ("NULL"))); - cinit.append (cdecl); - } - - if (m.source_reference != null && m.source_reference.comment != null) { - source_type_member_definition.append (new CCodeComment (m.source_reference.comment)); - } - source_type_member_definition.append (function); - - if (m is CreationMethod) { - if (in_gobject_creation_method) { - int n_params = ((CreationMethod) m).n_construction_params; - - if (n_params > 0) { - // declare construction parameter array - var cparamsinit = new CCodeFunctionCall (new CCodeIdentifier ("g_new0")); - cparamsinit.add_argument (new CCodeIdentifier ("GParameter")); - cparamsinit.add_argument (new CCodeConstant (n_params.to_string ())); - - var cdecl = new CCodeDeclaration ("GParameter *"); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("__params", cparamsinit)); - cinit.append (cdecl); - - cdecl = new CCodeDeclaration ("GParameter *"); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("__params_it", new CCodeIdentifier ("__params"))); - cinit.append (cdecl); - } - - /* type, dup func, and destroy func properties for generic types */ - foreach (TypeParameter type_param in current_class.get_type_parameters ()) { - string func_name; - CCodeMemberAccess cmember; - CCodeAssignment cassign; - - func_name = "%s_type".printf (type_param.name.down ()); - cmember = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); - cassign = new CCodeAssignment (cmember, new CCodeIdentifier (func_name)); - function.block.add_statement (new CCodeExpressionStatement (cassign)); - - func_name = "%s_dup_func".printf (type_param.name.down ()); - cmember = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); - cassign = new CCodeAssignment (cmember, new CCodeIdentifier (func_name)); - function.block.add_statement (new CCodeExpressionStatement (cassign)); - - func_name = "%s_destroy_func".printf (type_param.name.down ()); - cmember = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); - cassign = new CCodeAssignment (cmember, new CCodeIdentifier (func_name)); - function.block.add_statement (new CCodeExpressionStatement (cassign)); - } - } else if (in_fundamental_creation_method) { - var cl = (Class) m.parent_symbol; - var cdecl = new CCodeDeclaration (cl.get_cname () + "*"); - var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_create_instance")); - ccall.add_argument (new CCodeIdentifier ("type")); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", new CCodeCastExpression (ccall, cl.get_cname () + "*"))); - cinit.append (cdecl); - } else if (in_gtypeinstance_creation_method) { - var cl = (Class) m.parent_symbol; - var cdecl = new CCodeDeclaration (cl.get_cname () + "*"); - var fundamental_class = find_fundamental_class (cl); - var ccall = new CCodeFunctionCall (new CCodeIdentifier (fundamental_class.default_construction_method.get_cname ())); - ccall.add_argument (new CCodeIdentifier (cl.get_type_id ())); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", ccall)); - cinit.append (cdecl); - } else if (current_type_symbol is Class) { - var cl = (Class) m.parent_symbol; - var cdecl = new CCodeDeclaration (cl.get_cname () + "*"); - var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0")); - ccall.add_argument (new CCodeIdentifier (cl.get_cname ())); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", ccall)); - cinit.append (cdecl); - } else { - var st = (Struct) m.parent_symbol; - - // memset needs string.h - string_h_needed = true; - var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset")); - czero.add_argument (new CCodeIdentifier ("self")); - czero.add_argument (new CCodeConstant ("0")); - czero.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (st.get_cname ()))); - cinit.append (new CCodeExpressionStatement (czero)); - } - } - - if (context.module_init_method == m && in_plugin) { - // GTypeModule-based plug-in, register types - cinit.append (module_init_fragment); - } - - foreach (Expression precondition in m.get_preconditions ()) { - cinit.append (create_precondition_statement (m, creturn_type, precondition)); - } - } - } - - if (m.is_abstract || m.is_virtual) { - var vfunc = new CCodeFunction (m.get_cname (), creturn_type.get_cname ()); - vfunc.line = function.line; - - ReferenceType this_type; - if (m.parent_symbol is Class) { - this_type = new ClassType ((Class) m.parent_symbol); - } else { - this_type = new InterfaceType ((Interface) m.parent_symbol); - } - - cparam_map = new HashMap (direct_hash, direct_equal); - var carg_map = new HashMap (direct_hash, direct_equal); - - var cparam = new CCodeFormalParameter ("self", this_type.get_cname ()); - cparam_map.set (get_param_pos (m.cinstance_parameter_position), cparam); - - var vblock = new CCodeBlock (); - - foreach (Expression precondition in m.get_preconditions ()) { - vblock.add_statement (create_precondition_statement (m, creturn_type, precondition)); - } - - CCodeFunctionCall vcast = null; - if (m.parent_symbol is Interface) { - var iface = (Interface) m.parent_symbol; - - vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null)))); - } else { - var cl = (Class) m.parent_symbol; - - vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null)))); - } - vcast.add_argument (new CCodeIdentifier ("self")); - - var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, m.vfunc_name)); - carg_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeIdentifier ("self")); - - var params = m.get_parameters (); - foreach (FormalParameter param in params) { - if (!param.no_array_length && param.type_reference is ArrayType) { - var array_type = (ArrayType) param.type_reference; - - var length_ctype = "int"; - if (param.direction != ParameterDirection.IN) { - length_ctype = "int*"; - } - - for (int dim = 1; dim <= array_type.rank; dim++) { - var cparam = new CCodeFormalParameter (get_array_length_cname (param.name, dim), length_ctype); - cparam_map.set (get_param_pos (param.carray_length_parameter_position + 0.01 * dim), cparam); - carg_map.set (get_param_pos (param.carray_length_parameter_position + 0.01 * dim), new CCodeIdentifier (cparam.name)); - } - } - - cparam_map.set (get_param_pos (param.cparameter_position), (CCodeFormalParameter) param.ccodenode); - carg_map.set (get_param_pos (param.cparameter_position), new CCodeIdentifier (param.name)); - - if (param.type_reference is DelegateType) { - var deleg_type = (DelegateType) param.type_reference; - var d = deleg_type.delegate_symbol; - if (d.instance) { - var cparam = new CCodeFormalParameter (get_delegate_target_cname (param.name), "void*"); - cparam_map.set (get_param_pos (param.cdelegate_target_parameter_position), cparam); - carg_map.set (get_param_pos (param.cdelegate_target_parameter_position), new CCodeIdentifier (cparam.name)); - } - } - } - - // return array length if appropriate - if (!m.no_array_length && creturn_type is ArrayType) { - var array_type = (ArrayType) creturn_type; - - for (int dim = 1; dim <= array_type.rank; dim++) { - var cparam = new CCodeFormalParameter (get_array_length_cname ("result", dim), "int*"); - cparam_map.set (get_param_pos (m.carray_length_parameter_position), cparam); - carg_map.set (get_param_pos (m.carray_length_parameter_position), new CCodeIdentifier (cparam.name)); - } - } else if (creturn_type is DelegateType) { - // return delegate target if appropriate - var deleg_type = (DelegateType) creturn_type; - var d = deleg_type.delegate_symbol; - if (d.instance) { - var cparam = new CCodeFormalParameter (get_delegate_target_cname ("result"), "void*"); - cparam_map.set (get_param_pos (m.cdelegate_target_parameter_position), cparam); - carg_map.set (get_param_pos (m.cdelegate_target_parameter_position), new CCodeIdentifier (cparam.name)); - } - } - - if (m.get_error_domains ().size > 0) { - var cparam = new CCodeFormalParameter ("error", "GError**"); - cparam_map.set (get_param_pos (-1), cparam); - carg_map.set (get_param_pos (-1), new CCodeIdentifier (cparam.name)); - } - - - // append C parameters and arguments in the right order - int last_pos = -1; - int min_pos; - while (true) { - min_pos = -1; - foreach (int pos in cparam_map.get_keys ()) { - if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { - min_pos = pos; - } - } - if (min_pos == -1) { - break; - } - vfunc.add_parameter (cparam_map.get (min_pos)); - vcall.add_argument (carg_map.get (min_pos)); - last_pos = min_pos; - } - - CCodeStatement cstmt; - if (creturn_type is VoidType) { - cstmt = new CCodeExpressionStatement (vcall); - } else if (m.get_postconditions ().size == 0) { - /* pass method return value */ - cstmt = new CCodeReturnStatement (vcall); - } else { - /* store method return value for postconditions */ - var cdecl = new CCodeDeclaration (creturn_type.get_cname ()); - cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("result", vcall)); - cstmt = cdecl; - } - cstmt.line = vfunc.line; - vblock.add_statement (cstmt); - - if (m.get_postconditions ().size > 0) { - foreach (Expression postcondition in m.get_postconditions ()) { - vblock.add_statement (create_postcondition_statement (postcondition)); - } - - if (!(creturn_type is VoidType)) { - var cret_stmt = new CCodeReturnStatement (new CCodeIdentifier ("result")); - cret_stmt.line = vfunc.line; - vblock.add_statement (cret_stmt); - } - } - - if (visible) { - header_type_member_declaration.append (vfunc.copy ()); - } else { - vfunc.modifiers |= CCodeModifiers.STATIC; - source_type_member_declaration.append (vfunc.copy ()); - } - - vfunc.block = vblock; - - if (m.is_abstract && m.source_reference != null && m.source_reference.comment != null) { - source_type_member_definition.append (new CCodeComment (m.source_reference.comment)); - } - source_type_member_definition.append (vfunc); - } - - if (m is CreationMethod) { - if (((CreationMethod) m).n_construction_params > 0) { - var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, new CCodeIdentifier ("__params_it"), new CCodeIdentifier ("__params")); - var cdofreeparam = new CCodeBlock (); - cdofreeparam.add_statement (new CCodeExpressionStatement (new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_DECREMENT, new CCodeIdentifier ("__params_it")))); - var cunsetcall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_unset")); - cunsetcall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("__params_it"), "value"))); - cdofreeparam.add_statement (new CCodeExpressionStatement (cunsetcall)); - function.block.add_statement (new CCodeWhileStatement (ccond, cdofreeparam)); - - var cfreeparams = new CCodeFunctionCall (new CCodeIdentifier ("g_free")); - cfreeparams.add_argument (new CCodeIdentifier ("__params")); - function.block.add_statement (new CCodeExpressionStatement (cfreeparams)); - } - - if (current_type_symbol is Class) { - var creturn = new CCodeReturnStatement (); - creturn.return_expression = new CCodeIdentifier ("self"); - function.block.add_statement (creturn); - } - } - - bool return_value = true; - bool args_parameter = true; - if (is_possible_entry_point (m, ref return_value, ref args_parameter)) { - // m is possible entry point, add appropriate startup code - var cmain = new CCodeFunction ("main", "int"); - cmain.line = function.line; - cmain.add_parameter (new CCodeFormalParameter ("argc", "int")); - cmain.add_parameter (new CCodeFormalParameter ("argv", "char **")); - var main_block = new CCodeBlock (); - - if (context.thread) { - var thread_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_thread_init")); - thread_init_call.line = cmain.line; - thread_init_call.add_argument (new CCodeConstant ("NULL")); - main_block.add_statement (new CCodeExpressionStatement (thread_init_call)); - } - - var type_init_call = new CCodeExpressionStatement (new CCodeFunctionCall (new CCodeIdentifier ("g_type_init"))); - type_init_call.line = cmain.line; - main_block.add_statement (type_init_call); - - var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name)); - if (args_parameter) { - main_call.add_argument (new CCodeIdentifier ("argv")); - main_call.add_argument (new CCodeIdentifier ("argc")); - } - if (return_value) { - var main_stmt = new CCodeReturnStatement (main_call); - main_stmt.line = cmain.line; - main_block.add_statement (main_stmt); - } else { - // method returns void, always use 0 as exit code - var main_stmt = new CCodeExpressionStatement (main_call); - main_stmt.line = cmain.line; - main_block.add_statement (main_stmt); - var ret_stmt = new CCodeReturnStatement (new CCodeConstant ("0")); - ret_stmt.line = cmain.line; - main_block.add_statement (ret_stmt); - } - cmain.block = main_block; - source_type_member_definition.append (cmain); - } - } - - private CCodeStatement create_method_type_check_statement (Method m, DataType return_type, Typesymbol t, bool non_null, string var_name) { - return create_type_check_statement (m, return_type, t, non_null, var_name); - } - - private CCodeStatement create_property_type_check_statement (Property prop, bool check_return_type, Typesymbol t, bool non_null, string var_name) { - if (check_return_type) { - return create_type_check_statement (prop, prop.type_reference, t, non_null, var_name); - } else { - return create_type_check_statement (prop, new VoidType (), t, non_null, var_name); - } - } - - private CCodeStatement? create_type_check_statement (CodeNode method_node, DataType ret_type, Typesymbol t, bool non_null, string var_name) { - var ccheck = new CCodeFunctionCall (); - - if ((t is Class && ((Class) t).is_subtype_of (gobject_type)) || (t is Interface && !((Interface) t).declaration_only)) { - var ctype_check = new CCodeFunctionCall (new CCodeIdentifier (t.get_upper_case_cname ("IS_"))); - ctype_check.add_argument (new CCodeIdentifier (var_name)); - - CCodeExpression cexpr = ctype_check; - if (!non_null) { - var cnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL")); - - cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cnull, ctype_check); - } - ccheck.add_argument (cexpr); - } else if (!non_null) { - return null; - } else { - var cnonnull = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier (var_name), new CCodeConstant ("NULL")); - ccheck.add_argument (cnonnull); - } - - if (ret_type is VoidType) { - /* void function */ - ccheck.call = new CCodeIdentifier ("g_return_if_fail"); - } else { - ccheck.call = new CCodeIdentifier ("g_return_val_if_fail"); - - var cdefault = default_value_for_type (ret_type, false); - if (cdefault != null) { - ccheck.add_argument (cdefault); - } else { - return new CCodeExpressionStatement (new CCodeConstant ("0")); - } - } - - return new CCodeExpressionStatement (ccheck); - } - - private CCodeStatement create_precondition_statement (CodeNode method_node, DataType ret_type, Expression precondition) { - var ccheck = new CCodeFunctionCall (); - - ccheck.add_argument ((CCodeExpression) precondition.ccodenode); - - if (ret_type is VoidType) { - /* void function */ - ccheck.call = new CCodeIdentifier ("g_return_if_fail"); - } else { - ccheck.call = new CCodeIdentifier ("g_return_val_if_fail"); - - var cdefault = default_value_for_type (ret_type, false); - if (cdefault != null) { - ccheck.add_argument (cdefault); - } else { - return new CCodeExpressionStatement (new CCodeConstant ("0")); - } - } - - return new CCodeExpressionStatement (ccheck); - } - - private CCodeStatement create_postcondition_statement (Expression postcondition) { - var cassert = new CCodeFunctionCall (new CCodeIdentifier ("g_assert")); - - cassert.add_argument ((CCodeExpression) postcondition.ccodenode); - - return new CCodeExpressionStatement (cassert); - } - - private CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) { - if ((type.data_type != null && type.data_type.is_reference_type ()) || type is PointerType || type is ArrayType) { - return new CCodeConstant ("NULL"); - } else if (type.data_type != null && type.data_type.get_default_value () != null) { - return new CCodeConstant (type.data_type.get_default_value ()); - } else if (type.data_type is Struct && initializer_expression) { - // 0-initialize struct with struct initializer { 0 } - // only allowed as initializer expression in C - var clist = new CCodeInitializerList (); - clist.append (new CCodeConstant ("0")); - return clist; - } else if (type.type_parameter != null) { - return new CCodeConstant ("NULL"); - } else if (type is ErrorType) { - return new CCodeConstant ("NULL"); - } - return null; - } - - private Typesymbol? find_parent_type (Symbol sym) { - while (sym != null) { - if (sym is Typesymbol) { - return (Typesymbol) sym; - } - sym = sym.parent_symbol; - } - return null; - } - - public override void visit_creation_method (CreationMethod m) { - if (m.body != null && current_type_symbol is Class && current_class.is_subtype_of (gobject_type)) { - int n_params = 0; - foreach (Statement stmt in m.body.get_statements ()) { - if (!(stmt is ExpressionStatement) || ((ExpressionStatement) stmt).assigned_property () == null) { - m.error = true; - Report.error (stmt.source_reference, "class creation methods only allow property assignment statements"); - return; - } - if (((ExpressionStatement) stmt).assigned_property ().set_accessor.construction) { - n_params++; - } - } - m.n_construction_params = n_params; - } - - visit_method (m); - } - - private bool is_possible_entry_point (Method m, ref bool return_value, ref bool args_parameter) { - if (m.name == null || m.name != "main") { - // method must be called "main" - return false; - } - - if (m.instance) { - // method must be static - return false; - } - - if (m.return_type.data_type == null) { - return_value = false; - } else if (m.return_type.data_type == int_type.data_type) { - return_value = true; - } else { - // return type must be void or int - return false; - } - - var params = m.get_parameters (); - if (params.size == 0) { - // method may have no parameters - args_parameter = false; - return true; - } - - if (params.size > 1) { - // method must not have more than one parameter - return false; - } - - Iterator params_it = params.iterator (); - params_it.next (); - var param = params_it.get (); - - if (param.direction == ParameterDirection.OUT) { - // parameter must not be an out parameter - return false; - } - - if (!(param.type_reference is ArrayType)) { - // parameter must be an array - return false; - } - - var array_type = (ArrayType) param.type_reference; - if (array_type.element_type.data_type != string_type.data_type) { - // parameter must be an array of strings - return false; - } - - args_parameter = true; - return true; - } - - private void add_object_creation (CCodeBlock b, bool has_params) { - var cl = (Class) current_type_symbol; - - var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_newv")); - ccall.add_argument (new CCodeConstant (cl.get_type_id ())); - if (has_params) { - ccall.add_argument (new CCodeConstant ("__params_it - __params")); - ccall.add_argument (new CCodeConstant ("__params")); - } else { - ccall.add_argument (new CCodeConstant ("0")); - ccall.add_argument (new CCodeConstant ("NULL")); - } - - var cdecl = new CCodeVariableDeclarator ("self"); - cdecl.initializer = ccall; - - var cdeclaration = new CCodeDeclaration ("%s *".printf (cl.get_cname ())); - cdeclaration.add_declarator (cdecl); - - b.add_statement (cdeclaration); - } - - private Class find_fundamental_class (Class cl) { - var fundamental_class = cl; - while (fundamental_class != null && fundamental_class.base_class != gtypeinstance_type) { - fundamental_class = fundamental_class.base_class; - } - return fundamental_class; - } -} - diff --git a/gobject/valaccodemethodbinding.vala b/gobject/valaccodemethodbinding.vala index 5866a1d..c710dd2 100644 --- a/gobject/valaccodemethodbinding.vala +++ b/gobject/valaccodemethodbinding.vala @@ -39,5 +39,746 @@ public class Vala.CCodeMethodBinding : CCodeBinding { } public override void emit () { + var m = method; + + Method old_method = codegen.current_method; + DataType old_return_type = codegen.current_return_type; + bool old_method_inner_error = codegen.current_method_inner_error; + int old_next_temp_var_id = codegen.next_temp_var_id; + codegen.current_symbol = m; + codegen.current_method = m; + codegen.current_return_type = m.return_type; + codegen.current_method_inner_error = false; + codegen.next_temp_var_id = 0; + + bool in_gtypeinstance_creation_method = false; + bool in_gobject_creation_method = false; + bool in_fundamental_creation_method = false; + + var creturn_type = codegen.current_return_type; + + if (m is CreationMethod) { + codegen.in_creation_method = true; + var cl = codegen.current_type_symbol as Class; + if (cl != null && cl.is_subtype_of (codegen.gtypeinstance_type)) { + in_gtypeinstance_creation_method = true; + if (cl.base_class == codegen.gtypeinstance_type) { + in_fundamental_creation_method = true; + } else if (cl.is_subtype_of (codegen.gobject_type)) { + in_gobject_creation_method = true; + } + } + + if (cl != null) { + creturn_type = new ClassType (cl); + } + } + + m.accept_children (codegen); + + if (m is CreationMethod) { + if (in_gobject_creation_method && m.body != null) { + var cblock = new CCodeBlock (); + + foreach (CodeNode stmt in m.body.get_statements ()) { + if (((ExpressionStatement) stmt).assigned_property ().set_accessor.construction) { + if (stmt.ccodenode is CCodeFragment) { + foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) { + cblock.add_statement (cstmt); + } + } else { + cblock.add_statement (stmt.ccodenode); + } + } + } + + add_object_creation (cblock, ((CreationMethod) m).n_construction_params > 0); + + foreach (CodeNode stmt in m.body.get_statements ()) { + if (!((ExpressionStatement) stmt).assigned_property ().set_accessor.construction) { + if (stmt.ccodenode is CCodeFragment) { + foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) { + cblock.add_statement (cstmt); + } + } else { + cblock.add_statement (stmt.ccodenode); + } + } + } + + m.body.ccodenode = cblock; + } + + codegen.in_creation_method = false; + } + + bool inner_error = codegen.current_method_inner_error; + + codegen.current_symbol = codegen.current_symbol.parent_symbol; + codegen.current_method = old_method; + codegen.current_return_type = old_return_type; + codegen.current_method_inner_error = old_method_inner_error; + codegen.next_temp_var_id = old_next_temp_var_id; + + if (codegen.current_type_symbol is Interface) { + var iface = (Interface) codegen.current_type_symbol; + if (iface.is_static) { + return; + } + } + + codegen.function = new CCodeFunction (m.get_real_cname (), creturn_type.get_cname ()); + m.ccodenode = codegen.function; + + if (m.is_inline) { + codegen.function.modifiers |= CCodeModifiers.INLINE; + } + + var cparam_map = new HashMap (direct_hash, direct_equal); + + CCodeFunctionDeclarator vdeclarator = null; + + if (m.instance || (m.parent_symbol is Struct && m is CreationMethod)) { + Typesymbol parent_type = find_parent_type (m); + DataType this_type; + if (parent_type is Class) { + this_type = new ClassType ((Class) parent_type); + } else if (parent_type is Interface) { + this_type = new InterfaceType ((Interface) parent_type); + } else { + this_type = new ValueType (parent_type); + } + + CCodeFormalParameter instance_param = null; + if (m.base_interface_method != null && !m.is_abstract && !m.is_virtual) { + var base_type = new InterfaceType ((Interface) m.base_interface_method.parent_symbol); + instance_param = new CCodeFormalParameter ("base", base_type.get_cname ()); + } else if (m.overrides) { + var base_type = new ClassType ((Class) m.base_method.parent_symbol); + instance_param = new CCodeFormalParameter ("base", base_type.get_cname ()); + } else { + if (m.parent_symbol is Struct && !((Struct) m.parent_symbol).is_simple_type ()) { + instance_param = new CCodeFormalParameter ("*self", this_type.get_cname ()); + } else { + instance_param = new CCodeFormalParameter ("self", this_type.get_cname ()); + } + } + cparam_map.set (codegen.get_param_pos (m.cinstance_parameter_position), instance_param); + + if (m.is_abstract || m.is_virtual) { + var vdecl = new CCodeDeclaration (creturn_type.get_cname ()); + vdeclarator = new CCodeFunctionDeclarator (m.vfunc_name); + vdecl.add_declarator (vdeclarator); + codegen.type_struct.add_declaration (vdecl); + } + } + + if (in_fundamental_creation_method) { + cparam_map.set (codegen.get_param_pos (0.1), new CCodeFormalParameter ("type", "GType")); + } + + if (in_gobject_creation_method) { + // memory management for generic types + int type_param_index = 0; + foreach (TypeParameter type_param in codegen.current_class.get_type_parameters ()) { + cparam_map.set (codegen.get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType")); + cparam_map.set (codegen.get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc")); + cparam_map.set (codegen.get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify")); + type_param_index++; + } + } + + var params = m.get_parameters (); + foreach (FormalParameter param in params) { + if (!param.no_array_length && param.type_reference is ArrayType) { + var array_type = (ArrayType) param.type_reference; + + var length_ctype = "int"; + if (param.direction != ParameterDirection.IN) { + length_ctype = "int*"; + } + + for (int dim = 1; dim <= array_type.rank; dim++) { + var cparam = new CCodeFormalParameter (codegen.get_array_length_cname (param.name, dim), length_ctype); + cparam_map.set (codegen.get_param_pos (param.carray_length_parameter_position + 0.01 * dim), cparam); + } + } + + cparam_map.set (codegen.get_param_pos (param.cparameter_position), (CCodeFormalParameter) param.ccodenode); + + if (param.type_reference is DelegateType) { + var deleg_type = (DelegateType) param.type_reference; + var d = deleg_type.delegate_symbol; + if (d.instance) { + var cparam = new CCodeFormalParameter (codegen.get_delegate_target_cname (param.name), "void*"); + cparam_map.set (codegen.get_param_pos (param.cdelegate_target_parameter_position), cparam); + } + } + } + + if (!m.no_array_length && creturn_type is ArrayType) { + // return array length if appropriate + var array_type = (ArrayType) creturn_type; + + for (int dim = 1; dim <= array_type.rank; dim++) { + var cparam = new CCodeFormalParameter (codegen.get_array_length_cname ("result", dim), "int*"); + cparam_map.set (codegen.get_param_pos (m.carray_length_parameter_position + 0.01 * dim), cparam); + } + } else if (creturn_type is DelegateType) { + // return delegate target if appropriate + var deleg_type = (DelegateType) creturn_type; + var d = deleg_type.delegate_symbol; + if (d.instance) { + var cparam = new CCodeFormalParameter (codegen.get_delegate_target_cname ("result"), "void*"); + cparam_map.set (codegen.get_param_pos (m.cdelegate_target_parameter_position), cparam); + } + } + + if (m.get_error_domains ().size > 0) { + var cparam = new CCodeFormalParameter ("error", "GError**"); + cparam_map.set (codegen.get_param_pos (-1), cparam); + } + + // append C parameters in the right order + int last_pos = -1; + int min_pos; + while (true) { + min_pos = -1; + foreach (int pos in cparam_map.get_keys ()) { + if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { + min_pos = pos; + } + } + if (min_pos == -1) { + break; + } + codegen.function.add_parameter (cparam_map.get (min_pos)); + if (vdeclarator != null) { + vdeclarator.add_parameter (cparam_map.get (min_pos)); + } + last_pos = min_pos; + } + + bool visible = !m.is_internal_symbol (); + + /* real function declaration and definition not needed + * for abstract methods */ + if (!m.is_abstract) { + if (visible && m.base_method == null && m.base_interface_method == null) { + /* public methods need function declaration in + * header file except virtual/overridden methods */ + codegen.header_type_member_declaration.append (codegen.function.copy ()); + } else { + /* declare all other functions in source file to + * avoid dependency on order within source file */ + codegen.function.modifiers |= CCodeModifiers.STATIC; + codegen.source_type_member_declaration.append (codegen.function.copy ()); + } + + /* Methods imported from a plain C file don't + * have a body, e.g. Vala.Parser.parse_file () */ + if (m.body != null) { + codegen.function.block = (CCodeBlock) m.body.ccodenode; + codegen.function.block.line = codegen.function.line; + + var cinit = new CCodeFragment (); + codegen.function.block.prepend_statement (cinit); + + if (m.parent_symbol is Class) { + var cl = (Class) m.parent_symbol; + if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) { + Method base_method; + ReferenceType base_expression_type; + if (m.overrides) { + base_method = m.base_method; + base_expression_type = new ClassType ((Class) base_method.parent_symbol); + } else { + base_method = m.base_interface_method; + base_expression_type = new InterfaceType ((Interface) base_method.parent_symbol); + } + var self_target_type = new ClassType (cl); + CCodeExpression cself = codegen.get_implicit_cast_expression (new CCodeIdentifier ("base"), base_expression_type, self_target_type); + + var cdecl = new CCodeDeclaration ("%s *".printf (cl.get_cname ())); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", cself)); + + cinit.append (cdecl); + } else if (m.instance) { + var ccheckstmt = create_method_type_check_statement (m, creturn_type, cl, true, "self"); + ccheckstmt.line = codegen.function.line; + cinit.append (ccheckstmt); + } + } + foreach (FormalParameter param in m.get_parameters ()) { + if (param.ellipsis) { + break; + } + + var t = param.type_reference.data_type; + if (t != null && t.is_reference_type ()) { + if (param.direction != ParameterDirection.OUT) { + var type_check = create_method_type_check_statement (m, creturn_type, t, (codegen.context.non_null && !param.type_reference.nullable), param.name); + if (type_check != null) { + type_check.line = codegen.function.line; + cinit.append (type_check); + } + } else { + // ensure that the passed reference for output parameter is cleared + var a = new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier (param.name)), new CCodeConstant ("NULL")); + cinit.append (new CCodeExpressionStatement (a)); + } + } + } + + if (inner_error) { + /* always separate error parameter and inner_error local variable + * as error may be set to NULL but we're always interested in inner errors + */ + var cdecl = new CCodeDeclaration ("GError *"); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("inner_error", new CCodeConstant ("NULL"))); + cinit.append (cdecl); + } + + if (m.source_reference != null && m.source_reference.comment != null) { + codegen.source_type_member_definition.append (new CCodeComment (m.source_reference.comment)); + } + codegen.source_type_member_definition.append (codegen.function); + + if (m is CreationMethod) { + if (in_gobject_creation_method) { + int n_params = ((CreationMethod) m).n_construction_params; + + if (n_params > 0) { + // declare construction parameter array + var cparamsinit = new CCodeFunctionCall (new CCodeIdentifier ("g_new0")); + cparamsinit.add_argument (new CCodeIdentifier ("GParameter")); + cparamsinit.add_argument (new CCodeConstant (n_params.to_string ())); + + var cdecl = new CCodeDeclaration ("GParameter *"); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("__params", cparamsinit)); + cinit.append (cdecl); + + cdecl = new CCodeDeclaration ("GParameter *"); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("__params_it", new CCodeIdentifier ("__params"))); + cinit.append (cdecl); + } + + /* type, dup func, and destroy func properties for generic types */ + foreach (TypeParameter type_param in codegen.current_class.get_type_parameters ()) { + string func_name; + CCodeMemberAccess cmember; + CCodeAssignment cassign; + + func_name = "%s_type".printf (type_param.name.down ()); + cmember = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); + cassign = new CCodeAssignment (cmember, new CCodeIdentifier (func_name)); + codegen.function.block.add_statement (new CCodeExpressionStatement (cassign)); + + func_name = "%s_dup_func".printf (type_param.name.down ()); + cmember = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); + cassign = new CCodeAssignment (cmember, new CCodeIdentifier (func_name)); + codegen.function.block.add_statement (new CCodeExpressionStatement (cassign)); + + func_name = "%s_destroy_func".printf (type_param.name.down ()); + cmember = new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name); + cassign = new CCodeAssignment (cmember, new CCodeIdentifier (func_name)); + codegen.function.block.add_statement (new CCodeExpressionStatement (cassign)); + } + } else if (in_fundamental_creation_method) { + var cl = (Class) m.parent_symbol; + var cdecl = new CCodeDeclaration (cl.get_cname () + "*"); + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_create_instance")); + ccall.add_argument (new CCodeIdentifier ("type")); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", new CCodeCastExpression (ccall, cl.get_cname () + "*"))); + cinit.append (cdecl); + } else if (in_gtypeinstance_creation_method) { + var cl = (Class) m.parent_symbol; + var cdecl = new CCodeDeclaration (cl.get_cname () + "*"); + var fundamental_class = find_fundamental_class (cl); + var ccall = new CCodeFunctionCall (new CCodeIdentifier (fundamental_class.default_construction_method.get_cname ())); + ccall.add_argument (new CCodeIdentifier (cl.get_type_id ())); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", ccall)); + cinit.append (cdecl); + } else if (codegen.current_type_symbol is Class) { + var cl = (Class) m.parent_symbol; + var cdecl = new CCodeDeclaration (cl.get_cname () + "*"); + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0")); + ccall.add_argument (new CCodeIdentifier (cl.get_cname ())); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("self", ccall)); + cinit.append (cdecl); + } else { + var st = (Struct) m.parent_symbol; + + // memset needs string.h + codegen.string_h_needed = true; + var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset")); + czero.add_argument (new CCodeIdentifier ("self")); + czero.add_argument (new CCodeConstant ("0")); + czero.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (st.get_cname ()))); + cinit.append (new CCodeExpressionStatement (czero)); + } + } + + if (codegen.context.module_init_method == m && codegen.in_plugin) { + // GTypeModule-based plug-in, register types + cinit.append (codegen.module_init_fragment); + } + + foreach (Expression precondition in m.get_preconditions ()) { + cinit.append (create_precondition_statement (m, creturn_type, precondition)); + } + } + } + + if (m.is_abstract || m.is_virtual) { + var vfunc = new CCodeFunction (m.get_cname (), creturn_type.get_cname ()); + vfunc.line = codegen.function.line; + + ReferenceType this_type; + if (m.parent_symbol is Class) { + this_type = new ClassType ((Class) m.parent_symbol); + } else { + this_type = new InterfaceType ((Interface) m.parent_symbol); + } + + cparam_map = new HashMap (direct_hash, direct_equal); + var carg_map = new HashMap (direct_hash, direct_equal); + + var cparam = new CCodeFormalParameter ("self", this_type.get_cname ()); + cparam_map.set (codegen.get_param_pos (m.cinstance_parameter_position), cparam); + + var vblock = new CCodeBlock (); + + foreach (Expression precondition in m.get_preconditions ()) { + vblock.add_statement (create_precondition_statement (m, creturn_type, precondition)); + } + + CCodeFunctionCall vcast = null; + if (m.parent_symbol is Interface) { + var iface = (Interface) m.parent_symbol; + + vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null)))); + } else { + var cl = (Class) m.parent_symbol; + + vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null)))); + } + vcast.add_argument (new CCodeIdentifier ("self")); + + var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, m.vfunc_name)); + carg_map.set (codegen.get_param_pos (m.cinstance_parameter_position), new CCodeIdentifier ("self")); + + var params = m.get_parameters (); + foreach (FormalParameter param in params) { + if (!param.no_array_length && param.type_reference is ArrayType) { + var array_type = (ArrayType) param.type_reference; + + var length_ctype = "int"; + if (param.direction != ParameterDirection.IN) { + length_ctype = "int*"; + } + + for (int dim = 1; dim <= array_type.rank; dim++) { + var cparam = new CCodeFormalParameter (codegen.get_array_length_cname (param.name, dim), length_ctype); + cparam_map.set (codegen.get_param_pos (param.carray_length_parameter_position + 0.01 * dim), cparam); + carg_map.set (codegen.get_param_pos (param.carray_length_parameter_position + 0.01 * dim), new CCodeIdentifier (cparam.name)); + } + } + + cparam_map.set (codegen.get_param_pos (param.cparameter_position), (CCodeFormalParameter) param.ccodenode); + carg_map.set (codegen.get_param_pos (param.cparameter_position), new CCodeIdentifier (param.name)); + + if (param.type_reference is DelegateType) { + var deleg_type = (DelegateType) param.type_reference; + var d = deleg_type.delegate_symbol; + if (d.instance) { + var cparam = new CCodeFormalParameter (codegen.get_delegate_target_cname (param.name), "void*"); + cparam_map.set (codegen.get_param_pos (param.cdelegate_target_parameter_position), cparam); + carg_map.set (codegen.get_param_pos (param.cdelegate_target_parameter_position), new CCodeIdentifier (cparam.name)); + } + } + } + + // return array length if appropriate + if (!m.no_array_length && creturn_type is ArrayType) { + var array_type = (ArrayType) creturn_type; + + for (int dim = 1; dim <= array_type.rank; dim++) { + var cparam = new CCodeFormalParameter (codegen.get_array_length_cname ("result", dim), "int*"); + cparam_map.set (codegen.get_param_pos (m.carray_length_parameter_position), cparam); + carg_map.set (codegen.get_param_pos (m.carray_length_parameter_position), new CCodeIdentifier (cparam.name)); + } + } else if (creturn_type is DelegateType) { + // return delegate target if appropriate + var deleg_type = (DelegateType) creturn_type; + var d = deleg_type.delegate_symbol; + if (d.instance) { + var cparam = new CCodeFormalParameter (codegen.get_delegate_target_cname ("result"), "void*"); + cparam_map.set (codegen.get_param_pos (m.cdelegate_target_parameter_position), cparam); + carg_map.set (codegen.get_param_pos (m.cdelegate_target_parameter_position), new CCodeIdentifier (cparam.name)); + } + } + + if (m.get_error_domains ().size > 0) { + var cparam = new CCodeFormalParameter ("error", "GError**"); + cparam_map.set (codegen.get_param_pos (-1), cparam); + carg_map.set (codegen.get_param_pos (-1), new CCodeIdentifier (cparam.name)); + } + + + // append C parameters and arguments in the right order + int last_pos = -1; + int min_pos; + while (true) { + min_pos = -1; + foreach (int pos in cparam_map.get_keys ()) { + if (pos > last_pos && (min_pos == -1 || pos < min_pos)) { + min_pos = pos; + } + } + if (min_pos == -1) { + break; + } + vfunc.add_parameter (cparam_map.get (min_pos)); + vcall.add_argument (carg_map.get (min_pos)); + last_pos = min_pos; + } + + CCodeStatement cstmt; + if (creturn_type is VoidType) { + cstmt = new CCodeExpressionStatement (vcall); + } else if (m.get_postconditions ().size == 0) { + /* pass method return value */ + cstmt = new CCodeReturnStatement (vcall); + } else { + /* store method return value for postconditions */ + var cdecl = new CCodeDeclaration (creturn_type.get_cname ()); + cdecl.add_declarator (new CCodeVariableDeclarator.with_initializer ("result", vcall)); + cstmt = cdecl; + } + cstmt.line = vfunc.line; + vblock.add_statement (cstmt); + + if (m.get_postconditions ().size > 0) { + foreach (Expression postcondition in m.get_postconditions ()) { + vblock.add_statement (create_postcondition_statement (postcondition)); + } + + if (!(creturn_type is VoidType)) { + var cret_stmt = new CCodeReturnStatement (new CCodeIdentifier ("result")); + cret_stmt.line = vfunc.line; + vblock.add_statement (cret_stmt); + } + } + + if (visible) { + codegen.header_type_member_declaration.append (vfunc.copy ()); + } else { + vfunc.modifiers |= CCodeModifiers.STATIC; + codegen.source_type_member_declaration.append (vfunc.copy ()); + } + + vfunc.block = vblock; + + if (m.is_abstract && m.source_reference != null && m.source_reference.comment != null) { + codegen.source_type_member_definition.append (new CCodeComment (m.source_reference.comment)); + } + codegen.source_type_member_definition.append (vfunc); + } + + if (m is CreationMethod) { + if (((CreationMethod) m).n_construction_params > 0) { + var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, new CCodeIdentifier ("__params_it"), new CCodeIdentifier ("__params")); + var cdofreeparam = new CCodeBlock (); + cdofreeparam.add_statement (new CCodeExpressionStatement (new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_DECREMENT, new CCodeIdentifier ("__params_it")))); + var cunsetcall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_unset")); + cunsetcall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("__params_it"), "value"))); + cdofreeparam.add_statement (new CCodeExpressionStatement (cunsetcall)); + codegen.function.block.add_statement (new CCodeWhileStatement (ccond, cdofreeparam)); + + var cfreeparams = new CCodeFunctionCall (new CCodeIdentifier ("g_free")); + cfreeparams.add_argument (new CCodeIdentifier ("__params")); + codegen.function.block.add_statement (new CCodeExpressionStatement (cfreeparams)); + } + + if (codegen.current_type_symbol is Class) { + var creturn = new CCodeReturnStatement (); + creturn.return_expression = new CCodeIdentifier ("self"); + codegen.function.block.add_statement (creturn); + } + } + + bool return_value = true; + bool args_parameter = true; + if (is_possible_entry_point (m, ref return_value, ref args_parameter)) { + // m is possible entry point, add appropriate startup code + var cmain = new CCodeFunction ("main", "int"); + cmain.line = codegen.function.line; + cmain.add_parameter (new CCodeFormalParameter ("argc", "int")); + cmain.add_parameter (new CCodeFormalParameter ("argv", "char **")); + var main_block = new CCodeBlock (); + + if (codegen.context.thread) { + var thread_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_thread_init")); + thread_init_call.line = cmain.line; + thread_init_call.add_argument (new CCodeConstant ("NULL")); + main_block.add_statement (new CCodeExpressionStatement (thread_init_call)); + } + + var type_init_call = new CCodeExpressionStatement (new CCodeFunctionCall (new CCodeIdentifier ("g_type_init"))); + type_init_call.line = cmain.line; + main_block.add_statement (type_init_call); + + var main_call = new CCodeFunctionCall (new CCodeIdentifier (codegen.function.name)); + if (args_parameter) { + main_call.add_argument (new CCodeIdentifier ("argv")); + main_call.add_argument (new CCodeIdentifier ("argc")); + } + if (return_value) { + var main_stmt = new CCodeReturnStatement (main_call); + main_stmt.line = cmain.line; + main_block.add_statement (main_stmt); + } else { + // method returns void, always use 0 as exit code + var main_stmt = new CCodeExpressionStatement (main_call); + main_stmt.line = cmain.line; + main_block.add_statement (main_stmt); + var ret_stmt = new CCodeReturnStatement (new CCodeConstant ("0")); + ret_stmt.line = cmain.line; + main_block.add_statement (ret_stmt); + } + cmain.block = main_block; + codegen.source_type_member_definition.append (cmain); + } + } + + private CCodeStatement create_method_type_check_statement (Method m, DataType return_type, Typesymbol t, bool non_null, string var_name) { + return codegen.create_type_check_statement (m, return_type, t, non_null, var_name); + } + + private CCodeStatement create_precondition_statement (CodeNode method_node, DataType ret_type, Expression precondition) { + var ccheck = new CCodeFunctionCall (); + + ccheck.add_argument ((CCodeExpression) precondition.ccodenode); + + if (ret_type is VoidType) { + /* void function */ + ccheck.call = new CCodeIdentifier ("g_return_if_fail"); + } else { + ccheck.call = new CCodeIdentifier ("g_return_val_if_fail"); + + var cdefault = codegen.default_value_for_type (ret_type, false); + if (cdefault != null) { + ccheck.add_argument (cdefault); + } else { + return new CCodeExpressionStatement (new CCodeConstant ("0")); + } + } + + return new CCodeExpressionStatement (ccheck); + } + + private CCodeStatement create_postcondition_statement (Expression postcondition) { + var cassert = new CCodeFunctionCall (new CCodeIdentifier ("g_assert")); + + cassert.add_argument ((CCodeExpression) postcondition.ccodenode); + + return new CCodeExpressionStatement (cassert); + } + + private Typesymbol? find_parent_type (Symbol sym) { + while (sym != null) { + if (sym is Typesymbol) { + return (Typesymbol) sym; + } + sym = sym.parent_symbol; + } + return null; + } + + private bool is_possible_entry_point (Method m, ref bool return_value, ref bool args_parameter) { + if (m.name == null || m.name != "main") { + // method must be called "main" + return false; + } + + if (m.instance) { + // method must be static + return false; + } + + if (m.return_type.data_type == null) { + return_value = false; + } else if (m.return_type.data_type == codegen.int_type.data_type) { + return_value = true; + } else { + // return type must be void or int + return false; + } + + var params = m.get_parameters (); + if (params.size == 0) { + // method may have no parameters + args_parameter = false; + return true; + } + + if (params.size > 1) { + // method must not have more than one parameter + return false; + } + + Iterator params_it = params.iterator (); + params_it.next (); + var param = params_it.get (); + + if (param.direction == ParameterDirection.OUT) { + // parameter must not be an out parameter + return false; + } + + if (!(param.type_reference is ArrayType)) { + // parameter must be an array + return false; + } + + var array_type = (ArrayType) param.type_reference; + if (array_type.element_type.data_type != codegen.string_type.data_type) { + // parameter must be an array of strings + return false; + } + + args_parameter = true; + return true; + } + + private void add_object_creation (CCodeBlock b, bool has_params) { + var cl = (Class) codegen.current_type_symbol; + + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_newv")); + ccall.add_argument (new CCodeConstant (cl.get_type_id ())); + if (has_params) { + ccall.add_argument (new CCodeConstant ("__params_it - __params")); + ccall.add_argument (new CCodeConstant ("__params")); + } else { + ccall.add_argument (new CCodeConstant ("0")); + ccall.add_argument (new CCodeConstant ("NULL")); + } + + var cdecl = new CCodeVariableDeclarator ("self"); + cdecl.initializer = ccall; + + var cdeclaration = new CCodeDeclaration ("%s *".printf (cl.get_cname ())); + cdeclaration.add_declarator (cdecl); + + b.add_statement (cdeclaration); + } + + private Class find_fundamental_class (Class cl) { + var fundamental_class = cl; + while (fundamental_class != null && fundamental_class.base_class != codegen.gtypeinstance_type) { + fundamental_class = fundamental_class.base_class; + } + return fundamental_class; } } diff --git a/vala/valacreationmethod.vala b/vala/valacreationmethod.vala index 0afd4a4..390e7f7 100644 --- a/vala/valacreationmethod.vala +++ b/vala/valacreationmethod.vala @@ -83,4 +83,8 @@ public class Vala.CreationMethod : Method { return "%s%s_%s".printf (parent.get_lower_case_cprefix (), infix, name.offset (".new.".len ())); } } + + public override CodeBinding? create_code_binding (CodeGenerator codegen) { + return codegen.create_creation_method_binding (this); + } }