diff options
-rw-r--r-- | ccode/valaccodefunction.vala | 6 | ||||
-rw-r--r-- | codegen/Makefile.am | 11 | ||||
-rw-r--r-- | codegen/valaccodegenerator.vala | 14 | ||||
-rw-r--r-- | codegen/valadovaarraymodule.vala | 130 | ||||
-rw-r--r-- | codegen/valadovaassignmentmodule.vala | 198 | ||||
-rw-r--r-- | codegen/valadovabasemodule.vala | 2142 | ||||
-rw-r--r-- | codegen/valadovacontrolflowmodule.vala | 98 | ||||
-rw-r--r-- | codegen/valadovadelegatemodule.vala | 210 | ||||
-rw-r--r-- | codegen/valadovamemberaccessmodule.vala | 255 | ||||
-rw-r--r-- | codegen/valadovamethodcallmodule.vala | 230 | ||||
-rw-r--r-- | codegen/valadovamethodmodule.vala | 46 | ||||
-rw-r--r-- | codegen/valadovaobjectmodule.vala | 1519 | ||||
-rw-r--r-- | codegen/valadovastructmodule.vala | 101 | ||||
-rw-r--r-- | codegen/valadovavaluemodule.vala | 863 |
14 files changed, 5822 insertions, 1 deletions
diff --git a/ccode/valaccodefunction.vala b/ccode/valaccodefunction.vala index 2b3775ddf..faac0ec94 100644 --- a/ccode/valaccodefunction.vala +++ b/ccode/valaccodefunction.vala @@ -63,7 +63,11 @@ public class Vala.CCodeFunction : CCodeNode { public void add_parameter (CCodeFormalParameter param) { parameters.add (param); } - + + public void insert_parameter (int position, CCodeFormalParameter param) { + parameters.insert (position, param); + } + /** * Returns a copy of this function. * diff --git a/codegen/Makefile.am b/codegen/Makefile.am index 511b07109..c62b6f6eb 100644 --- a/codegen/Makefile.am +++ b/codegen/Makefile.am @@ -34,6 +34,17 @@ libvala_la_VALASOURCES = \ valadbusinterfaceregisterfunction.vala \ valadbusmodule.vala \ valadbusservermodule.vala \ + valadovaarraymodule.vala \ + valadovaassignmentmodule.vala \ + valadovabasemodule.vala \ + valadovacontrolflowmodule.vala \ + valadovadelegatemodule.vala \ + valadovamemberaccessmodule.vala \ + valadovamethodcallmodule.vala \ + valadovamethodmodule.vala \ + valadovaobjectmodule.vala \ + valadovastructmodule.vala \ + valadovavaluemodule.vala \ valaenumregisterfunction.vala \ valagerrormodule.vala \ valagirwriter.vala \ diff --git a/codegen/valaccodegenerator.vala b/codegen/valaccodegenerator.vala index 4853e7069..e2155cf74 100644 --- a/codegen/valaccodegenerator.vala +++ b/codegen/valaccodegenerator.vala @@ -53,6 +53,20 @@ public class Vala.CCodeGenerator : CodeGenerator { head = new DBusClientModule (this, head); */ head = new DBusServerModule (this, head); + } else if (context.profile == Profile.DOVA) { + /* included by inheritance + head = new DovaBaseModule (this, head); + head = new DovaStructModule (this, head); + head = new DovaMethodModule (this, head); + head = new DovaControlFlowModule (this, head); + head = new DovaMemberAccessModule (this, head); + head = new DovaAssignmentModule (this, head); + head = new DovaMethodCallModule (this, head); + head = new DovaArrayModule (this, head); + head = new DovaObjectModule (this, head); + head = new DovaValueModule (this, head); + */ + head = new DovaDelegateModule (this, head); } else { /* included by inheritance head = new CCodeBaseModule (this, head); diff --git a/codegen/valadovaarraymodule.vala b/codegen/valadovaarraymodule.vala new file mode 100644 index 000000000..83ddd4593 --- /dev/null +++ b/codegen/valadovaarraymodule.vala @@ -0,0 +1,130 @@ +/* valadovaarraymodule.vala + * + * Copyright (C) 2006-2009 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 <j@bitron.ch> + */ + +internal class Vala.DovaArrayModule : DovaMethodCallModule { + public DovaArrayModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + void append_initializer_list (CCodeCommaExpression ce, CCodeExpression name_cnode, InitializerList initializer_list, int rank, ref int i) { + foreach (Expression e in initializer_list.get_initializers ()) { + if (rank > 1) { + append_initializer_list (ce, name_cnode, (InitializerList) e, rank - 1, ref i); + } else { + ce.append_expression (new CCodeAssignment (new CCodeElementAccess (name_cnode, new CCodeConstant (i.to_string ())), (CCodeExpression) e.ccodenode)); + i++; + } + } + } + + public override void visit_array_creation_expression (ArrayCreationExpression expr) { + expr.accept_children (codegen); + + var array_type = expr.target_type as ArrayType; + if (array_type != null && array_type.fixed_length) { + // no heap allocation for fixed-length arrays + + var ce = new CCodeCommaExpression (); + var temp_var = get_temp_variable (array_type, true, expr); + var name_cnode = new CCodeIdentifier (temp_var.name); + int i = 0; + + temp_vars.insert (0, temp_var); + + append_initializer_list (ce, name_cnode, expr.initializer_list, expr.rank, ref i); + + ce.append_expression (name_cnode); + + expr.ccodenode = ce; + + return; + } + + generate_method_declaration (array_class.default_construction_method, source_declarations); + + var gnew = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_new")); + gnew.add_argument (get_type_id_expression (expr.element_type)); + + bool first = true; + CCodeExpression cexpr = null; + + // iterate over each dimension + foreach (Expression size in expr.get_sizes ()) { + CCodeExpression csize = (CCodeExpression) size.ccodenode; + + if (!is_pure_ccode_expression (csize)) { + var temp_var = get_temp_variable (int_type, false, expr); + var name_cnode = new CCodeIdentifier (temp_var.name); + size.ccodenode = name_cnode; + + temp_vars.insert (0, temp_var); + + csize = new CCodeAssignment (name_cnode, csize); + } + + if (first) { + cexpr = csize; + first = false; + } else { + cexpr = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, cexpr, csize); + } + } + + gnew.add_argument (cexpr); + + if (expr.initializer_list != null) { + var ce = new CCodeCommaExpression (); + var temp_var = get_temp_variable (expr.value_type, true, expr); + var name_cnode = new CCodeIdentifier (temp_var.name); + int i = 0; + + temp_vars.insert (0, temp_var); + + ce.append_expression (new CCodeAssignment (name_cnode, gnew)); + + append_initializer_list (ce, name_cnode, expr.initializer_list, expr.rank, ref i); + + ce.append_expression (name_cnode); + + expr.ccodenode = ce; + } else { + expr.ccodenode = gnew; + } + } + + public override void visit_element_access (ElementAccess expr) { + expr.accept_children (codegen); + + List<Expression> indices = expr.get_indices (); + int rank = indices.size; + + var ccontainer = (CCodeExpression) expr.container.ccodenode; + var cindex = (CCodeExpression) indices[0].ccodenode; + + // access to element in an array + for (int i = 1; i < rank; i++) { + var cmul = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, cindex, head.get_array_length_cexpression (expr.container, i + 1)); + cindex = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, cmul, (CCodeExpression) indices[i].ccodenode); + } + expr.ccodenode = new CCodeElementAccess (ccontainer, cindex); + } +} diff --git a/codegen/valadovaassignmentmodule.vala b/codegen/valadovaassignmentmodule.vala new file mode 100644 index 000000000..1c400b7f1 --- /dev/null +++ b/codegen/valadovaassignmentmodule.vala @@ -0,0 +1,198 @@ +/* valadovaassignmentmodule.vala + * + * Copyright (C) 2006-2009 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 <j@bitron.ch> + */ + +/** + * The link between an assignment and generated code. + */ +internal class Vala.DovaAssignmentModule : DovaMemberAccessModule { + public DovaAssignmentModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + CCodeExpression emit_property_assignment (Assignment assignment) { + var ma = assignment.left as MemberAccess; + + var prop = (Property) assignment.left.symbol_reference; + + if (!(prop is DynamicProperty)) { + generate_property_accessor_declaration (prop.set_accessor, source_declarations); + + if (!prop.external && prop.external_package) { + // internal VAPI properties + // only add them once per source file + if (add_generated_external_symbol (prop)) { + visit_property (prop); + } + } + } + + CCodeExpression cexpr = (CCodeExpression) assignment.right.ccodenode; + + if (assignment.operator != AssignmentOperator.SIMPLE) { + CCodeBinaryOperator cop; + if (assignment.operator == AssignmentOperator.BITWISE_OR) { + cop = CCodeBinaryOperator.BITWISE_OR; + } else if (assignment.operator == AssignmentOperator.BITWISE_AND) { + cop = CCodeBinaryOperator.BITWISE_AND; + } else if (assignment.operator == AssignmentOperator.BITWISE_XOR) { + cop = CCodeBinaryOperator.BITWISE_XOR; + } else if (assignment.operator == AssignmentOperator.ADD) { + cop = CCodeBinaryOperator.PLUS; + } else if (assignment.operator == AssignmentOperator.SUB) { + cop = CCodeBinaryOperator.MINUS; + } else if (assignment.operator == AssignmentOperator.MUL) { + cop = CCodeBinaryOperator.MUL; + } else if (assignment.operator == AssignmentOperator.DIV) { + cop = CCodeBinaryOperator.DIV; + } else if (assignment.operator == AssignmentOperator.PERCENT) { + cop = CCodeBinaryOperator.MOD; + } else if (assignment.operator == AssignmentOperator.SHIFT_LEFT) { + cop = CCodeBinaryOperator.SHIFT_LEFT; + } else if (assignment.operator == AssignmentOperator.SHIFT_RIGHT) { + cop = CCodeBinaryOperator.SHIFT_RIGHT; + } else { + assert_not_reached (); + } + cexpr = new CCodeBinaryExpression (cop, (CCodeExpression) get_ccodenode (assignment.left), cexpr); + } + + var ccall = get_property_set_call (prop, ma, cexpr, assignment.right); + + // assignments are expressions, so return the current property value, except if we're sure that it can't be used + if (!(assignment.parent_node is ExpressionStatement)) { + var ccomma = new CCodeCommaExpression (); + ccomma.append_expression (ccall); // update property + ccomma.append_expression ((CCodeExpression) get_ccodenode (ma)); // current property value + + return ccomma; + } else { + return ccall; + } + } + + CCodeExpression emit_simple_assignment (Assignment assignment) { + CCodeExpression rhs = (CCodeExpression) assignment.right.ccodenode; + CCodeExpression lhs = (CCodeExpression) get_ccodenode (assignment.left); + CCodeCommaExpression outer_ccomma = null; + + bool unref_old = requires_destroy (assignment.left.value_type); + + if (unref_old) { + var ccomma = new CCodeCommaExpression (); + + if (!is_pure_ccode_expression (lhs)) { + /* Assign lhs to temp var to avoid repeating side effect */ + outer_ccomma = new CCodeCommaExpression (); + + var lhs_value_type = assignment.left.value_type.copy (); + string lhs_temp_name = "_tmp%d_".printf (next_temp_var_id++); + var lhs_temp = new LocalVariable (lhs_value_type, "*" + lhs_temp_name); + temp_vars.insert (0, lhs_temp); + outer_ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (lhs_temp_name), new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, lhs))); + lhs = new CCodeParenthesizedExpression (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (lhs_temp_name))); + } + + var temp_decl = get_temp_variable (assignment.left.value_type); + temp_vars.insert (0, temp_decl); + ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_decl.name), rhs)); + if (unref_old) { + /* unref old value */ + ccomma.append_expression (get_unref_expression (lhs, assignment.left.value_type, assignment.left)); + } + + ccomma.append_expression (get_variable_cexpression (temp_decl.name)); + + rhs = ccomma; + } + + var cop = CCodeAssignmentOperator.SIMPLE; + if (assignment.operator == AssignmentOperator.BITWISE_OR) { + cop = CCodeAssignmentOperator.BITWISE_OR; + } else if (assignment.operator == AssignmentOperator.BITWISE_AND) { + cop = CCodeAssignmentOperator.BITWISE_AND; + } else if (assignment.operator == AssignmentOperator.BITWISE_XOR) { + cop = CCodeAssignmentOperator.BITWISE_XOR; + } else if (assignment.operator == AssignmentOperator.ADD) { + cop = CCodeAssignmentOperator.ADD; + } else if (assignment.operator == AssignmentOperator.SUB) { + cop = CCodeAssignmentOperator.SUB; + } else if (assignment.operator == AssignmentOperator.MUL) { + cop = CCodeAssignmentOperator.MUL; + } else if (assignment.operator == AssignmentOperator.DIV) { + cop = CCodeAssignmentOperator.DIV; + } else if (assignment.operator == AssignmentOperator.PERCENT) { + cop = CCodeAssignmentOperator.PERCENT; + } else if (assignment.operator == AssignmentOperator.SHIFT_LEFT) { + cop = CCodeAssignmentOperator.SHIFT_LEFT; + } else if (assignment.operator == AssignmentOperator.SHIFT_RIGHT) { + cop = CCodeAssignmentOperator.SHIFT_RIGHT; + } + + CCodeExpression codenode = new CCodeAssignment (lhs, rhs, cop); + + if (outer_ccomma != null) { + outer_ccomma.append_expression (codenode); + codenode = outer_ccomma; + } + + return codenode; + } + + CCodeExpression emit_fixed_length_array_assignment (Assignment assignment, ArrayType array_type) { + CCodeExpression rhs = (CCodeExpression) assignment.right.ccodenode; + CCodeExpression lhs = (CCodeExpression) get_ccodenode (assignment.left); + + source_declarations.add_include ("string.h"); + + // it is necessary to use memcpy for fixed-length (stack-allocated) arrays + // simple assignments do not work in C + var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); + sizeof_call.add_argument (new CCodeIdentifier (array_type.element_type.get_cname ())); + var size = new CCodeBinaryExpression (CCodeBinaryOperator.MUL, new CCodeConstant ("%d".printf (array_type.length)), sizeof_call); + var ccopy = new CCodeFunctionCall (new CCodeIdentifier ("memcpy")); + ccopy.add_argument (lhs); + ccopy.add_argument (rhs); + ccopy.add_argument (size); + + return ccopy; + } + + public override void visit_assignment (Assignment assignment) { + assignment.right.accept (codegen); + + if (assignment.left.error || assignment.right.error) { + assignment.error = true; + return; + } + + if (assignment.left.symbol_reference is Property) { + assignment.ccodenode = emit_property_assignment (assignment); + } else { + var array_type = assignment.left.value_type as ArrayType; + if (array_type != null && array_type.fixed_length) { + assignment.ccodenode = emit_fixed_length_array_assignment (assignment, array_type); + } else { + assignment.ccodenode = emit_simple_assignment (assignment); + } + } + } +} diff --git a/codegen/valadovabasemodule.vala b/codegen/valadovabasemodule.vala new file mode 100644 index 000000000..f58a52540 --- /dev/null +++ b/codegen/valadovabasemodule.vala @@ -0,0 +1,2142 @@ +/* valadovabasemodule.vala + * + * Copyright (C) 2006-2010 Jürg Billeter + * Copyright (C) 2006-2008 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 <j@bitron.ch> + * Raffaele Sandrini <raffaele@sandrini.ch> + */ + +/** + * Code visitor generating C Code. + */ +internal class Vala.DovaBaseModule : CCodeModule { + public CodeContext context { get; set; } + + public Symbol root_symbol; + public Symbol current_symbol; + public TryStatement current_try; + + public TypeSymbol? current_type_symbol { + get { + var sym = current_symbol; + while (sym != null) { + if (sym is TypeSymbol) { + return (TypeSymbol) sym; + } + sym = sym.parent_symbol; + } + return null; + } + } + + public Class? current_class { + get { return current_type_symbol as Class; } + } + + public Method? current_method { + get { + var sym = current_symbol; + while (sym is Block) { + sym = sym.parent_symbol; + } + return sym as Method; + } + } + + public PropertyAccessor? current_property_accessor { + get { + var sym = current_symbol; + while (sym is Block) { + sym = sym.parent_symbol; + } + return sym as PropertyAccessor; + } + } + + public DataType? current_return_type { + get { + var m = current_method; + if (m != null) { + return m.return_type; + } + + var acc = current_property_accessor; + if (acc != null) { + if (acc.readable) { + return acc.value_type; + } else { + return void_type; + } + } + + return null; + } + } + + public CCodeDeclarationSpace header_declarations; + public CCodeDeclarationSpace internal_header_declarations; + public CCodeDeclarationSpace source_declarations; + + public CCodeFragment source_type_member_definition; + public CCodeFragment instance_init_fragment; + public CCodeFragment instance_finalize_fragment; + + // code nodes to be inserted before the current statement + // used by async method calls in coroutines + public CCodeFragment pre_statement_fragment; + + /* all temporary variables */ + public ArrayList<LocalVariable> temp_vars = new ArrayList<LocalVariable> (); + /* temporary variables that own their content */ + public ArrayList<LocalVariable> temp_ref_vars = new ArrayList<LocalVariable> (); + /* (constant) hash table with all reserved identifiers in the generated code */ + Set<string> reserved_identifiers; + + public int next_temp_var_id = 0; + public int next_string_const_id = 0; + public bool in_creation_method { get { return current_method is CreationMethod; } } + public bool current_method_inner_error = false; + + public DataType void_type = new VoidType (); + public DataType bool_type; + public DataType char_type; + public DataType short_type; + public DataType ushort_type; + public DataType int_type; + public DataType uint_type; + public DataType long_type; + public DataType ulong_type; + public DataType string_type; + public DataType float_type; + public DataType double_type; + public Class object_class; + public Class type_class; + public Class value_class; + public Class array_class; + public Class delegate_class; + + Set<Symbol> generated_external_symbols; + + public Map<string,string> variable_name_map = new HashMap<string,string> (str_hash, str_equal); + + public DovaBaseModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + + reserved_identifiers = new HashSet<string> (str_hash, str_equal); + + // C99 keywords + reserved_identifiers.add ("_Bool"); + reserved_identifiers.add ("_Complex"); + reserved_identifiers.add ("_Imaginary"); + reserved_identifiers.add ("auto"); + reserved_identifiers.add ("break"); + reserved_identifiers.add ("case"); + reserved_identifiers.add ("char"); + reserved_identifiers.add ("const"); + reserved_identifiers.add ("continue"); + reserved_identifiers.add ("default"); + reserved_identifiers.add ("do"); + reserved_identifiers.add ("double"); + reserved_identifiers.add ("else"); + reserved_identifiers.add ("enum"); + reserved_identifiers.add ("extern"); + reserved_identifiers.add ("float"); + reserved_identifiers.add ("for"); + reserved_identifiers.add ("goto"); + reserved_identifiers.add ("if"); + reserved_identifiers.add ("inline"); + reserved_identifiers.add ("int"); + reserved_identifiers.add ("long"); + reserved_identifiers.add ("register"); + reserved_identifiers.add ("restrict"); + reserved_identifiers.add ("return"); + reserved_identifiers.add ("short"); + reserved_identifiers.add ("signed"); + reserved_identifiers.add ("sizeof"); + reserved_identifiers.add ("static"); + reserved_identifiers.add ("struct"); + reserved_identifiers.add ("switch"); + reserved_identifiers.add ("typedef"); + reserved_identifiers.add ("union"); + reserved_identifiers.add ("unsigned"); + reserved_identifiers.add ("void"); + reserved_identifiers.add ("volatile"); + reserved_identifiers.add ("while"); + + // reserved for Vala naming conventions + reserved_identifiers.add ("error"); + reserved_identifiers.add ("result"); + reserved_identifiers.add ("this"); + } + + public override void emit (CodeContext context) { + this.context = context; + + root_symbol = context.root; + + bool_type = new BooleanType ((Struct) root_symbol.scope.lookup ("bool")); + char_type = new IntegerType ((Struct) root_symbol.scope.lookup ("char")); + short_type = new IntegerType ((Struct) root_symbol.scope.lookup ("short")); + ushort_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ushort")); + int_type = new IntegerType ((Struct) root_symbol.scope.lookup ("int")); + uint_type = new IntegerType ((Struct) root_symbol.scope.lookup ("uint")); + long_type = new IntegerType ((Struct) root_symbol.scope.lookup ("long")); + ulong_type = new IntegerType ((Struct) root_symbol.scope.lookup ("ulong")); + float_type = new FloatingType ((Struct) root_symbol.scope.lookup ("float")); + double_type = new FloatingType ((Struct) root_symbol.scope.lookup ("double")); + string_type = new ObjectType ((Class) root_symbol.scope.lookup ("string")); + + var dova_ns = (Namespace) root_symbol.scope.lookup ("Dova"); + object_class = (Class) dova_ns.scope.lookup ("Object"); + type_class = (Class) dova_ns.scope.lookup ("Type"); + value_class = (Class) dova_ns.scope.lookup ("Value"); + array_class = (Class) dova_ns.scope.lookup ("Array"); + delegate_class = (Class) dova_ns.scope.lookup ("Delegate"); + + header_declarations = new CCodeDeclarationSpace (); + internal_header_declarations = new CCodeDeclarationSpace (); + + /* we're only interested in non-pkg source files */ + var source_files = context.get_source_files (); + foreach (SourceFile file in source_files) { + if (!file.external_package) { + file.accept (codegen); + } + } + + // generate C header file for public API + if (context.header_filename != null) { + var writer = new CCodeWriter (context.header_filename); + if (!writer.open (context.version_header)) { + Report.error (null, "unable to open `%s' for writing".printf (writer.filename)); + return; + } + writer.write_newline (); + + var once = new CCodeOnceSection (get_define_for_filename (writer.filename)); + once.append (new CCodeNewline ()); + once.append (header_declarations.include_directives); + once.append (new CCodeNewline ()); + + once.append (new CCodeNewline ()); + once.append (header_declarations.type_declaration); + once.append (new CCodeNewline ()); + once.append (header_declarations.type_definition); + once.append (new CCodeNewline ()); + once.append (header_declarations.type_member_declaration); + once.append (new CCodeNewline ()); + once.append (header_declarations.constant_declaration); + once.append (new CCodeNewline ()); + + once.append (new CCodeNewline ()); + once.write (writer); + writer.close (); + } + + // generate C header file for internal API + if (context.internal_header_filename != null) { + var writer = new CCodeWriter (context.internal_header_filename); + if (!writer.open (context.version_header)) { + Report.error (null, "unable to open `%s' for writing".printf (writer.filename)); + return; + } + writer.write_newline (); + + var once = new CCodeOnceSection (get_define_for_filename (writer.filename)); + once.append (new CCodeNewline ()); + once.append (internal_header_declarations.include_directives); + once.append (new CCodeNewline ()); + + once.append (new CCodeNewline ()); + once.append (internal_header_declarations.type_declaration); + once.append (new CCodeNewline ()); + once.append (internal_header_declarations.type_definition); + once.append (new CCodeNewline ()); + once.append (internal_header_declarations.type_member_declaration); + once.append (new CCodeNewline ()); + once.append (internal_header_declarations.constant_declaration); + once.append (new CCodeNewline ()); + + once.append (new CCodeNewline ()); + once.write (writer); + writer.close (); + } + } + + public override void visit_source_file (SourceFile source_file) { + source_declarations = new CCodeDeclarationSpace (); + source_type_member_definition = new CCodeFragment (); + + next_temp_var_id = 0; + variable_name_map.clear (); + + generated_external_symbols = new HashSet<Symbol> (); + + source_file.accept_children (codegen); + + if (context.report.get_errors () > 0) { + return; + } + + var writer = new CCodeWriter (source_file.get_csource_filename ()); + if (!writer.open (context.version_header)) { + Report.error (null, "unable to open `%s' for writing".printf (writer.filename)); + return; + } + writer.line_directives = context.debug; + + writer.write_newline (); + source_declarations.include_directives.write (writer); + writer.write_newline (); + source_declarations.type_declaration.write_combined (writer); + writer.write_newline (); + source_declarations.type_definition.write_combined (writer); + writer.write_newline (); + source_declarations.type_member_declaration.write_declaration (writer); + writer.write_newline (); + source_declarations.type_member_declaration.write (writer); + writer.write_newline (); + source_declarations.constant_declaration.write_combined (writer); + writer.write_newline (); + source_type_member_definition.write (writer); + writer.write_newline (); + writer.close (); + + source_declarations = null; + source_type_member_definition = null; + } + + private static string get_define_for_filename (string filename) { + var define = new StringBuilder ("__"); + + var i = filename; + while (i.len () > 0) { + var c = i.get_char (); + if (c.isalnum () && c < 0x80) { + define.append_unichar (c.toupper ()); + } else { + define.append_c ('_'); + } + + i = i.next_char (); + } + + define.append ("__"); + + return define.str; + } + + public void generate_enum_declaration (Enum en, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (en, en.get_cname ())) { + return; + } + + var cenum = new CCodeEnum (en.get_cname ()); + + foreach (EnumValue ev in en.get_values ()) { + if (ev.value == null) { + cenum.add_value (new CCodeEnumValue (ev.get_cname ())); + } else { + ev.value.accept (codegen); + cenum.add_value (new CCodeEnumValue (ev.get_cname (), (CCodeExpression) ev.value.ccodenode)); + } + } + + decl_space.add_type_definition (cenum); + decl_space.add_type_definition (new CCodeNewline ()); + } + + public override void visit_enum (Enum en) { + en.accept_children (codegen); + + generate_enum_declaration (en, source_declarations); + + if (!en.is_internal_symbol ()) { + generate_enum_declaration (en, header_declarations); + } + generate_enum_declaration (en, internal_header_declarations); + } + + public override void visit_member (Member m) { + } + + public void generate_constant_declaration (Constant c, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (c, c.get_cname ())) { + return; + } + + c.accept_children (codegen); + + if (!c.external) { + if (c.initializer is InitializerList) { + var cdecl = new CCodeDeclaration (c.type_reference.get_const_cname ()); + var arr = ""; + if (c.type_reference is ArrayType) { + arr = "[]"; + } + cdecl.add_declarator (new CCodeVariableDeclarator ("%s%s".printf (c.get_cname (), arr), (CCodeExpression) c.initializer.ccodenode)); + cdecl.modifiers = CCodeModifiers.STATIC; + + decl_space.add_constant_declaration (cdecl); + } else { + var cdefine = new CCodeMacroReplacement.with_expression (c.get_cname (), (CCodeExpression) c.initializer.ccodenode); + decl_space.add_type_member_declaration (cdefine); + } + } + } + + public override void visit_constant (Constant c) { + generate_constant_declaration (c, source_declarations); + + if (!c.is_internal_symbol ()) { + generate_constant_declaration (c, header_declarations); + } + generate_constant_declaration (c, internal_header_declarations); + } + + public void generate_field_declaration (Field f, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (f, f.get_cname ())) { + return; + } + + generate_type_declaration (f.field_type, decl_space); + + string field_ctype = f.field_type.get_cname (); + if (f.is_volatile) { + field_ctype = "volatile " + field_ctype; + } + + var cdecl = new CCodeDeclaration (field_ctype); + cdecl.add_declarator (new CCodeVariableDeclarator (f.get_cname ())); + if (f.is_private_symbol ()) { + cdecl.modifiers = CCodeModifiers.STATIC; + } else { + cdecl.modifiers = CCodeModifiers.EXTERN; + } + decl_space.add_type_member_declaration (cdecl); + } + + public override void visit_field (Field f) { + f.accept_children (codegen); + + var cl = f.parent_symbol as Class; + + CCodeExpression lhs = null; + + string field_ctype = f.field_type.get_cname (); + if (f.is_volatile) { + field_ctype = "volatile " + field_ctype; + } + + if (f.binding == MemberBinding.INSTANCE) { + if (cl != null && f.access == SymbolAccessibility.PRIVATE) { + var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_PRIVATE".printf (cl.get_upper_case_cname (null)))); + priv_call.add_argument (new CCodeIdentifier ("this")); + lhs = new CCodeMemberAccess.pointer (priv_call, f.get_cname ()); + } else { + lhs = new CCodeMemberAccess.pointer (new CCodeIdentifier ("this"), f.get_cname ()); + } + + if (f.initializer != null) { + var rhs = (CCodeExpression) f.initializer.ccodenode; + + instance_init_fragment.append (new CCodeExpressionStatement (new CCodeAssignment (lhs, rhs))); + + append_temp_decl (instance_init_fragment, temp_vars); + temp_vars.clear (); + } + + if (requires_destroy (f.field_type) && instance_finalize_fragment != null) { + var this_access = new MemberAccess.simple ("this"); + this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol); + + var field_st = f.parent_symbol as Struct; + if (field_st != null && !field_st.is_simple_type ()) { + this_access.ccodenode = new CCodeIdentifier ("(*this)"); + } else { + this_access.ccodenode = new CCodeIdentifier ("this"); + } + + var ma = new MemberAccess (this_access, f.name); + ma.symbol_reference = f; + instance_finalize_fragment.append (new CCodeExpressionStatement (get_unref_expression (lhs, f.field_type, ma))); + } + } else { + generate_field_declaration (f, source_declarations); + + if (!f.is_internal_symbol ()) { + generate_field_declaration (f, header_declarations); + } + generate_field_declaration (f, internal_header_declarations); + + lhs = new CCodeIdentifier (f.get_cname ()); + + var var_decl = new CCodeVariableDeclarator (f.get_cname ()); + var_decl.initializer = default_value_for_type (f.field_type, true); + + if (f.initializer != null) { + var init = (CCodeExpression) f.initializer.ccodenode; + if (is_constant_ccode_expression (init)) { + var_decl.initializer = init; + } + } + + var var_def = new CCodeDeclaration (field_ctype); + var_def.add_declarator (var_decl); + if (!f.is_private_symbol ()) { + var_def.modifiers = CCodeModifiers.EXTERN; + } else { + var_def.modifiers = CCodeModifiers.STATIC; + } + source_declarations.add_type_member_declaration (var_def); + } + } + + public bool is_constant_ccode_expression (CCodeExpression cexpr) { + if (cexpr is CCodeConstant) { + return true; + } else if (cexpr is CCodeCastExpression) { + var ccast = (CCodeCastExpression) cexpr; + return is_constant_ccode_expression (ccast.inner); + } else if (cexpr is CCodeBinaryExpression) { + var cbinary = (CCodeBinaryExpression) cexpr; + return is_constant_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right); + } + + var cparenthesized = (cexpr as CCodeParenthesizedExpression); + return (null != cparenthesized && is_constant_ccode_expression (cparenthesized.inner)); + } + + /** + * Returns whether the passed cexpr is a pure expression, i.e. an + * expression without side-effects. + */ + public bool is_pure_ccode_expression (CCodeExpression cexpr) { + if (cexpr is CCodeConstant || cexpr is CCodeIdentifier) { + return true; + } else if (cexpr is CCodeBinaryExpression) { + var cbinary = (CCodeBinaryExpression) cexpr; + return is_pure_ccode_expression (cbinary.left) && is_constant_ccode_expression (cbinary.right); + } else if (cexpr is CCodeUnaryExpression) { + var cunary = (CCodeUnaryExpression) cexpr; + switch (cunary.operator) { + case CCodeUnaryOperator.PREFIX_INCREMENT: + case CCodeUnaryOperator.PREFIX_DECREMENT: + case CCodeUnaryOperator.POSTFIX_INCREMENT: + case CCodeUnaryOperator.POSTFIX_DECREMENT: + return false; + default: + return is_pure_ccode_expression (cunary.inner); + } + } else if (cexpr is CCodeMemberAccess) { + var cma = (CCodeMemberAccess) cexpr; + return is_pure_ccode_expression (cma.inner); + } else if (cexpr is CCodeElementAccess) { + var cea = (CCodeElementAccess) cexpr; + return is_pure_ccode_expression (cea.container) && is_pure_ccode_expression (cea.index); + } else if (cexpr is CCodeCastExpression) { + var ccast = (CCodeCastExpression) cexpr; + return is_pure_ccode_expression (ccast.inner); + } else if (cexpr is CCodeParenthesizedExpression) { + var cparenthesized = (CCodeParenthesizedExpression) cexpr; + return is_pure_ccode_expression (cparenthesized.inner); + } + + return false; + } + + public override void visit_formal_parameter (FormalParameter p) { + p.accept_children (codegen); + } + + public override void visit_property (Property prop) { + int old_next_temp_var_id = next_temp_var_id; + var old_temp_vars = temp_vars; + var old_temp_ref_vars = temp_ref_vars; + var old_variable_name_map = variable_name_map; + next_temp_var_id = 0; + temp_vars = new ArrayList<LocalVariable> (); + temp_ref_vars = new ArrayList<LocalVariable> (); + variable_name_map = new HashMap<string,string> (str_hash, str_equal); + + prop.accept_children (codegen); + + next_temp_var_id = old_next_temp_var_id; + temp_vars = old_temp_vars; + temp_ref_vars = old_temp_ref_vars; + variable_name_map = old_variable_name_map; + } + + public void generate_type_declaration (DataType type, CCodeDeclarationSpace decl_space) { + if (type is ObjectType) { + var object_type = (ObjectType) type; + if (object_type.type_symbol is Class) { + generate_class_declaration ((Class) object_type.type_symbol, decl_space); + } else if (object_type.type_symbol is Interface) { + generate_interface_declaration ((Interface) object_type.type_symbol, decl_space); + } + } else if (type is DelegateType) { + var deleg_type = (DelegateType) type; + var d = deleg_type.delegate_symbol; + generate_delegate_declaration (d, decl_space); + } else if (type.data_type is Enum) { + var en = (Enum) type.data_type; + generate_enum_declaration (en, decl_space); + } else if (type is ValueType) { + var value_type = (ValueType) type; + generate_struct_declaration ((Struct) value_type.type_symbol, decl_space); + } else if (type is ArrayType) { + var array_type = (ArrayType) type; + generate_type_declaration (array_type.element_type, decl_space); + } else if (type is PointerType) { + var pointer_type = (PointerType) type; + generate_type_declaration (pointer_type.base_type, decl_space); + } + + foreach (DataType type_arg in type.get_type_arguments ()) { + generate_type_declaration (type_arg, decl_space); + } + } + + public virtual void generate_struct_declaration (Struct st, CCodeDeclarationSpace decl_space) { + } + + public virtual void generate_delegate_declaration (Delegate d, CCodeDeclarationSpace decl_space) { + } + + public virtual void generate_cparameters (Method m, CCodeDeclarationSpace decl_space, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, CCodeFunctionCall? vcall = null) { + } + + public virtual void generate_property_accessor_declaration (PropertyAccessor acc, CCodeDeclarationSpace decl_space) { + } + + public override void visit_destructor (Destructor d) { + bool old_method_inner_error = current_method_inner_error; + current_method_inner_error = false; + + d.accept_children (codegen); + + CCodeFragment cfrag = new CCodeFragment (); + + cfrag.append (d.body.ccodenode); + + d.ccodenode = cfrag; + + current_method_inner_error = old_method_inner_error; + } + + public override void visit_block (Block b) { + var old_symbol = current_symbol; + current_symbol = b; + + b.accept_children (codegen); + + var local_vars = b.get_local_variables (); + foreach (LocalVariable local in local_vars) { + local.active = false; + } + + var cblock = new CCodeBlock (); + + foreach (CodeNode stmt in b.get_statements ()) { + if (stmt.error) { + continue; + } + + if (stmt.ccodenode is CCodeFragment) { + foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) { + cblock.add_statement (cstmt); + } + } else { + cblock.add_statement (stmt.ccodenode); + } + } + + foreach (LocalVariable local in local_vars) { + if (!local.floating && requires_destroy (local.variable_type)) { + var ma = new MemberAccess.simple (local.name); + ma.symbol_reference = local; + cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma))); + } + } + + if (b.parent_symbol is Method) { + var m = (Method) b.parent_symbol; + foreach (FormalParameter param in m.get_parameters ()) { + if (requires_destroy (param.parameter_type) && param.direction == ParameterDirection.IN) { + var ma = new MemberAccess.simple (param.name); + ma.symbol_reference = param; + cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (param.name), param.parameter_type, ma))); + } + } + } + + b.ccodenode = cblock; + + current_symbol = old_symbol; + } + + public override void visit_empty_statement (EmptyStatement stmt) { + stmt.ccodenode = new CCodeEmptyStatement (); + } + + public override void visit_declaration_statement (DeclarationStatement stmt) { + stmt.declaration.accept (codegen); + + stmt.ccodenode = stmt.declaration.ccodenode; + + var local = stmt.declaration as LocalVariable; + if (local != null && local.initializer != null) { + create_temp_decl (stmt, local.initializer.temp_vars); + } + + create_temp_decl (stmt, temp_vars); + temp_vars.clear (); + } + + public CCodeExpression get_variable_cexpression (string name) { + return new CCodeIdentifier (get_variable_cname (name)); + } + + public string get_variable_cname (string name) { + if (name[0] == '.') { + // compiler-internal variable + if (!variable_name_map.contains (name)) { + variable_name_map.set (name, "_tmp%d_".printf (next_temp_var_id)); + next_temp_var_id++; + } + return variable_name_map.get (name); + } else if (reserved_identifiers.contains (name)) { + return "_%s_".printf (name); + } else { + return name; + } + } + + public override void visit_local_variable (LocalVariable local) { + local.accept_children (codegen); + + generate_type_declaration (local.variable_type, source_declarations); + + CCodeExpression rhs = null; + if (local.initializer != null && local.initializer.ccodenode != null) { + rhs = (CCodeExpression) local.initializer.ccodenode; + } + + var cfrag = new CCodeFragment (); + + if (pre_statement_fragment != null) { + cfrag.append (pre_statement_fragment); + pre_statement_fragment = null; + } + + var cvar = new CCodeVariableDeclarator (get_variable_cname (local.name), rhs, local.variable_type.get_cdeclarator_suffix ()); + + var cdecl = new CCodeDeclaration (local.variable_type.get_cname ()); + cdecl.add_declarator (cvar); + cfrag.append (cdecl); + + // try to initialize uninitialized variables + // initialization not necessary for variables stored in closure + if (cvar.initializer == null) { + cvar.initializer = default_value_for_type (local.variable_type, true); + cvar.init0 = true; + } + + if (local.initializer != null && local.initializer.tree_can_fail) { + head.add_simple_check (local.initializer, cfrag); + } + + local.ccodenode = cfrag; + + local.active = true; + } + + public override void visit_initializer_list (InitializerList list) { + list.accept_children (codegen); + + if (list.target_type.data_type is Struct) { + /* initializer is used as struct initializer */ + var st = (Struct) list.target_type.data_type; + + var clist = new CCodeInitializerList (); + + var field_it = st.get_fields ().iterator (); + foreach (Expression expr in list.get_initializers ()) { + Field field = null; + while (field == null) { + field_it.next (); + field = field_it.get (); + if (field.binding != MemberBinding.INSTANCE) { + // we only initialize instance fields + field = null; + } + } + + var cexpr = (CCodeExpression) expr.ccodenode; + + string ctype = field.get_ctype (); + if (ctype != null) { + cexpr = new CCodeCastExpression (cexpr, ctype); + } + + clist.append (cexpr); + } + + list.ccodenode = clist; + } else { + var clist = new CCodeInitializerList (); + foreach (Expression expr in list.get_initializers ()) { + clist.append ((CCodeExpression) expr.ccodenode); + } + list.ccodenode = clist; + } + } + + public LocalVariable get_temp_variable (DataType type, bool value_owned = true, CodeNode? node_reference = null) { + var var_type = type.copy (); + var_type.value_owned = value_owned; + var local = new LocalVariable (var_type, "_tmp%d_".printf (next_temp_var_id)); + + if (node_reference != null) { + local.source_reference = node_reference.source_reference; + } + + next_temp_var_id++; + + return local; + } + + bool is_in_generic_type (DataType type) { + if (type.type_parameter.parent_symbol is TypeSymbol + && (current_method == null || current_method.binding == MemberBinding.INSTANCE)) { + return true; + } else { + return false; + } + } + + public CCodeExpression get_type_private_from_type (ObjectTypeSymbol type_symbol, CCodeExpression type_expression) { + if (type_symbol is Class) { + // class + return new CCodeCastExpression (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeCastExpression (type_expression, "char *"), new CCodeIdentifier ("_%s_type_offset".printf (((Class) type_symbol).get_lower_case_cname ()))), "%sTypePrivate *".printf (((Class) type_symbol).get_cname ())); + } else { + // interface + var get_interface = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_interface")); + get_interface.add_argument (type_expression); + get_interface.add_argument (new CCodeIdentifier ("%s_type".printf (((Interface) type_symbol).get_lower_case_cname ()))); + return new CCodeCastExpression (get_interface, "%sTypePrivate *".printf (((Interface) type_symbol).get_cname ())); + } + } + + public CCodeExpression get_type_id_expression (DataType type, bool is_chainup = false) { + if (type is GenericType) { + string var_name = "%s_type".printf (type.type_parameter.name.down ()); + if (is_in_generic_type (type) && !is_chainup) { + return new CCodeMemberAccess.pointer (get_type_private_from_type ((ObjectTypeSymbol) type.type_parameter.parent_symbol, new CCodeMemberAccess.pointer (new CCodeIdentifier ("this"), "type")), var_name); + } else { + return new CCodeIdentifier (var_name); + } + } else { + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (type.data_type.get_lower_case_cname ()))); + var object_type_symbol = type.data_type as ObjectTypeSymbol; + if (object_type_symbol != null) { + for (int i = 0; i < object_type_symbol.get_type_parameters ().size; i++) { + if (type.get_type_arguments ().size == 0) { + ccall.add_argument (new CCodeConstant ("NULL")); + } else { + ccall.add_argument (get_type_id_expression (type.get_type_arguments ().get (i))); + } + } + } + return ccall; + } + } + + public virtual CCodeExpression? get_dup_func_expression (DataType type, SourceReference? source_reference, bool is_chainup = false) { + if (type.data_type != null) { + string dup_function = ""; + if (type.data_type.is_reference_counting ()) { + dup_function = type.data_type.get_ref_function (); + } else if (type is ValueType) { + dup_function = type.data_type.get_dup_function (); + if (dup_function == null) { + dup_function = ""; + } + } + + return new CCodeIdentifier (dup_function); + } else if (type.type_parameter != null) { + return null; + } else if (type is ArrayType) { + return new CCodeIdentifier ("dova_object_ref"); + } else if (type is DelegateType) { + return new CCodeIdentifier ("dova_object_ref"); + } else if (type is PointerType) { + var pointer_type = (PointerType) type; + return get_dup_func_expression (pointer_type.base_type, source_reference); + } else { + source_declarations.add_include ("stddef.h"); + return new CCodeConstant ("NULL"); + } + } + + public CCodeExpression? get_destroy_func_expression (DataType type, bool is_chainup = false) { + if (type.data_type != null) { + string unref_function; + if (type is ReferenceType) { + if (type.data_type.is_reference_counting ()) { + unref_function = type.data_type.get_unref_function (); + } else { + unref_function = type.data_type.get_free_function (); + } + } else { + if (type.nullable) { + unref_function = type.data_type.get_free_function (); + if (unref_function == null) { + unref_function = "free"; + } + } else { + var st = (Struct) type.data_type; + unref_function = st.get_copy_function (); + } + } + if (unref_function == null) { + source_declarations.add_include ("stddef.h"); + return new CCodeConstant ("NULL"); + } + return new CCodeIdentifier (unref_function); + } else if (type.type_parameter != null && current_type_symbol is Class) { + // FIXME ask type for dup/ref function + return new CCodeIdentifier ("dova_object_unref"); + } else if (type is ArrayType) { + return new CCodeIdentifier ("dova_object_unref"); + } else if (type is DelegateType) { + return new CCodeIdentifier ("dova_object_unref"); + } else if (type is PointerType) { + return new CCodeIdentifier ("free"); + } else { + source_declarations.add_include ("stddef.h"); + return new CCodeConstant ("NULL"); + } + } + + public virtual CCodeExpression get_unref_expression (CCodeExpression cvar, DataType type, Expression? expr = null) { + var ccall = new CCodeFunctionCall (get_destroy_func_expression (type)); + + if (type is ValueType && !type.nullable) { + // normal value type, no null check + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cvar)); + ccall.add_argument (new CCodeConstant ("0")); + ccall.add_argument (new CCodeConstant ("NULL")); + ccall.add_argument (new CCodeConstant ("0")); + + return ccall; + } + + /* (foo == NULL ? NULL : foo = (unref (foo), NULL)) */ + + /* can be simplified to + * foo = (unref (foo), NULL) + * if foo is of static type non-null + */ + + source_declarations.add_include ("stddef.h"); + + var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, cvar, new CCodeConstant ("NULL")); + if (type.type_parameter != null) { + if (!(current_type_symbol is Class) || current_class.is_compact) { + return new CCodeConstant ("NULL"); + } + + // unref functions are optional for type parameters + var cunrefisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_destroy_func_expression (type), new CCodeConstant ("NULL")); + cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cunrefisnull); + } + + ccall.add_argument (cvar); + + /* set freed references to NULL to prevent further use */ + var ccomma = new CCodeCommaExpression (); + + ccomma.append_expression (ccall); + ccomma.append_expression (new CCodeConstant ("NULL")); + + var cassign = new CCodeAssignment (cvar, ccomma); + + return new CCodeConditionalExpression (cisnull, new CCodeConstant ("NULL"), cassign); + } + + public override void visit_end_full_expression (Expression expr) { + /* expr is a full expression, i.e. an initializer, the + * expression in an expression statement, the controlling + * expression in if, while, for, or foreach statements + * + * we unref temporary variables at the end of a full + * expression + */ + + /* can't automatically deep copy lists yet, so do it + * manually for now + * replace with + * expr.temp_vars = temp_vars; + * when deep list copying works + */ + expr.temp_vars.clear (); + foreach (LocalVariable local in temp_vars) { + expr.temp_vars.add (local); + } + temp_vars.clear (); + + if (((List<LocalVariable>) temp_ref_vars).size == 0) { + /* nothing to do without temporary variables */ + return; + } + + var expr_type = expr.value_type; + if (expr.target_type != null) { + expr_type = expr.target_type; + } + + var full_expr_var = get_temp_variable (expr_type, true, expr); + expr.temp_vars.add (full_expr_var); + + var expr_list = new CCodeCommaExpression (); + expr_list.append_expression (new CCodeAssignment (get_variable_cexpression (full_expr_var.name), (CCodeExpression) expr.ccodenode)); + + foreach (LocalVariable local in temp_ref_vars) { + var ma = new MemberAccess.simple (local.name); + ma.symbol_reference = local; + expr_list.append_expression (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)); + } + + expr_list.append_expression (get_variable_cexpression (full_expr_var.name)); + + expr.ccodenode = expr_list; + + temp_ref_vars.clear (); + } + + public void append_temp_decl (CCodeFragment cfrag, List<LocalVariable> temp_vars) { + foreach (LocalVariable local in temp_vars) { + var cdecl = new CCodeDeclaration (local.variable_type.get_cname ()); + + var vardecl = new CCodeVariableDeclarator (local.name, null, local.variable_type.get_cdeclarator_suffix ()); + // sets #line + local.ccodenode = vardecl; + cdecl.add_declarator (vardecl); + + var st = local.variable_type.data_type as Struct; + var array_type = local.variable_type as ArrayType; + + if (local.name.has_prefix ("*")) { + // do not dereference unintialized variable + // initialization is not needed for these special + // pointer temp variables + // used to avoid side-effects in assignments + } else if (local.variable_type is GenericType) { + var value_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_value_size")); + value_size.add_argument (get_type_id_expression (local.variable_type)); + + var alloca_call = new CCodeFunctionCall (new CCodeIdentifier ("alloca")); + alloca_call.add_argument (value_size); + + // memset needs string.h + source_declarations.add_include ("string.h"); + var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset")); + memset_call.add_argument (alloca_call); + memset_call.add_argument (new CCodeConstant ("0")); + memset_call.add_argument (value_size); + + vardecl.initializer = memset_call; + vardecl.init0 = true; + } else if (!local.variable_type.nullable && + (st != null && !st.is_simple_type ()) || + (array_type != null && array_type.fixed_length)) { + // 0-initialize struct with struct initializer { 0 } + // necessary as they will be passed by reference + var clist = new CCodeInitializerList (); + clist.append (new CCodeConstant ("0")); + + vardecl.initializer = clist; + vardecl.init0 = true; + } else if (local.variable_type.is_reference_type_or_type_parameter () || + local.variable_type.nullable) { + source_declarations.add_include ("stddef.h"); + vardecl.initializer = new CCodeConstant ("NULL"); + vardecl.init0 = true; + } + + cfrag.append (cdecl); + } + } + + public override void visit_expression_statement (ExpressionStatement stmt) { + stmt.accept_children (codegen); + + if (stmt.expression.error) { + stmt.error = true; + return; + } + + stmt.ccodenode = new CCodeExpressionStatement ((CCodeExpression) stmt.expression.ccodenode); + + if (stmt.tree_can_fail && stmt.expression.tree_can_fail) { + // simple case, no node breakdown necessary + + var cfrag = new CCodeFragment (); + + cfrag.append (stmt.ccodenode); + + head.add_simple_check (stmt.expression, cfrag); + + stmt.ccodenode = cfrag; + } + + /* free temporary objects */ + + if (((List<LocalVariable>) temp_vars).size == 0 + && pre_statement_fragment == null) { + /* nothing to do without temporary variables */ + return; + } + + var cfrag = new CCodeFragment (); + append_temp_decl (cfrag, temp_vars); + + if (pre_statement_fragment != null) { + cfrag.append (pre_statement_fragment); + pre_statement_fragment = null; + } + + cfrag.append (stmt.ccodenode); + + foreach (LocalVariable local in temp_ref_vars) { + var ma = new MemberAccess.simple (local.name); + ma.symbol_reference = local; + cfrag.append (new CCodeExpressionStatement (get_unref_expression (new CCodeIdentifier (local.name), local.variable_type, ma))); + } + + stmt.ccodenode = cfrag; + + temp_vars.clear (); + temp_ref_vars.clear (); + } + + public void create_temp_decl (Statement stmt, List<LocalVariable> temp_vars) { + /* declare temporary variables */ + + if (temp_vars.size == 0) { + /* nothing to do without temporary variables */ + return; + } + + var cfrag = new CCodeFragment (); + append_temp_decl (cfrag, temp_vars); + + cfrag.append (stmt.ccodenode); + + stmt.ccodenode = cfrag; + } + + public virtual void append_local_free (Symbol sym, CCodeFragment cfrag, bool stop_at_loop = false) { + var b = (Block) sym; + + var local_vars = b.get_local_variables (); + foreach (LocalVariable local in local_vars) { + if (local.active && !local.floating && requires_destroy (local.variable_type)) { + var ma = new MemberAccess.simple (local.name); + ma.symbol_reference = local; + cfrag.append (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma))); + } + } + + if (stop_at_loop) { + if (b.parent_node is Loop || + b.parent_node is ForeachStatement || + b.parent_node is SwitchStatement) { + return; + } + } + + if (sym.parent_symbol is Block) { + append_local_free (sym.parent_symbol, cfrag, stop_at_loop); + } else if (sym.parent_symbol is Method) { + append_param_free ((Method) sym.parent_symbol, cfrag); + } + } + + public void append_error_free (Symbol sym, CCodeFragment cfrag, TryStatement current_try) { + var b = (Block) sym; + + var local_vars = b.get_local_variables (); + foreach (LocalVariable local in local_vars) { + if (local.active && !local.floating && requires_destroy (local.variable_type)) { + var ma = new MemberAccess.simple (local.name); + ma.symbol_reference = local; + cfrag.append (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma))); + } + } + + if (sym == current_try.body) { + return; + } + + if (sym.parent_symbol is Block) { + append_error_free (sym.parent_symbol, cfrag, current_try); + } else if (sym.parent_symbol is Method) { + append_param_free ((Method) sym.parent_symbol, cfrag); + } + } + + private void append_param_free (Method m, CCodeFragment cfrag) { + foreach (FormalParameter param in m.get_parameters ()) { + if (requires_destroy (param.parameter_type) && param.direction == ParameterDirection.IN) { + var ma = new MemberAccess.simple (param.name); + ma.symbol_reference = param; + cfrag.append (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (param.name), param.parameter_type, ma))); + } + } + } + + public void create_local_free (CodeNode stmt, bool stop_at_loop = false) { + var cfrag = new CCodeFragment (); + + append_local_free (current_symbol, cfrag, stop_at_loop); + + cfrag.append (stmt.ccodenode); + stmt.ccodenode = cfrag; + } + + public override void visit_return_statement (ReturnStatement stmt) { + stmt.accept_children (codegen); + + var cfrag = new CCodeFragment (); + + // free local variables + append_local_free (current_symbol, cfrag); + + cfrag.append (new CCodeReturnStatement ((current_return_type is VoidType) ? null : new CCodeIdentifier ("result"))); + + stmt.ccodenode = cfrag; + } + + public override void visit_delete_statement (DeleteStatement stmt) { + stmt.accept_children (codegen); + + var pointer_type = (PointerType) stmt.expression.value_type; + DataType type = pointer_type; + if (pointer_type.base_type.data_type != null && pointer_type.base_type.data_type.is_reference_type ()) { + type = pointer_type.base_type; + } + + var ccall = new CCodeFunctionCall (get_destroy_func_expression (type)); + ccall.add_argument ((CCodeExpression) stmt.expression.ccodenode); + stmt.ccodenode = new CCodeExpressionStatement (ccall); + } + + public override void visit_expression (Expression expr) { + if (expr.ccodenode != null && !expr.lvalue) { + // memory management, implicit casts, and boxing/unboxing + expr.ccodenode = transform_expression ((CCodeExpression) expr.ccodenode, expr.value_type, expr.target_type, expr); + } + } + + public override void visit_boolean_literal (BooleanLiteral expr) { + source_declarations.add_include ("stdbool.h"); + expr.ccodenode = new CCodeConstant (expr.value ? "true" : "false"); + } + + public override void visit_character_literal (CharacterLiteral expr) { + if (expr.get_char () >= 0x20 && expr.get_char () < 0x80) { + expr.ccodenode = new CCodeConstant (expr.value); + } else { + expr.ccodenode = new CCodeConstant ("%uU".printf (expr.get_char ())); + } + } + + public override void visit_integer_literal (IntegerLiteral expr) { + expr.ccodenode = new CCodeConstant (expr.value); + } + + public override void visit_real_literal (RealLiteral expr) { + string c_literal = expr.value; + if (c_literal.has_suffix ("d") || c_literal.has_suffix ("D")) { + // there is no suffix for double in C + c_literal = c_literal.substring (0, c_literal.length - 1); + } + if (!("." in c_literal || "e" in c_literal || "E" in c_literal)) { + // C requires period or exponent part for floating constants + if ("f" in c_literal || "F" in c_literal) { + c_literal = c_literal.substring (0, c_literal.length - 1) + ".f"; + } else { + c_literal += "."; + } + } + expr.ccodenode = new CCodeConstant (c_literal); + } + + public override void visit_string_literal (StringLiteral expr) { + var val = new CCodeInitializerList (); + val.append (new CCodeConstant ("0")); + // FIXME handle escaped characters in scanner/parser and escape them here again for C + val.append (new CCodeConstant ((expr.value.size () - 2).to_string ())); + val.append (new CCodeConstant (expr.value)); + + var cdecl = new CCodeDeclaration ("const string"); + cdecl.add_declarator (new CCodeVariableDeclarator ("_string%d_".printf (next_string_const_id), val)); + cdecl.modifiers = CCodeModifiers.STATIC; + source_declarations.add_constant_declaration (cdecl); + + expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeConstant ("_string%d_".printf (next_string_const_id))); + + next_string_const_id++; + } + + public override void visit_null_literal (NullLiteral expr) { + source_declarations.add_include ("stddef.h"); + expr.ccodenode = new CCodeConstant ("NULL"); + } + + public override void visit_base_access (BaseAccess expr) { + generate_type_declaration (expr.value_type, source_declarations); + expr.ccodenode = new CCodeCastExpression (new CCodeIdentifier ("this"), expr.value_type.get_cname ()); + } + + public override void visit_postfix_expression (PostfixExpression expr) { + MemberAccess ma = find_property_access (expr.inner); + if (ma != null) { + // property postfix expression + var prop = (Property) ma.symbol_reference; + + var ccomma = new CCodeCommaExpression (); + + // assign current value to temp variable + var temp_decl = get_temp_variable (prop.property_type, true, expr); + temp_vars.insert (0, temp_decl); + ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_decl.name), (CCodeExpression) expr.inner.ccodenode)); + + // increment/decrement property + var op = expr.increment ? CCodeBinaryOperator.PLUS : CCodeBinaryOperator.MINUS; + var cexpr = new CCodeBinaryExpression (op, get_variable_cexpression (temp_decl.name), new CCodeConstant ("1")); + var ccall = get_property_set_call (prop, ma, cexpr); + ccomma.append_expression (ccall); + + // return previous value + ccomma.append_expression (new CCodeIdentifier (temp_decl.name)); + + expr.ccodenode = ccomma; + return; + } + + var op = expr.increment ? CCodeUnaryOperator.POSTFIX_INCREMENT : CCodeUnaryOperator.POSTFIX_DECREMENT; + + expr.ccodenode = new CCodeUnaryExpression (op, (CCodeExpression) expr.inner.ccodenode); + } + + private MemberAccess? find_property_access (Expression expr) { + if (!(expr is MemberAccess)) { + return null; + } + + var ma = (MemberAccess) expr; + if (ma.symbol_reference is Property) { + return ma; + } + + return null; + } + + public bool requires_copy (DataType type) { + if (!type.is_disposable ()) { + return false; + } + + var cl = type.data_type as Class; + if (cl != null && cl.is_reference_counting () + && cl.get_ref_function () == "") { + // empty ref_function => no ref necessary + return false; + } + + if (type.type_parameter != null) { + return false; + } + + return true; + } + + public bool requires_destroy (DataType type) { + if (!type.is_disposable ()) { + return false; + } + + var array_type = type as ArrayType; + if (array_type != null && array_type.fixed_length) { + return requires_destroy (array_type.element_type); + } + + var cl = type.data_type as Class; + if (cl != null && cl.is_reference_counting () + && cl.get_unref_function () == "") { + // empty unref_function => no unref necessary + return false; + } + + if (type.type_parameter != null) { + return false; + } + + return true; + } + + bool is_ref_function_void (DataType type) { + var cl = type.data_type as Class; + if (cl != null && cl.ref_function_void) { + return true; + } else { + return false; + } + } + + public virtual CCodeExpression? get_ref_cexpression (DataType expression_type, CCodeExpression cexpr, Expression? expr, CodeNode node) { + if (expression_type is ValueType && !expression_type.nullable) { + // normal value type, no null check + // (copy (&temp, 0, &expr, 0), temp) + + var decl = get_temp_variable (expression_type, false, node); + temp_vars.insert (0, decl); + + var ctemp = get_variable_cexpression (decl.name); + + var vt = (ValueType) expression_type; + var st = (Struct) vt.type_symbol; + var copy_call = new CCodeFunctionCall (new CCodeIdentifier (st.get_copy_function ())); + copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, ctemp)); + copy_call.add_argument (new CCodeConstant ("0")); + copy_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr)); + copy_call.add_argument (new CCodeConstant ("0")); + + var ccomma = new CCodeCommaExpression (); + + ccomma.append_expression (copy_call); + ccomma.append_expression (ctemp); + + return ccomma; + } + + /* (temp = expr, temp == NULL ? NULL : ref (temp)) + * + * can be simplified to + * ref (expr) + * if static type of expr is non-null + */ + + var dupexpr = get_dup_func_expression (expression_type, node.source_reference); + + if (dupexpr == null) { + node.error = true; + return null; + } + + var ccall = new CCodeFunctionCall (dupexpr); + + if (expr != null && expr.is_non_null () + && !is_ref_function_void (expression_type)) { + // expression is non-null + ccall.add_argument ((CCodeExpression) expr.ccodenode); + + return ccall; + } else { + var decl = get_temp_variable (expression_type, false, node); + temp_vars.insert (0, decl); + + var ctemp = get_variable_cexpression (decl.name); + + source_declarations.add_include ("stddef.h"); + var cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, ctemp, new CCodeConstant ("NULL")); + if (expression_type.type_parameter != null) { + // dup functions are optional for type parameters + var cdupisnull = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, get_dup_func_expression (expression_type, node.source_reference), new CCodeConstant ("NULL")); + cisnull = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cisnull, cdupisnull); + } + + ccall.add_argument (ctemp); + + var ccomma = new CCodeCommaExpression (); + ccomma.append_expression (new CCodeAssignment (ctemp, cexpr)); + + var cifnull = new CCodeConstant ("NULL"); + ccomma.append_expression (new CCodeConditionalExpression (cisnull, cifnull, ccall)); + + // repeat temp variable at the end of the comma expression + // if the ref function returns void + if (is_ref_function_void (expression_type)) { + ccomma.append_expression (ctemp); + } + + return ccomma; + } + } + + public virtual void generate_class_declaration (Class cl, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (cl, cl.get_cname ())) { + return; + } + } + + public virtual void generate_interface_declaration (Interface iface, CCodeDeclarationSpace decl_space) { + } + + public virtual void generate_method_declaration (Method m, CCodeDeclarationSpace decl_space) { + } + + public void add_generic_type_arguments (CCodeFunctionCall ccall, List<DataType> type_args, CodeNode expr, bool is_chainup = false) { + foreach (var type_arg in type_args) { + if (type_arg is GenericType) { + var generic_type = (GenericType) type_arg; + string var_name = "%s_type".printf (generic_type.type_parameter.name.down ()); + if (is_in_generic_type (type_arg) && !is_chainup) { + ccall.add_argument (new CCodeMemberAccess.pointer (get_type_private_from_type ((ObjectTypeSymbol) generic_type.type_parameter.parent_symbol, new CCodeMemberAccess.pointer (new CCodeIdentifier ("this"), "type")), var_name)); + } else { + ccall.add_argument (new CCodeIdentifier (var_name)); + } + } else { + ccall.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (type_arg.data_type.get_lower_case_cname ())))); + } + } + } + + public override void visit_object_creation_expression (ObjectCreationExpression expr) { + expr.accept_children (codegen); + + CCodeExpression instance = null; + CCodeExpression creation_expr = null; + + var st = expr.type_reference.data_type as Struct; + + bool struct_by_ref = false; + if (st != null && !st.is_boolean_type () && !st.is_integer_type () && !st.is_floating_type ()) { + struct_by_ref = true; + } + + if (struct_by_ref || expr.get_object_initializer ().size > 0) { + // value-type initialization or object creation expression with object initializer + var temp_decl = get_temp_variable (expr.type_reference, false, expr); + temp_vars.add (temp_decl); + + instance = get_variable_cexpression (get_variable_cname (temp_decl.name)); + } + + if (expr.symbol_reference == null) { + // no creation method + if (expr.type_reference.data_type is Struct) { + // memset needs string.h + source_declarations.add_include ("string.h"); + var creation_call = new CCodeFunctionCall (new CCodeIdentifier ("memset")); + creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance)); + creation_call.add_argument (new CCodeConstant ("0")); + creation_call.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (expr.type_reference.get_cname ()))); + + creation_expr = creation_call; + } + } else if (expr.symbol_reference is Method) { + // use creation method + var m = (Method) expr.symbol_reference; + var params = m.get_parameters (); + CCodeFunctionCall creation_call; + + generate_method_declaration (m, source_declarations); + + var cl = expr.type_reference.data_type as Class; + + if (!m.has_new_function) { + // use construct function directly + creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ())); + creation_call.add_argument (new CCodeIdentifier (cl.get_type_id ())); + } else { + creation_call = new CCodeFunctionCall (new CCodeIdentifier (m.get_cname ())); + } + + if (struct_by_ref && !(m.cinstance_parameter_position < 0)) { + creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance)); + } + + generate_type_declaration (expr.type_reference, source_declarations); + + if (cl != null && !cl.is_compact) { + add_generic_type_arguments (creation_call, expr.type_reference.get_type_arguments (), expr); + } + + bool ellipsis = false; + + int i = 1; + Iterator<FormalParameter> params_it = params.iterator (); + foreach (Expression arg in expr.get_argument_list ()) { + CCodeExpression cexpr = (CCodeExpression) arg.ccodenode; + FormalParameter param = null; + if (params_it.next ()) { + param = params_it.get (); + ellipsis = param.ellipsis; + if (!ellipsis) { + cexpr = handle_struct_argument (param, arg, cexpr); + } + } + + creation_call.add_argument (cexpr); + + i++; + } + while (params_it.next ()) { + var param = params_it.get (); + + if (param.ellipsis) { + ellipsis = true; + break; + } + + if (param.default_expression == null) { + Report.error (expr.source_reference, "no default expression for argument %d".printf (i)); + return; + } + + /* evaluate default expression here as the code + * generator might not have visited the formal + * parameter yet */ + param.default_expression.accept (codegen); + + creation_call.add_argument ((CCodeExpression) param.default_expression.ccodenode); + i++; + } + + if (struct_by_ref && m.cinstance_parameter_position < 0) { + // instance parameter is at the end in a struct creation method + creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance)); + } + + if (expr.tree_can_fail) { + // method can fail + current_method_inner_error = true; + creation_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_inner_error_"))); + } + + if (ellipsis) { + /* ensure variable argument list ends with NULL + * except when using printf-style arguments */ + if (!m.printf_format && !m.scanf_format && m.sentinel != "") { + creation_call.add_argument (new CCodeConstant (m.sentinel)); + } + } + + creation_expr = creation_call; + + // cast the return value of the creation method back to the intended type if + // it requested a special C return type + if (head.get_custom_creturn_type (m) != null) { + creation_expr = new CCodeCastExpression (creation_expr, expr.type_reference.get_cname ()); + } + } else { + assert (false); + } + + if (instance != null) { + var ccomma = new CCodeCommaExpression (); + + if (expr.type_reference.data_type is Struct) { + ccomma.append_expression (creation_expr); + } else { + ccomma.append_expression (new CCodeAssignment (instance, creation_expr)); + } + + foreach (MemberInitializer init in expr.get_object_initializer ()) { + if (init.symbol_reference is Field) { + var f = (Field) init.symbol_reference; + var instance_target_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol); + var typed_inst = transform_expression (instance, expr.type_reference, instance_target_type); + CCodeExpression lhs; + if (expr.type_reference.data_type is Struct) { + lhs = new CCodeMemberAccess (typed_inst, f.get_cname ()); + } else { + lhs = new CCodeMemberAccess.pointer (typed_inst, f.get_cname ()); + } + ccomma.append_expression (new CCodeAssignment (lhs, (CCodeExpression) init.initializer.ccodenode)); + } else if (init.symbol_reference is Property) { + var inst_ma = new MemberAccess.simple ("new"); + inst_ma.value_type = expr.type_reference; + inst_ma.ccodenode = instance; + var ma = new MemberAccess (inst_ma, init.name); + ccomma.append_expression (get_property_set_call ((Property) init.symbol_reference, ma, (CCodeExpression) init.initializer.ccodenode)); + } + } + + ccomma.append_expression (instance); + + expr.ccodenode = ccomma; + } else if (creation_expr != null) { + expr.ccodenode = creation_expr; + } + } + + public CCodeExpression? handle_struct_argument (FormalParameter param, Expression arg, CCodeExpression? cexpr) { + if (arg.formal_target_type is GenericType && !(arg.target_type is GenericType)) { + // we already use a reference for arguments of ref and out parameters + if (param.direction == ParameterDirection.IN) { + var unary = cexpr as CCodeUnaryExpression; + if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) { + // *expr => expr + return unary.inner; + } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) { + return new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr); + } else { + // if cexpr is e.g. a function call, we can't take the address of the expression + // (tmp = expr, &tmp) + var ccomma = new CCodeCommaExpression (); + + var temp_var = get_temp_variable (arg.target_type); + temp_vars.insert (0, temp_var); + ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), cexpr)); + ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (temp_var.name))); + + return ccomma; + } + } + } + + return cexpr; + } + + public override void visit_sizeof_expression (SizeofExpression expr) { + var csizeof = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); + csizeof.add_argument (new CCodeIdentifier (expr.type_reference.get_cname ())); + expr.ccodenode = csizeof; + } + + public override void visit_typeof_expression (TypeofExpression expr) { + expr.ccodenode = get_type_id_expression (expr.type_reference); + } + + public override void visit_unary_expression (UnaryExpression expr) { + expr.accept_children (codegen); + + CCodeUnaryOperator op; + if (expr.operator == UnaryOperator.PLUS) { + op = CCodeUnaryOperator.PLUS; + } else if (expr.operator == UnaryOperator.MINUS) { + op = CCodeUnaryOperator.MINUS; + } else if (expr.operator == UnaryOperator.LOGICAL_NEGATION) { + op = CCodeUnaryOperator.LOGICAL_NEGATION; + } else if (expr.operator == UnaryOperator.BITWISE_COMPLEMENT) { + op = CCodeUnaryOperator.BITWISE_COMPLEMENT; + } else if (expr.operator == UnaryOperator.INCREMENT) { + op = CCodeUnaryOperator.PREFIX_INCREMENT; + } else if (expr.operator == UnaryOperator.DECREMENT) { + op = CCodeUnaryOperator.PREFIX_DECREMENT; + } else if (expr.operator == UnaryOperator.REF) { + op = CCodeUnaryOperator.ADDRESS_OF; + } else if (expr.operator == UnaryOperator.OUT) { + op = CCodeUnaryOperator.ADDRESS_OF; + } else { + assert_not_reached (); + } + expr.ccodenode = new CCodeUnaryExpression (op, (CCodeExpression) expr.inner.ccodenode); + } + + public override void visit_cast_expression (CastExpression expr) { + if (expr.is_silent_cast) { + expr.error = true; + Report.error (expr.source_reference, "Operation not supported for this type"); + return; + } + + generate_type_declaration (expr.type_reference, source_declarations); + + if (expr.inner.value_type is GenericType && !(expr.type_reference is GenericType)) { + // generic types use an extra pointer, dereference that pointer + expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeCastExpression ((CCodeExpression) expr.inner.ccodenode, expr.type_reference.get_cname () + "*")); + } else { + expr.ccodenode = new CCodeCastExpression ((CCodeExpression) expr.inner.ccodenode, expr.type_reference.get_cname ()); + } + } + + public override void visit_pointer_indirection (PointerIndirection expr) { + expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, (CCodeExpression) expr.inner.ccodenode); + } + + public override void visit_addressof_expression (AddressofExpression expr) { + expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, (CCodeExpression) expr.inner.ccodenode); + } + + public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) { + expr.accept_children (codegen); + + /* (tmp = var, var = null, tmp) */ + var ccomma = new CCodeCommaExpression (); + var temp_decl = get_temp_variable (expr.value_type, true, expr); + temp_vars.insert (0, temp_decl); + var cvar = get_variable_cexpression (temp_decl.name); + + ccomma.append_expression (new CCodeAssignment (cvar, (CCodeExpression) expr.inner.ccodenode)); + ccomma.append_expression (new CCodeAssignment ((CCodeExpression) expr.inner.ccodenode, new CCodeConstant ("NULL"))); + ccomma.append_expression (cvar); + expr.ccodenode = ccomma; + } + + public override void visit_binary_expression (BinaryExpression expr) { + expr.accept_children (codegen); + + var cleft = (CCodeExpression) expr.left.ccodenode; + var cright = (CCodeExpression) expr.right.ccodenode; + + CCodeBinaryOperator op; + if (expr.operator == BinaryOperator.PLUS) { + op = CCodeBinaryOperator.PLUS; + } else if (expr.operator == BinaryOperator.MINUS) { + op = CCodeBinaryOperator.MINUS; + } else if (expr.operator == BinaryOperator.MUL) { + op = CCodeBinaryOperator.MUL; + } else if (expr.operator == BinaryOperator.DIV) { + op = CCodeBinaryOperator.DIV; + } else if (expr.operator == BinaryOperator.MOD) { + op = CCodeBinaryOperator.MOD; + } else if (expr.operator == BinaryOperator.SHIFT_LEFT) { + op = CCodeBinaryOperator.SHIFT_LEFT; + } else if (expr.operator == BinaryOperator.SHIFT_RIGHT) { + op = CCodeBinaryOperator.SHIFT_RIGHT; + } else if (expr.operator == BinaryOperator.LESS_THAN) { + op = CCodeBinaryOperator.LESS_THAN; + } else if (expr.operator == BinaryOperator.GREATER_THAN) { + op = CCodeBinaryOperator.GREATER_THAN; + } else if (expr.operator == BinaryOperator.LESS_THAN_OR_EQUAL) { + op = CCodeBinaryOperator.LESS_THAN_OR_EQUAL; + } else if (expr.operator == BinaryOperator.GREATER_THAN_OR_EQUAL) { + op = CCodeBinaryOperator.GREATER_THAN_OR_EQUAL; + } else if (expr.operator == BinaryOperator.EQUALITY) { + op = CCodeBinaryOperator.EQUALITY; + } else if (expr.operator == BinaryOperator.INEQUALITY) { + op = CCodeBinaryOperator.INEQUALITY; + } else if (expr.operator == BinaryOperator.BITWISE_AND) { + op = CCodeBinaryOperator.BITWISE_AND; + } else if (expr.operator == BinaryOperator.BITWISE_OR) { + op = CCodeBinaryOperator.BITWISE_OR; + } else if (expr.operator == BinaryOperator.BITWISE_XOR) { + op = CCodeBinaryOperator.BITWISE_XOR; + } else if (expr.operator == BinaryOperator.AND) { + op = CCodeBinaryOperator.AND; + } else if (expr.operator == BinaryOperator.OR) { + op = CCodeBinaryOperator.OR; + } else if (expr.operator == BinaryOperator.IN) { + expr.ccodenode = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeBinaryExpression (CCodeBinaryOperator.BITWISE_AND, cright, cleft), cleft); + return; + } else { + assert_not_reached (); + } + + if (expr.operator == BinaryOperator.EQUALITY || + expr.operator == BinaryOperator.INEQUALITY) { + var left_type_as_struct = expr.left.value_type.data_type as Struct; + var right_type_as_struct = expr.right.value_type.data_type as Struct; + + if (expr.left.value_type.data_type is Class && !((Class) expr.left.value_type.data_type).is_compact && + expr.right.value_type.data_type is Class && !((Class) expr.right.value_type.data_type).is_compact) { + var left_cl = (Class) expr.left.value_type.data_type; + var right_cl = (Class) expr.right.value_type.data_type; + + if (left_cl != right_cl) { + if (left_cl.is_subtype_of (right_cl)) { + cleft = generate_instance_cast (cleft, right_cl); + } else if (right_cl.is_subtype_of (left_cl)) { + cright = generate_instance_cast (cright, left_cl); + } + } + } else if (left_type_as_struct != null && right_type_as_struct != null) { + // FIXME generate and use compare/equal function for real structs + if (expr.left.value_type.nullable && expr.right.value_type.nullable) { + // FIXME also compare contents, not just address + } else if (expr.left.value_type.nullable) { + // FIXME check left value is not null + cleft = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cleft); + } else if (expr.right.value_type.nullable) { + // FIXME check right value is not null + cright = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, cright); + } + } + } + + expr.ccodenode = new CCodeBinaryExpression (op, cleft, cright); + } + + public string? get_type_check_function (TypeSymbol type) { + var cl = type as Class; + if (cl != null && cl.type_check_function != null) { + return cl.type_check_function; + } else if ((cl != null && cl.is_compact) || type is Struct || type is Enum || type is Delegate) { + return null; + } else { + return type.get_upper_case_cname ("IS_"); + } + } + + CCodeExpression? create_type_check (CCodeNode ccodenode, DataType type) { + string type_check_func = get_type_check_function (type.data_type); + if (type_check_func == null) { + return new CCodeInvalidExpression (); + } + var ccheck = new CCodeFunctionCall (new CCodeIdentifier (type_check_func)); + ccheck.add_argument ((CCodeExpression) ccodenode); + return ccheck; + } + + public override void visit_type_check (TypeCheck expr) { + generate_type_declaration (expr.type_reference, source_declarations); + + expr.ccodenode = create_type_check (expr.expression.ccodenode, expr.type_reference); + if (expr.ccodenode is CCodeInvalidExpression) { + Report.error (expr.source_reference, "type check expressions not supported for compact classes, structs, and enums"); + } + } + + public override void visit_lambda_expression (LambdaExpression l) { + // use instance position from delegate + var dt = (DelegateType) l.target_type; + l.method.cinstance_parameter_position = dt.delegate_symbol.cinstance_parameter_position; + + var old_temp_vars = temp_vars; + var old_temp_ref_vars = temp_ref_vars; + temp_vars = new ArrayList<LocalVariable> (); + temp_ref_vars = new ArrayList<LocalVariable> (); + + l.accept_children (codegen); + + temp_vars = old_temp_vars; + temp_ref_vars = old_temp_ref_vars; + + l.ccodenode = new CCodeIdentifier (l.method.get_cname ()); + } + + // manage memory and implicit casts + public CCodeExpression transform_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) { + var cexpr = source_cexpr; + if (expression_type == null) { + return cexpr; + } + + + if (expression_type.value_owned + && (target_type == null || !target_type.value_owned)) { + // value leaked, destroy it + var pointer_type = target_type as PointerType; + if (pointer_type != null && !(pointer_type.base_type is VoidType)) { + // manual memory management for non-void pointers + // treat void* special to not leak memory with void* method parameters + } else if (requires_destroy (expression_type)) { + var decl = get_temp_variable (expression_type, true, expression_type); + temp_vars.insert (0, decl); + temp_ref_vars.insert (0, decl); + cexpr = new CCodeAssignment (get_variable_cexpression (decl.name), cexpr); + } + } + + if (target_type == null) { + // value will be destroyed, no need for implicit casts + return cexpr; + } + + cexpr = get_implicit_cast_expression (cexpr, expression_type, target_type, expr); + + if (target_type.value_owned && !expression_type.value_owned) { + // need to copy value + if (requires_copy (target_type) && !(expression_type is NullType)) { + CodeNode node = expr; + if (node == null) { + node = expression_type; + } + cexpr = get_ref_cexpression (target_type, cexpr, expr, node); + } + } + + return cexpr; + } + + public virtual CCodeExpression get_implicit_cast_expression (CCodeExpression source_cexpr, DataType? expression_type, DataType? target_type, Expression? expr = null) { + var cexpr = source_cexpr; + + if (expression_type.data_type != null && expression_type.data_type == target_type.data_type) { + // same type, no cast required + return cexpr; + } + + if (expression_type is NullType) { + // null literal, no cast required when not converting to generic type pointer + return cexpr; + } + + generate_type_declaration (target_type, source_declarations); + + if (target_type is DelegateType && expression_type is MethodType) { + var deleg_type = (DelegateType) target_type; + var method_type = (MethodType) expression_type; + CCodeExpression delegate_target = new CCodeConstant ("NULL"); + if (method_type.method_symbol.binding == MemberBinding.INSTANCE) { + var ma = (MemberAccess) expr; + delegate_target = (CCodeExpression) get_ccodenode (ma.inner); + } + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_new".printf (deleg_type.delegate_symbol.get_lower_case_cname ()))); + ccall.add_argument (delegate_target); + ccall.add_argument (source_cexpr); + return ccall; + } + + var cl = target_type.data_type as Class; + var iface = target_type.data_type as Interface; + if (context.checking && (iface != null || (cl != null && !cl.is_compact))) { + // checked cast for strict subtypes of GTypeInstance + return generate_instance_cast (cexpr, target_type.data_type); + } else if (target_type.data_type != null && expression_type.get_cname () != target_type.get_cname ()) { + var st = target_type.data_type as Struct; + if (target_type.data_type.is_reference_type () || (st != null && st.is_simple_type ())) { + // don't cast non-simple structs + return new CCodeCastExpression (cexpr, target_type.get_cname ()); + } else { + return cexpr; + } + } else { + return cexpr; + } + } + + public CCodeFunctionCall get_property_set_call (Property prop, MemberAccess ma, CCodeExpression cexpr, Expression? rhs = null) { + string set_func; + + var base_property = prop; + if (prop.base_property != null) { + base_property = prop.base_property; + } else if (prop.base_interface_property != null) { + base_property = prop.base_interface_property; + } + + if (prop is DynamicProperty) { + set_func = head.get_dynamic_property_setter_cname ((DynamicProperty) prop); + } else { + generate_property_accessor_declaration (base_property.set_accessor, source_declarations); + set_func = base_property.set_accessor.get_cname (); + } + + var ccall = new CCodeFunctionCall (new CCodeIdentifier (set_func)); + + if (prop.binding == MemberBinding.INSTANCE) { + /* target instance is first argument */ + ccall.add_argument ((CCodeExpression) get_ccodenode (ma.inner)); + } + + ccall.add_argument (cexpr); + + return ccall; + } + + public bool add_generated_external_symbol (Symbol external_symbol) { + return generated_external_symbols.add (external_symbol); + } + + public static DataType get_data_type_for_symbol (TypeSymbol sym) { + DataType type = null; + + if (sym is Class) { + type = new ObjectType ((Class) sym); + } else if (sym is Interface) { + type = new ObjectType ((Interface) sym); + } else if (sym is Struct) { + var st = (Struct) sym; + if (st.is_boolean_type ()) { + type = new BooleanType (st); + } else if (st.is_integer_type ()) { + type = new IntegerType (st); + } else if (st.is_floating_type ()) { + type = new FloatingType (st); + } else { + type = new StructValueType (st); + } + } else if (sym is Enum) { + type = new EnumValueType ((Enum) sym); + } else if (sym is ErrorDomain) { + type = new ErrorType ((ErrorDomain) sym, null); + } else if (sym is ErrorCode) { + type = new ErrorType ((ErrorDomain) sym.parent_symbol, (ErrorCode) sym); + } else { + Report.error (null, "internal error: `%s' is not a supported type".printf (sym.get_full_name ())); + return new InvalidType (); + } + + return type; + } + + public CCodeExpression? default_value_for_type (DataType type, bool initializer_expression) { + source_declarations.add_include ("stddef.h"); + + var st = type.data_type as Struct; + var array_type = type as ArrayType; + if (type is GenericType) { + var value_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_value_size")); + value_size.add_argument (get_type_id_expression (type)); + + var alloca_call = new CCodeFunctionCall (new CCodeIdentifier ("alloca")); + alloca_call.add_argument (value_size); + + // memset needs string.h + source_declarations.add_include ("string.h"); + var memset_call = new CCodeFunctionCall (new CCodeIdentifier ("memset")); + memset_call.add_argument (alloca_call); + memset_call.add_argument (new CCodeConstant ("0")); + memset_call.add_argument (value_size); + + return memset_call; + } else if (initializer_expression && !type.nullable && + ((st != null && !st.is_simple_type ()) || + (array_type != null && array_type.fixed_length))) { + // 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.data_type != null && type.data_type.is_reference_type ()) + || type.nullable + || type is PointerType || type is DelegateType + || (array_type != null && !array_type.fixed_length)) { + 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 ()); + } + return null; + } + + public CCodeNode? get_ccodenode (CodeNode node) { + if (node.ccodenode == null) { + node.accept (codegen); + } + return node.ccodenode; + } + + public override void visit_class (Class cl) { + } + + public CCodeFunctionCall generate_instance_cast (CCodeExpression expr, TypeSymbol type) { + var result = new CCodeFunctionCall (new CCodeIdentifier (type.get_upper_case_cname (null))); + result.add_argument (expr); + return result; + } +} diff --git a/codegen/valadovacontrolflowmodule.vala b/codegen/valadovacontrolflowmodule.vala new file mode 100644 index 000000000..6616dcca7 --- /dev/null +++ b/codegen/valadovacontrolflowmodule.vala @@ -0,0 +1,98 @@ +/* valadovacontrolflowmodule.vala + * + * Copyright (C) 2006-2009 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 <j@bitron.ch> + */ + +internal class Vala.DovaControlFlowModule : DovaMethodModule { + public DovaControlFlowModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override void visit_if_statement (IfStatement stmt) { + stmt.accept_children (codegen); + + if (stmt.false_statement != null) { + stmt.ccodenode = new CCodeIfStatement ((CCodeExpression) stmt.condition.ccodenode, (CCodeStatement) stmt.true_statement.ccodenode, (CCodeStatement) stmt.false_statement.ccodenode); + } else { + stmt.ccodenode = new CCodeIfStatement ((CCodeExpression) stmt.condition.ccodenode, (CCodeStatement) stmt.true_statement.ccodenode); + } + + create_temp_decl (stmt, stmt.condition.temp_vars); + } + + public override void visit_switch_statement (SwitchStatement stmt) { + stmt.accept_children (codegen); + + var cswitch = new CCodeSwitchStatement ((CCodeExpression) stmt.expression.ccodenode); + stmt.ccodenode = cswitch; + + foreach (SwitchSection section in stmt.get_sections ()) { + if (section.has_default_label ()) { + cswitch.add_statement (new CCodeLabel ("default")); + var cdefaultblock = new CCodeBlock (); + cswitch.add_statement (cdefaultblock); + foreach (CodeNode default_stmt in section.get_statements ()) { + cdefaultblock.add_statement (default_stmt.ccodenode); + } + continue; + } + + foreach (SwitchLabel label in section.get_labels ()) { + cswitch.add_statement (new CCodeCaseStatement ((CCodeExpression) label.expression.ccodenode)); + } + + var cblock = new CCodeBlock (); + cswitch.add_statement (cblock); + foreach (CodeNode body_stmt in section.get_statements ()) { + cblock.add_statement (body_stmt.ccodenode); + } + } + + create_temp_decl (stmt, stmt.expression.temp_vars); + } + + public override void visit_switch_section (SwitchSection section) { + visit_block (section); + } + + public override void visit_switch_label (SwitchLabel label) { + label.accept_children (codegen); + } + + public override void visit_loop (Loop stmt) { + stmt.accept_children (codegen); + + source_declarations.add_include ("stdbool.h"); + stmt.ccodenode = new CCodeWhileStatement (new CCodeConstant ("true"), (CCodeStatement) stmt.body.ccodenode); + } + + public override void visit_break_statement (BreakStatement stmt) { + stmt.ccodenode = new CCodeBreakStatement (); + + create_local_free (stmt, true); + } + + public override void visit_continue_statement (ContinueStatement stmt) { + stmt.ccodenode = new CCodeContinueStatement (); + + create_local_free (stmt, true); + } +} + diff --git a/codegen/valadovadelegatemodule.vala b/codegen/valadovadelegatemodule.vala new file mode 100644 index 000000000..c82f4c553 --- /dev/null +++ b/codegen/valadovadelegatemodule.vala @@ -0,0 +1,210 @@ +/* valadovadelegatemodule.vala + * + * Copyright (C) 2006-2009 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 <j@bitron.ch> + * Raffaele Sandrini <raffaele@sandrini.ch> + */ + +/** + * The link between a delegate and generated code. + */ +internal class Vala.DovaDelegateModule : DovaValueModule { + public DovaDelegateModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override void generate_delegate_declaration (Delegate d, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (d, d.get_cname ())) { + return; + } + + decl_space.add_type_declaration (new CCodeTypeDefinition ("struct _%s".printf (d.get_cname ()), new CCodeVariableDeclarator (d.get_cname ()))); + + generate_class_declaration (type_class, decl_space); + generate_method_declaration ((Method) object_class.scope.lookup ("ref"), decl_space); + generate_method_declaration ((Method) object_class.scope.lookup ("unref"), decl_space); + + var type_fun = new CCodeFunction ("%s_type_get".printf (d.get_lower_case_cname ()), "DovaType *"); + decl_space.add_type_member_declaration (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (d.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + decl_space.add_type_member_declaration (type_init_fun); + + generate_type_declaration (d.return_type, decl_space); + + var function = generate_new_function (d, decl_space); + function.block = null; + decl_space.add_type_member_declaration (function); + + function = generate_invoke_function (d, decl_space); + function.block = null; + decl_space.add_type_member_declaration (function); + } + + CCodeFunction generate_new_function (Delegate d, CCodeDeclarationSpace decl_space) { + var function = new CCodeFunction ("%s_new".printf (d.get_lower_case_cname ()), "%s*".printf (d.get_cname ())); + if (d.is_private_symbol ()) { + function.modifiers |= CCodeModifiers.STATIC; + } + + function.add_parameter (new CCodeFormalParameter ("target", "DovaObject *")); + function.add_parameter (new CCodeFormalParameter ("(*method) (void)", "void")); + + function.block = new CCodeBlock (); + + var alloc_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_alloc")); + alloc_call.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (d.get_lower_case_cname ())))); + + var cdecl = new CCodeDeclaration ("%s*".printf (d.get_cname ())); + cdecl.add_declarator (new CCodeVariableDeclarator ("this", alloc_call)); + function.block.add_statement (cdecl); + + var init_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_delegate_init")); + init_call.add_argument (new CCodeIdentifier ("this")); + init_call.add_argument (new CCodeIdentifier ("target")); + function.block.add_statement (new CCodeExpressionStatement (init_call)); + + var priv = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_PRIVATE".printf (d.get_upper_case_cname ()))); + priv.add_argument (new CCodeIdentifier ("this")); + var assignment = new CCodeAssignment (new CCodeMemberAccess.pointer (priv, "method"), new CCodeIdentifier ("method")); + function.block.add_statement (new CCodeExpressionStatement (assignment)); + + function.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("this"))); + + return function; + } + + CCodeFunction generate_invoke_function (Delegate d, CCodeDeclarationSpace decl_space) { + var function = new CCodeFunction ("%s_invoke".printf (d.get_lower_case_cname ()), d.return_type.get_cname ()); + + if (d.is_private_symbol ()) { + function.modifiers |= CCodeModifiers.STATIC; + } + + function.add_parameter (new CCodeFormalParameter ("this", "%s*".printf (d.get_cname ()))); + + string param_list = ""; + + foreach (FormalParameter param in d.get_parameters ()) { + generate_type_declaration (param.parameter_type, decl_space); + + function.add_parameter (new CCodeFormalParameter (param.name, param.parameter_type.get_cname ())); + + if (param_list != "") { + param_list += ", "; + } + param_list += param.parameter_type.get_cname (); + } + + function.block = new CCodeBlock (); + + var get_target = new CCodeFunctionCall (new CCodeIdentifier ("dova_delegate_get_target")); + get_target.add_argument (new CCodeIdentifier ("this")); + + var cdecl = new CCodeDeclaration ("DovaObject*"); + cdecl.add_declarator (new CCodeVariableDeclarator ("target", get_target)); + function.block.add_statement (cdecl); + + var priv = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_PRIVATE".printf (d.get_upper_case_cname ()))); + priv.add_argument (new CCodeIdentifier ("this")); + + string instance_param_list = "(DovaObject *"; + if (param_list != "") { + instance_param_list += ","; + instance_param_list += param_list; + } + instance_param_list += ")"; + + var instance_block = new CCodeBlock (); + var instance_call = new CCodeFunctionCall (new CCodeCastExpression (new CCodeMemberAccess.pointer (priv, "method"), "%s (*) %s".printf (d.return_type.get_cname (), instance_param_list))); + + instance_call.add_argument (new CCodeIdentifier ("target")); + + string static_param_list = "("; + if (param_list != "") { + static_param_list += param_list; + } else { + static_param_list += "void"; + } + static_param_list += ")"; + + var static_block = new CCodeBlock (); + var static_call = new CCodeFunctionCall (new CCodeCastExpression (new CCodeMemberAccess.pointer (priv, "method"), "%s (*) %s".printf (d.return_type.get_cname (), static_param_list))); + + foreach (FormalParameter param in d.get_parameters ()) { + instance_call.add_argument (new CCodeIdentifier (param.name)); + static_call.add_argument (new CCodeIdentifier (param.name)); + } + + if (d.return_type is VoidType) { + instance_block.add_statement (new CCodeExpressionStatement (instance_call)); + static_block.add_statement (new CCodeExpressionStatement (static_call)); + } else { + instance_block.add_statement (new CCodeReturnStatement (instance_call)); + static_block.add_statement (new CCodeReturnStatement (static_call)); + } + + function.block.add_statement (new CCodeIfStatement (new CCodeIdentifier ("target"), instance_block, static_block)); + + return function; + } + + public override void visit_delegate (Delegate d) { + d.accept_children (codegen); + + generate_delegate_declaration (d, source_declarations); + + if (!d.is_internal_symbol ()) { + generate_delegate_declaration (d, header_declarations); + } + if (!d.is_private_symbol ()) { + generate_delegate_declaration (d, internal_header_declarations); + } + + generate_type_get_function (d, delegate_class); + + var instance_priv_struct = new CCodeStruct ("_%sPrivate".printf (d.get_cname ())); + var type_priv_struct = new CCodeStruct ("_%sTypePrivate".printf (d.get_cname ())); + + instance_priv_struct.add_field ("void", "(*method) (void)"); + + source_declarations.add_type_declaration (new CCodeTypeDefinition ("struct %s".printf (instance_priv_struct.name), new CCodeVariableDeclarator ("%sPrivate".printf (d.get_cname ())))); + source_declarations.add_type_definition (instance_priv_struct); + + source_declarations.add_type_declaration (new CCodeTypeDefinition ("struct %s".printf (type_priv_struct.name), new CCodeVariableDeclarator ("%sTypePrivate".printf (d.get_cname ())))); + source_declarations.add_type_definition (type_priv_struct); + + string macro = "((%sPrivate *) (((char *) o) + _%s_object_offset))".printf (d.get_cname (), d.get_lower_case_cname ()); + source_declarations.add_type_member_declaration (new CCodeMacroReplacement ("%s_GET_PRIVATE(o)".printf (d.get_upper_case_cname (null)), macro)); + + var cdecl = new CCodeDeclaration ("int"); + cdecl.add_declarator (new CCodeVariableDeclarator ("_%s_object_offset".printf (d.get_lower_case_cname ()), new CCodeConstant ("0"))); + cdecl.modifiers = CCodeModifiers.STATIC; + source_declarations.add_type_member_declaration (cdecl); + + cdecl = new CCodeDeclaration ("int"); + cdecl.add_declarator (new CCodeVariableDeclarator ("_%s_type_offset".printf (d.get_lower_case_cname ()), new CCodeConstant ("0"))); + cdecl.modifiers = CCodeModifiers.STATIC; + source_declarations.add_type_member_declaration (cdecl); + + source_type_member_definition.append (generate_new_function (d, source_declarations)); + source_type_member_definition.append (generate_invoke_function (d, source_declarations)); + } +} diff --git a/codegen/valadovamemberaccessmodule.vala b/codegen/valadovamemberaccessmodule.vala new file mode 100644 index 000000000..0be4ff293 --- /dev/null +++ b/codegen/valadovamemberaccessmodule.vala @@ -0,0 +1,255 @@ +/* valadovamemberaccessmodule.vala + * + * Copyright (C) 2006-2009 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 <j@bitron.ch> + */ + +using GLib; + +internal class Vala.DovaMemberAccessModule : DovaControlFlowModule { + public DovaMemberAccessModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override void visit_member_access (MemberAccess expr) { + expr.accept_children (codegen); + + CCodeExpression pub_inst = null; + DataType base_type = null; + + if (expr.inner != null) { + pub_inst = (CCodeExpression) expr.inner.ccodenode; + + if (expr.inner.value_type != null) { + base_type = expr.inner.value_type; + } + } + + if (expr.symbol_reference is Method) { + var m = (Method) expr.symbol_reference; + + if (!(m is DynamicMethod)) { + generate_method_declaration (m, source_declarations); + + if (!m.external && m.external_package) { + // internal VAPI methods + // only add them once per source file + if (add_generated_external_symbol (m)) { + visit_method (m); + } + } + } + + if (expr.inner is BaseAccess) { + if (m.base_method != null) { + var base_class = (Class) m.base_method.parent_symbol; + + expr.ccodenode = new CCodeIdentifier ("%s_base_%s".printf (base_class.get_lower_case_cname (null), m.name)); + return; + } else if (m.base_interface_method != null) { + var base_iface = (Interface) m.base_interface_method.parent_symbol; + + expr.ccodenode = new CCodeIdentifier ("%s_base_%s".printf (base_iface.get_lower_case_cname (null), m.name)); + return; + } + } + + if (m.base_method != null) { + if (!head.method_has_wrapper (m.base_method)) { + var inst = pub_inst; + if (expr.inner != null && !expr.inner.is_pure ()) { + // instance expression has side-effects + // store in temp. variable + var temp_var = get_temp_variable (expr.inner.value_type); + temp_vars.insert (0, temp_var); + var ctemp = new CCodeIdentifier (temp_var.name); + inst = new CCodeAssignment (ctemp, pub_inst); + expr.inner.ccodenode = ctemp; + } + var base_class = (Class) m.base_method.parent_symbol; + var vclass = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (base_class.get_upper_case_cname (null)))); + vclass.add_argument (inst); + expr.ccodenode = new CCodeMemberAccess.pointer (vclass, m.name); + } else { + expr.ccodenode = new CCodeIdentifier (m.base_method.get_cname ()); + } + } else if (m.base_interface_method != null) { + expr.ccodenode = new CCodeIdentifier (m.base_interface_method.get_cname ()); + } else if (m is CreationMethod) { + expr.ccodenode = new CCodeIdentifier (m.get_real_cname ()); + } else { + expr.ccodenode = new CCodeIdentifier (m.get_cname ()); + } + } else if (expr.symbol_reference is ArrayLengthField) { + generate_property_accessor_declaration (((Property) array_class.scope.lookup ("length")).get_accessor, source_declarations); + + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_length")); + ccall.add_argument (pub_inst); + expr.ccodenode = ccall; + } else if (expr.symbol_reference is Field) { + var f = (Field) expr.symbol_reference; + if (f.binding == MemberBinding.INSTANCE) { + var instance_target_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol); + + var cl = instance_target_type.data_type as Class; + bool dova_priv = false; + if (f.access == SymbolAccessibility.PRIVATE && + (cl.base_class == null || cl.base_class.get_full_name () != "Dova.Value")) { + dova_priv = true; + } + + CCodeExpression inst; + if (dova_priv) { + var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_PRIVATE".printf (cl.get_upper_case_cname (null)))); + priv_call.add_argument (pub_inst); + inst = priv_call; + } else { + inst = pub_inst; + } + if (instance_target_type.data_type.is_reference_type () || (expr.inner != null && expr.inner.value_type is PointerType)) { + expr.ccodenode = new CCodeMemberAccess.pointer (inst, f.get_cname ()); + } else { + expr.ccodenode = new CCodeMemberAccess (inst, f.get_cname ()); + } + } else { + generate_field_declaration (f, source_declarations); + + expr.ccodenode = new CCodeIdentifier (f.get_cname ()); + } + } else if (expr.symbol_reference is Constant) { + var c = (Constant) expr.symbol_reference; + + generate_constant_declaration (c, source_declarations); + + expr.ccodenode = new CCodeIdentifier (c.get_cname ()); + } else if (expr.symbol_reference is Property) { + var prop = (Property) expr.symbol_reference; + + if (!(prop is DynamicProperty)) { + generate_property_accessor_declaration (prop.get_accessor, source_declarations); + + if (!prop.external && prop.external_package) { + // internal VAPI properties + // only add them once per source file + if (add_generated_external_symbol (prop)) { + visit_property (prop); + } + } + } + + if (expr.inner is BaseAccess) { + if (prop.base_property != null) { + var base_class = (Class) prop.base_property.parent_symbol; + var vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_CLASS".printf (base_class.get_upper_case_cname (null)))); + vcast.add_argument (new CCodeIdentifier ("%s_parent_class".printf (current_class.get_lower_case_cname (null)))); + + var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name))); + ccall.add_argument ((CCodeExpression) expr.inner.ccodenode); + expr.ccodenode = ccall; + return; + } else if (prop.base_interface_property != null) { + var base_iface = (Interface) prop.base_interface_property.parent_symbol; + string parent_iface_var = "%s_%s_parent_iface".printf (current_class.get_lower_case_cname (null), base_iface.get_lower_case_cname (null)); + + var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (new CCodeIdentifier (parent_iface_var), "get_%s".printf (prop.name))); + ccall.add_argument ((CCodeExpression) expr.inner.ccodenode); + expr.ccodenode = ccall; + return; + } + } + + var base_property = prop; + if (prop.base_property != null) { + base_property = prop.base_property; + } else if (prop.base_interface_property != null) { + base_property = prop.base_interface_property; + } + string getter_cname; + if (prop is DynamicProperty) { + getter_cname = head.get_dynamic_property_getter_cname ((DynamicProperty) prop); + } else { + getter_cname = base_property.get_accessor.get_cname (); + } + var ccall = new CCodeFunctionCall (new CCodeIdentifier (getter_cname)); + + if (prop.binding == MemberBinding.INSTANCE) { + ccall.add_argument (pub_inst); + } + + expr.ccodenode = ccall; + } else if (expr.symbol_reference is EnumValue) { + var ev = (EnumValue) expr.symbol_reference; + + generate_enum_declaration ((Enum) ev.parent_symbol, source_declarations); + + expr.ccodenode = new CCodeConstant (ev.get_cname ()); + } else if (expr.symbol_reference is LocalVariable) { + var local = (LocalVariable) expr.symbol_reference; + if (local.is_result) { + // used in postconditions + expr.ccodenode = new CCodeIdentifier ("result"); + } else { + expr.ccodenode = get_variable_cexpression (local.name); + } + } else if (expr.symbol_reference is FormalParameter) { + var p = (FormalParameter) expr.symbol_reference; + if (p.name == "this") { + if (current_method != null && current_method.coroutine) { + // use closure + expr.ccodenode = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "this"); + } else { + var st = current_type_symbol as Struct; + if (st != null && !st.is_boolean_type () && !st.is_integer_type () && !st.is_floating_type () && (!st.is_simple_type () || current_method is CreationMethod)) { + expr.ccodenode = new CCodeIdentifier ("(*this)"); + } else { + expr.ccodenode = new CCodeIdentifier ("this"); + } + } + } else { + if (current_method != null && current_method.coroutine) { + // use closure + expr.ccodenode = get_variable_cexpression (p.name); + } else { + var type_as_struct = p.parameter_type.data_type as Struct; + if (p.direction != ParameterDirection.IN + || (type_as_struct != null && !type_as_struct.is_simple_type () && !p.parameter_type.nullable)) { + if (p.parameter_type is GenericType) { + expr.ccodenode = get_variable_cexpression (p.name); + } else { + expr.ccodenode = new CCodeIdentifier ("(*%s)".printf (get_variable_cname (p.name))); + } + } else { + // Property setters of non simple structs shall replace all occurences + // of the "value" formal parameter with a dereferencing version of that + // parameter. + if (current_property_accessor != null && + current_property_accessor.writable && + current_property_accessor.value_parameter == p && + current_property_accessor.prop.property_type.is_real_struct_type ()) { + expr.ccodenode = new CCodeIdentifier ("(*value)"); + } else { + expr.ccodenode = get_variable_cexpression (p.name); + } + } + } + } + } + } +} + diff --git a/codegen/valadovamethodcallmodule.vala b/codegen/valadovamethodcallmodule.vala new file mode 100644 index 000000000..aca957071 --- /dev/null +++ b/codegen/valadovamethodcallmodule.vala @@ -0,0 +1,230 @@ +/* valadovamethodcallmodule.vala + * + * Copyright (C) 2006-2009 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 <j@bitron.ch> + */ + +internal class Vala.DovaMethodCallModule : DovaAssignmentModule { + public DovaMethodCallModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override void visit_method_call (MethodCall expr) { + expr.accept_children (codegen); + + // the bare function call + var ccall = new CCodeFunctionCall ((CCodeExpression) expr.call.ccodenode); + + Method m = null; + Delegate deleg = null; + List<FormalParameter> params; + + var ma = expr.call as MemberAccess; + + var itype = expr.call.value_type; + params = itype.get_parameters (); + + if (itype is MethodType) { + assert (ma != null); + m = ((MethodType) itype).method_symbol; + } else if (itype is ObjectType) { + // constructor + var cl = (Class) ((ObjectType) itype).type_symbol; + m = cl.default_construction_method; + generate_method_declaration (m, source_declarations); + ccall = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ())); + } else if (itype is DelegateType) { + deleg = ((DelegateType) itype).delegate_symbol; + ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_invoke".printf (deleg.get_lower_case_cname ()))); + ccall.add_argument ((CCodeExpression) expr.call.ccodenode); + } + + if (m is CreationMethod) { + var cl = (Class) m.parent_symbol; + + if (cl == current_class) { + ccall.add_argument (new CCodeIdentifier ("this")); + } else { + ccall.add_argument (new CCodeCastExpression (new CCodeIdentifier ("this"), cl.get_cname () + "*")); + } + } else if (m != null) { + if (m.binding == MemberBinding.INSTANCE) { + var instance = (CCodeExpression) ma.inner.ccodenode; + + if (ma.member_name == "begin" && ma.inner.symbol_reference == ma.symbol_reference) { + var inner_ma = (MemberAccess) ma.inner; + instance = (CCodeExpression) inner_ma.inner.ccodenode; + } + + var st = m.parent_symbol as Struct; + if (st != null && !st.is_simple_type ()) { + // we need to pass struct instance by reference + var unary = instance as CCodeUnaryExpression; + if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) { + // *expr => expr + instance = unary.inner; + } else if (instance is CCodeIdentifier || instance is CCodeMemberAccess) { + instance = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, instance); + } else { + // if instance is e.g. a function call, we can't take the address of the expression + // (tmp = expr, &tmp) + var ccomma = new CCodeCommaExpression (); + + var temp_var = get_temp_variable (ma.inner.target_type); + temp_vars.insert (0, temp_var); + ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), instance)); + ccomma.append_expression (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name))); + + instance = ccomma; + } + } + + if (ma.inner is BaseAccess) { + ccall.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (((Class) current_class.base_class).get_lower_case_cname ())))); + } + ccall.add_argument (instance); + } + + if (m.binding != MemberBinding.INSTANCE && m.parent_symbol is ObjectTypeSymbol) { + // support static methods in generic types + var type_symbol = (ObjectTypeSymbol) m.parent_symbol; + if (type_symbol.get_type_parameters ().size > 0 && ma.inner is MemberAccess) { + var type_ma = (MemberAccess) ma.inner; + add_generic_type_arguments (ccall, type_ma.get_type_arguments (), expr); + } + } + if (m.get_type_parameters ().size > 0) { + add_generic_type_arguments (ccall, ma.get_type_arguments (), expr); + } + } + + // the complete call expression, might include casts, comma expressions, and/or assignments + CCodeExpression ccall_expr = ccall; + + bool ellipsis = false; + + int i = 1; + Iterator<FormalParameter> params_it = params.iterator (); + foreach (Expression arg in expr.get_argument_list ()) { + CCodeExpression cexpr = (CCodeExpression) arg.ccodenode; + + if (params_it.next ()) { + var param = params_it.get (); + ellipsis = param.params_array || param.ellipsis; + if (!ellipsis) { + cexpr = handle_struct_argument (param, arg, cexpr); + + // unref old value for non-null non-weak ref/out arguments + // disabled for arrays for now as that requires special handling + // (ret_tmp = call (&tmp), var1 = (assign_tmp = dup (tmp), free (var1), assign_tmp), ret_tmp) + if (param.direction != ParameterDirection.IN && requires_destroy (arg.value_type) + && (param.direction == ParameterDirection.OUT || !param.parameter_type.value_owned) + && !(param.parameter_type is ArrayType)) { + var unary = (UnaryExpression) arg; + + var ccomma = new CCodeCommaExpression (); + + var temp_var = get_temp_variable (param.parameter_type, param.parameter_type.value_owned); + temp_vars.insert (0, temp_var); + cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name)); + + if (param.direction == ParameterDirection.REF) { + var crefcomma = new CCodeCommaExpression (); + crefcomma.append_expression (new CCodeAssignment (get_variable_cexpression (temp_var.name), (CCodeExpression) unary.inner.ccodenode)); + crefcomma.append_expression (cexpr); + cexpr = crefcomma; + } + + // call function + LocalVariable ret_temp_var = null; + if (itype.get_return_type () is VoidType) { + ccomma.append_expression (ccall_expr); + } else { + ret_temp_var = get_temp_variable (itype.get_return_type ()); + temp_vars.insert (0, ret_temp_var); + ccomma.append_expression (new CCodeAssignment (get_variable_cexpression (ret_temp_var.name), ccall_expr)); + } + + var cassign_comma = new CCodeCommaExpression (); + + var assign_temp_var = get_temp_variable (unary.inner.value_type, unary.inner.value_type.value_owned); + temp_vars.insert (0, assign_temp_var); + + cassign_comma.append_expression (new CCodeAssignment (get_variable_cexpression (assign_temp_var.name), transform_expression (get_variable_cexpression (temp_var.name), param.parameter_type, unary.inner.value_type, arg))); + + // unref old value + cassign_comma.append_expression (get_unref_expression ((CCodeExpression) unary.inner.ccodenode, arg.value_type, arg)); + + cassign_comma.append_expression (get_variable_cexpression (assign_temp_var.name)); + + // assign new value + ccomma.append_expression (new CCodeAssignment ((CCodeExpression) unary.inner.ccodenode, cassign_comma)); + + // return value + if (!(itype.get_return_type () is VoidType)) { + ccomma.append_expression (get_variable_cexpression (ret_temp_var.name)); + } + + ccall_expr = ccomma; + } + + if (param.ctype != null) { + cexpr = new CCodeCastExpression (cexpr, param.ctype); + } + } + } + + ccall.add_argument (cexpr); + + i++; + } + if (params_it.next ()) { + var param = params_it.get (); + + /* if there are more parameters than arguments, + * the additional parameter is an ellipsis parameter + * otherwise there is a bug in the semantic analyzer + */ + assert (param.params_array || param.ellipsis); + ellipsis = true; + } + + if (m != null && m.return_type is GenericType) { + var ccomma = new CCodeCommaExpression (); + + var temp_var = get_temp_variable (expr.value_type); + temp_vars.insert (0, temp_var); + if (expr.value_type is GenericType) { + ccall.add_argument (get_variable_cexpression (temp_var.name)); + } else { + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression (temp_var.name))); + } + + // call function + ccomma.append_expression (ccall_expr); + + ccomma.append_expression (get_variable_cexpression (temp_var.name)); + + ccall_expr = ccomma; + } + + expr.ccodenode = ccall_expr; + } +} + diff --git a/codegen/valadovamethodmodule.vala b/codegen/valadovamethodmodule.vala new file mode 100644 index 000000000..4be1b8d06 --- /dev/null +++ b/codegen/valadovamethodmodule.vala @@ -0,0 +1,46 @@ +/* valadovamethodmodule.vala + * + * Copyright (C) 2007-2009 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 <j@bitron.ch> + */ + +/** + * The link between a method and generated code. + */ +internal class Vala.DovaMethodModule : DovaStructModule { + public DovaMethodModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override bool method_has_wrapper (Method method) { + return (method.get_attribute ("NoWrapper") == null); + } + + public override string? get_custom_creturn_type (Method m) { + var attr = m.get_attribute ("CCode"); + if (attr != null) { + string type = attr.get_string ("type"); + if (type != null) { + return type; + } + } + return null; + } +} + diff --git a/codegen/valadovaobjectmodule.vala b/codegen/valadovaobjectmodule.vala new file mode 100644 index 000000000..276cbf582 --- /dev/null +++ b/codegen/valadovaobjectmodule.vala @@ -0,0 +1,1519 @@ +/* valadovaobjectmodule.vala + * + * Copyright (C) 2009-2010 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 <j@bitron.ch> + */ + +internal class Vala.DovaObjectModule : DovaArrayModule { + public DovaObjectModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override void generate_class_declaration (Class cl, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (cl, cl.get_cname ())) { + return; + } + + if (cl.base_class == null) { + decl_space.add_type_declaration (new CCodeTypeDefinition ("struct _%s".printf (cl.get_cname ()), new CCodeVariableDeclarator (cl.get_cname ()))); + } else { + // typedef to base class instead of dummy struct to avoid warnings/casts + generate_class_declaration (cl.base_class, decl_space); + decl_space.add_type_declaration (new CCodeTypeDefinition (cl.base_class.get_cname (), new CCodeVariableDeclarator (cl.get_cname ()))); + } + + if (cl.base_class == null) { + var instance_struct = new CCodeStruct ("_%s".printf (cl.get_cname ())); + instance_struct.add_field ("DovaType *", "type"); + decl_space.add_type_definition (instance_struct); + } else if (cl == type_class) { + decl_space.add_include ("stdbool.h"); + + var value_copy_function = new CCodeFunction ("dova_type_value_copy"); + value_copy_function.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + value_copy_function.add_parameter (new CCodeFormalParameter ("dest", "void *")); + value_copy_function.add_parameter (new CCodeFormalParameter ("dest_index", "int32_t")); + value_copy_function.add_parameter (new CCodeFormalParameter ("src", "void *")); + value_copy_function.add_parameter (new CCodeFormalParameter ("src_index", "int32_t")); + + source_declarations.add_type_member_declaration (value_copy_function); + + var value_equal_function = new CCodeFunction ("dova_type_value_equal", "bool"); + value_equal_function.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + value_equal_function.add_parameter (new CCodeFormalParameter ("value", "void *")); + value_equal_function.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + value_equal_function.add_parameter (new CCodeFormalParameter ("other", "void *")); + value_equal_function.add_parameter (new CCodeFormalParameter ("other_index", "int32_t")); + + source_declarations.add_type_member_declaration (value_equal_function); + + var value_hash_function = new CCodeFunction ("dova_type_value_hash", "int32_t"); + value_hash_function.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + value_hash_function.add_parameter (new CCodeFormalParameter ("value", "void *")); + value_hash_function.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + + source_declarations.add_type_member_declaration (value_hash_function); + } + + generate_class_declaration (type_class, decl_space); + generate_method_declaration ((Method) object_class.scope.lookup ("ref"), decl_space); + generate_method_declaration ((Method) object_class.scope.lookup ("unref"), decl_space); + + var type_fun = new CCodeFunction ("%s_type_get".printf (cl.get_lower_case_cname ()), "DovaType *"); + foreach (var type_param in cl.get_type_parameters ()) { + type_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + decl_space.add_type_member_declaration (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (cl.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + foreach (var type_param in cl.get_type_parameters ()) { + type_init_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + decl_space.add_type_member_declaration (type_init_fun); + } + + void generate_virtual_method_declaration (Method m, CCodeDeclarationSpace decl_space, CCodeStruct type_struct) { + if (!m.is_abstract && !m.is_virtual) { + return; + } + + // add vfunc field to the type struct + var vdeclarator = new CCodeFunctionDeclarator (m.vfunc_name); + + generate_cparameters (m, decl_space, new CCodeFunction ("fake"), vdeclarator); + + var vdecl = new CCodeDeclaration (m.return_type.get_cname ()); + vdecl.add_declarator (vdeclarator); + type_struct.add_declaration (vdecl); + } + + void generate_class_private_declaration (Class cl, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (cl, cl.get_cname () + "Private")) { + return; + } + + var instance_priv_struct = new CCodeStruct ("_%sPrivate".printf (cl.get_cname ())); + var type_priv_struct = new CCodeStruct ("_%sTypePrivate".printf (cl.get_cname ())); + + foreach (Field f in cl.get_fields ()) { + if (f.binding == MemberBinding.INSTANCE) { + generate_type_declaration (f.field_type, decl_space); + + string field_ctype = f.field_type.get_cname (); + if (f.is_volatile) { + field_ctype = "volatile " + field_ctype; + } + + instance_priv_struct.add_field (field_ctype, f.get_cname ()); + } + } + + if (cl.get_full_name () == "Dova.Type") { + var vdeclarator = new CCodeFunctionDeclarator ("value_copy"); + vdeclarator.add_parameter (new CCodeFormalParameter ("dest", "void *")); + vdeclarator.add_parameter (new CCodeFormalParameter ("dest_index", "int32_t")); + vdeclarator.add_parameter (new CCodeFormalParameter ("src", "void *")); + vdeclarator.add_parameter (new CCodeFormalParameter ("src_index", "int32_t")); + + var vdecl = new CCodeDeclaration ("void"); + vdecl.add_declarator (vdeclarator); + instance_priv_struct.add_declaration (vdecl); + + vdeclarator = new CCodeFunctionDeclarator ("value_equal"); + vdeclarator.add_parameter (new CCodeFormalParameter ("value", "void *")); + vdeclarator.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + vdeclarator.add_parameter (new CCodeFormalParameter ("other", "void *")); + vdeclarator.add_parameter (new CCodeFormalParameter ("other_index", "int32_t")); + + vdecl = new CCodeDeclaration ("bool"); + vdecl.add_declarator (vdeclarator); + instance_priv_struct.add_declaration (vdecl); + + vdeclarator = new CCodeFunctionDeclarator ("value_hash"); + vdeclarator.add_parameter (new CCodeFormalParameter ("value", "void *")); + vdeclarator.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + + vdecl = new CCodeDeclaration ("int32_t"); + vdecl.add_declarator (vdeclarator); + instance_priv_struct.add_declaration (vdecl); + } + + foreach (var type_param in cl.get_type_parameters ()) { + var type_param_decl = new CCodeDeclaration ("DovaType *"); + type_param_decl.add_declarator (new CCodeVariableDeclarator ("%s_type".printf (type_param.name.down ()))); + type_priv_struct.add_declaration (type_param_decl); + } + + foreach (Method m in cl.get_methods ()) { + generate_virtual_method_declaration (m, decl_space, type_priv_struct); + } + + foreach (Property prop in cl.get_properties ()) { + if (!prop.is_abstract && !prop.is_virtual) { + continue; + } + generate_type_declaration (prop.property_type, decl_space); + + var t = (ObjectTypeSymbol) prop.parent_symbol; + + var this_type = new ObjectType (t); + var cselfparam = new CCodeFormalParameter ("this", this_type.get_cname ()); + var cvalueparam = new CCodeFormalParameter ("value", prop.property_type.get_cname ()); + + if (prop.get_accessor != null) { + var vdeclarator = new CCodeFunctionDeclarator ("get_%s".printf (prop.name)); + vdeclarator.add_parameter (cselfparam); + string creturn_type = prop.property_type.get_cname (); + + var vdecl = new CCodeDeclaration (creturn_type); + vdecl.add_declarator (vdeclarator); + type_priv_struct.add_declaration (vdecl); + } + if (prop.set_accessor != null) { + var vdeclarator = new CCodeFunctionDeclarator ("set_%s".printf (prop.name)); + vdeclarator.add_parameter (cselfparam); + vdeclarator.add_parameter (cvalueparam); + + var vdecl = new CCodeDeclaration ("void"); + vdecl.add_declarator (vdeclarator); + type_priv_struct.add_declaration (vdecl); + } + } + + decl_space.add_type_declaration (new CCodeTypeDefinition ("struct %s".printf (instance_priv_struct.name), new CCodeVariableDeclarator ("%sPrivate".printf (cl.get_cname ())))); + decl_space.add_type_definition (instance_priv_struct); + + decl_space.add_type_declaration (new CCodeTypeDefinition ("struct %s".printf (type_priv_struct.name), new CCodeVariableDeclarator ("%sTypePrivate".printf (cl.get_cname ())))); + decl_space.add_type_definition (type_priv_struct); + + string macro; + if (cl.base_class == null) { + // offset of Object class is 0 + macro = "((%sPrivate *) o)".printf (cl.get_cname ()); + } else { + var cdecl = new CCodeDeclaration ("int"); + cdecl.add_declarator (new CCodeVariableDeclarator ("_%s_object_offset".printf (cl.get_lower_case_cname ()), new CCodeConstant ("0"))); + cdecl.modifiers = CCodeModifiers.STATIC; + decl_space.add_type_member_declaration (cdecl); + + macro = "((%sPrivate *) (((char *) o) + _%s_object_offset))".printf (cl.get_cname (), cl.get_lower_case_cname ()); + } + decl_space.add_type_member_declaration (new CCodeMacroReplacement ("%s_GET_PRIVATE(o)".printf (cl.get_upper_case_cname (null)), macro)); + + var cdecl = new CCodeDeclaration ("int"); + cdecl.add_declarator (new CCodeVariableDeclarator ("_%s_type_offset".printf (cl.get_lower_case_cname ()), new CCodeConstant ("0"))); + cdecl.modifiers = CCodeModifiers.STATIC; + decl_space.add_type_member_declaration (cdecl); + } + + CCodeFunction create_set_value_copy_function (bool decl_only = false) { + var result = new CCodeFunction ("dova_type_set_value_copy"); + result.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + result.add_parameter (new CCodeFormalParameter ("(*function) (void *dest, int32_t dest_index, void *src, int32_t src_index)", "void")); + if (decl_only) { + return result; + } + + result.block = new CCodeBlock (); + + var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("DOVA_TYPE_GET_PRIVATE")); + priv_call.add_argument (new CCodeIdentifier ("type")); + + result.block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (priv_call, "value_copy"), new CCodeIdentifier ("function")))); + return result; + } + + public void declare_set_value_copy_function (CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (type_class, "dova_type_set_value_copy")) { + return; + } + decl_space.add_type_member_declaration (create_set_value_copy_function (true)); + } + + CCodeFunction create_set_value_equal_function (bool decl_only = false) { + var result = new CCodeFunction ("dova_type_set_value_equal"); + result.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + result.add_parameter (new CCodeFormalParameter ("(*function) (void *value, int32_t value_index, void *other, int32_t other_index)", "bool")); + if (decl_only) { + return result; + } + + result.block = new CCodeBlock (); + + var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("DOVA_TYPE_GET_PRIVATE")); + priv_call.add_argument (new CCodeIdentifier ("type")); + + result.block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (priv_call, "value_equal"), new CCodeIdentifier ("function")))); + return result; + } + + public void declare_set_value_equal_function (CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (type_class, "dova_type_set_value_equal")) { + return; + } + decl_space.add_type_member_declaration (create_set_value_equal_function (true)); + } + + CCodeFunction create_set_value_hash_function (bool decl_only = false) { + var result = new CCodeFunction ("dova_type_set_value_hash"); + result.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + result.add_parameter (new CCodeFormalParameter ("(*function) (void *value, int32_t value_index)", "int32_t")); + if (decl_only) { + return result; + } + + result.block = new CCodeBlock (); + + var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("DOVA_TYPE_GET_PRIVATE")); + priv_call.add_argument (new CCodeIdentifier ("type")); + + result.block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (priv_call, "value_hash"), new CCodeIdentifier ("function")))); + return result; + } + + public void declare_set_value_hash_function (CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (type_class, "dova_type_set_value_hash")) { + return; + } + decl_space.add_type_member_declaration (create_set_value_hash_function (true)); + } + + public CCodeBlock generate_type_get_function (TypeSymbol cl, Class? base_class) { + source_declarations.add_include ("stddef.h"); + // calloc + source_declarations.add_include ("stdlib.h"); + + var cdecl = new CCodeDeclaration ("DovaType *"); + cdecl.add_declarator (new CCodeVariableDeclarator ("%s_type".printf (cl.get_lower_case_cname ()), new CCodeConstant ("NULL"))); + cdecl.modifiers = CCodeModifiers.STATIC; + source_declarations.add_type_member_declaration (cdecl); + + var type_fun = new CCodeFunction ("%s_type_get".printf (cl.get_lower_case_cname ()), "DovaType *"); + + var object_type_symbol = cl as ObjectTypeSymbol; + if (object_type_symbol != null) { + foreach (var type_param in object_type_symbol.get_type_parameters ()) { + type_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + } + + type_fun.block = new CCodeBlock (); + + var type_init_block = new CCodeBlock (); + + if (base_class == null) { + // var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); + // sizeof_call.add_argument (new CCodeIdentifier ("DovaObject")); + + var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); + sizeof_call.add_argument (new CCodeIdentifier ("%sPrivate".printf (cl.get_cname ()))); + + var calloc_call = new CCodeFunctionCall (new CCodeIdentifier ("calloc")); + calloc_call.add_argument (new CCodeConstant ("1")); + // calloc_call.add_argument (sizeof_call); + // FIXME + calloc_call.add_argument (new CCodeConstant ("sizeof (DovaObjectPrivate) + sizeof (DovaTypePrivate) + sizeof (DovaObjectTypePrivate)")); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ())), calloc_call))); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("_dova_type_object_offset"), sizeof_call))); + + var set_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_object_size")); + set_size.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + set_size.add_argument (sizeof_call); + type_init_block.add_statement (new CCodeExpressionStatement (set_size)); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("_dova_object_type_offset"), new CCodeConstant ("sizeof (DovaObjectPrivate) + sizeof (DovaTypePrivate)")))); + + set_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_type_size")); + set_size.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + set_size.add_argument (new CCodeConstant ("sizeof (DovaObjectPrivate) + sizeof (DovaTypePrivate) + sizeof (DovaObjectTypePrivate)")); + type_init_block.add_statement (new CCodeExpressionStatement (set_size)); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeCastExpression (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ())), "DovaObject *"), "type"), new CCodeFunctionCall (new CCodeIdentifier ("dova_type_type_get"))))); + } else { + generate_method_declaration ((Method) object_class.scope.lookup ("alloc"), source_declarations); + generate_method_declaration ((Method) type_class.scope.lookup ("alloc"), source_declarations); + + var base_type = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (base_class.get_lower_case_cname ()))); + for (int i = 0; i < base_class.get_type_parameters ().size; i++) { + base_type.add_argument (new CCodeConstant ("NULL")); + } + + var alloc_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_alloc")); + alloc_call.add_argument (base_type); + alloc_call.add_argument (new CCodeConstant ("sizeof (%sPrivate)".printf (cl.get_cname ()))); + alloc_call.add_argument (new CCodeConstant ("sizeof (%sTypePrivate)".printf (cl.get_cname ()))); + alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ())))); + alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_%s_object_offset".printf (cl.get_lower_case_cname ())))); + alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_%s_type_offset".printf (cl.get_lower_case_cname ())))); + + type_init_block.add_statement (new CCodeExpressionStatement (alloc_call)); + } + + var type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (cl.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + + if (object_type_symbol != null) { + for (int i = 0; i < object_type_symbol.get_type_parameters ().size; i++) { + type_init_call.add_argument (new CCodeConstant ("NULL")); + } + } + + type_init_block.add_statement (new CCodeExpressionStatement (type_init_call)); + + type_fun.block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))), type_init_block)); + + if (object_type_symbol != null && object_type_symbol.get_type_parameters ().size > 0) { + // generics + var specialized_type_get_block = new CCodeBlock (); + + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("next_type")).get_accessor, source_declarations); + generate_method_declaration ((Method) type_class.scope.lookup ("insert_type"), source_declarations); + + var first = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_next_type")); + first.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + + cdecl = new CCodeDeclaration ("DovaType *"); + cdecl.add_declarator (new CCodeVariableDeclarator ("result", first)); + specialized_type_get_block.add_statement (cdecl); + + var next = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_next_type")); + next.add_argument (new CCodeIdentifier ("result")); + + var next_check = new CCodeBlock (); + next_check.add_statement (new CCodeIfStatement (new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeMemberAccess.pointer (get_type_private_from_type (object_type_symbol, new CCodeIdentifier ("result")), "%s_type".printf (object_type_symbol.get_type_parameters ().get (0).name.down ())), new CCodeIdentifier ("%s_type".printf (object_type_symbol.get_type_parameters ().get (0).name.down ()))), new CCodeBreakStatement ())); + next_check.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("result"), next))); + + specialized_type_get_block.add_statement (new CCodeWhileStatement (new CCodeIdentifier ("result"), next_check)); + + var specialized_type_init_block = new CCodeBlock (); + + generate_method_declaration ((Method) type_class.scope.lookup ("alloc"), source_declarations); + + var base_type = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (base_class.get_lower_case_cname ()))); + foreach (var type_param in base_class.get_type_parameters ()) { + base_type.add_argument (new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + } + + var alloc_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_alloc")); + alloc_call.add_argument (base_type); + alloc_call.add_argument (new CCodeConstant ("sizeof (%sPrivate)".printf (cl.get_cname ()))); + alloc_call.add_argument (new CCodeConstant ("sizeof (%sTypePrivate)".printf (cl.get_cname ()))); + alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("result"))); + alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_%s_object_offset".printf (cl.get_lower_case_cname ())))); + alloc_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("_%s_type_offset".printf (cl.get_lower_case_cname ())))); + + specialized_type_init_block.add_statement (new CCodeExpressionStatement (alloc_call)); + + type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (cl.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("result")); + + foreach (var type_param in object_type_symbol.get_type_parameters ()) { + type_init_call.add_argument (new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + } + + specialized_type_init_block.add_statement (new CCodeExpressionStatement (type_init_call)); + + var insert_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_insert_type")); + insert_call.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + insert_call.add_argument (new CCodeIdentifier ("result")); + specialized_type_init_block.add_statement (new CCodeExpressionStatement (insert_call)); + + specialized_type_get_block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("result")), specialized_type_init_block)); + + specialized_type_get_block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("result"))); + + type_fun.block.add_statement (new CCodeIfStatement (new CCodeIdentifier ("%s_type".printf (object_type_symbol.get_type_parameters ().get (0).name.down ())), specialized_type_get_block)); + } + + type_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ())))); + + source_type_member_definition.append (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (cl.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + if (object_type_symbol != null) { + foreach (var type_param in object_type_symbol.get_type_parameters ()) { + type_init_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + } + type_init_fun.block = new CCodeBlock (); + + if (base_class == null) { + var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); + sizeof_call.add_argument (new CCodeIdentifier ("void *")); + + var set_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_size")); + set_size.add_argument (new CCodeIdentifier ("type")); + set_size.add_argument (sizeof_call); + type_init_fun.block.add_statement (new CCodeExpressionStatement (set_size)); + + declare_set_value_copy_function (source_declarations); + + var value_copy_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_copy")); + value_copy_call.add_argument (new CCodeIdentifier ("type")); + value_copy_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("dova_object_copy"), "void (*)(void *, int32_t, void *, int32_t)")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (value_copy_call)); + + var function = new CCodeFunction ("dova_object_copy", "void"); + function.modifiers = CCodeModifiers.STATIC; + function.add_parameter (new CCodeFormalParameter ("dest", "DovaObject **")); + function.add_parameter (new CCodeFormalParameter ("dest_index", "int32_t")); + function.add_parameter (new CCodeFormalParameter ("src", "DovaObject **")); + function.add_parameter (new CCodeFormalParameter ("src_index", "int32_t")); + + function.block = new CCodeBlock (); + var cfrag = new CCodeFragment (); + function.block.add_statement (cfrag); + + var dest = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("dest"), new CCodeIdentifier ("dest_index")); + var src = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("src"), new CCodeIdentifier ("src_index")); + + var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref")); + unref_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest)); + var unref_block = new CCodeBlock (); + unref_block.add_statement (new CCodeExpressionStatement (unref_call)); + unref_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest), new CCodeConstant ("NULL")))); + function.block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest), unref_block)); + + var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_ref")); + ref_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, src)); + var ref_block = new CCodeBlock (); + ref_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest), ref_call))); + function.block.add_statement (new CCodeIfStatement (new CCodeIdentifier ("src"), ref_block)); + + source_type_member_definition.append (function); + } else { + type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (base_class.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("type")); + + foreach (var type_param in base_class.get_type_parameters ()) { + type_init_call.add_argument (new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + } + + type_init_fun.block.add_statement (new CCodeExpressionStatement (type_init_call)); + + if (object_type_symbol != null) { + foreach (var type_param in object_type_symbol.get_type_parameters ()) { + type_init_fun.block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (get_type_private_from_type (object_type_symbol, new CCodeIdentifier ("type")), "%s_type".printf (type_param.name.down ())), new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))))); + } + } + } + + source_type_member_definition.append (type_init_fun); + + return type_init_fun.block; + } + + void add_finalize_function (Class cl) { + var function = new CCodeFunction ("%sfinalize".printf (cl.get_lower_case_cprefix ()), "void"); + function.modifiers = CCodeModifiers.STATIC; + + function.add_parameter (new CCodeFormalParameter ("this", cl.get_cname () + "*")); + + source_declarations.add_type_member_declaration (function.copy ()); + + + var cblock = new CCodeBlock (); + + if (cl.destructor != null) { + cblock.add_statement (cl.destructor.ccodenode); + } + + cblock.add_statement (instance_finalize_fragment); + + // chain up to finalize function of the base class + foreach (DataType base_type in cl.get_base_types ()) { + var object_type = (ObjectType) base_type; + if (object_type.type_symbol is Class) { + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_base_finalize")); + var type_get_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (object_type.type_symbol.get_lower_case_cname ()))); + foreach (var type_arg in base_type.get_type_arguments ()) { + type_get_call.add_argument (get_type_id_expression (type_arg, false)); + } + ccall.add_argument (type_get_call); + ccall.add_argument (new CCodeIdentifier ("this")); + cblock.add_statement (new CCodeExpressionStatement (ccall)); + } + } + + + function.block = cblock; + + source_type_member_definition.append (function); + } + + public override void visit_class (Class cl) { + var old_symbol = current_symbol; + var old_instance_finalize_fragment = instance_finalize_fragment; + current_symbol = cl; + instance_finalize_fragment = new CCodeFragment (); + + generate_class_declaration (cl, source_declarations); + generate_class_private_declaration (cl, source_declarations); + + if (!cl.is_internal_symbol ()) { + generate_class_declaration (cl, header_declarations); + } + generate_class_declaration (cl, internal_header_declarations); + + cl.accept_children (codegen); + + var type_init_block = generate_type_get_function (cl, cl.base_class); + + foreach (DataType base_type in cl.get_base_types ()) { + var object_type = (ObjectType) base_type; + if (object_type.type_symbol is Interface) { + generate_interface_declaration ((Interface) object_type.type_symbol, source_declarations); + + var type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (object_type.type_symbol.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("type")); + foreach (var type_arg in base_type.get_type_arguments ()) { + type_init_call.add_argument (get_type_id_expression (type_arg, true)); + } + type_init_block.add_statement (new CCodeExpressionStatement (type_init_call)); + } + } + + // finalizer + if (cl.base_class != null && (cl.get_fields ().size > 0 || cl.destructor != null)) { + add_finalize_function (cl); + + generate_method_declaration ((Method) object_class.scope.lookup ("finalize"), source_declarations); + + var override_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_override_finalize")); + override_call.add_argument (new CCodeIdentifier ("type")); + override_call.add_argument (new CCodeIdentifier ("%sfinalize".printf (cl.get_lower_case_cprefix ()))); + type_init_block.add_statement (new CCodeExpressionStatement (override_call)); + } + + foreach (Method m in cl.get_methods ()) { + if (m.is_virtual || m.overrides) { + var override_call = new CCodeFunctionCall (new CCodeIdentifier ("%soverride_%s".printf (m.base_method.parent_symbol.get_lower_case_cprefix (), m.name))); + override_call.add_argument (new CCodeIdentifier ("type")); + override_call.add_argument (new CCodeIdentifier (m.get_real_cname ())); + type_init_block.add_statement (new CCodeExpressionStatement (override_call)); + } else if (m.base_interface_method != null) { + var override_call = new CCodeFunctionCall (new CCodeIdentifier ("%soverride_%s".printf (m.base_interface_method.parent_symbol.get_lower_case_cprefix (), m.name))); + override_call.add_argument (new CCodeIdentifier ("type")); + override_call.add_argument (new CCodeIdentifier (m.get_real_cname ())); + type_init_block.add_statement (new CCodeExpressionStatement (override_call)); + } + } + + if (cl == type_class) { + var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("DOVA_TYPE_GET_PRIVATE")); + priv_call.add_argument (new CCodeIdentifier ("type")); + + var value_copy_function = new CCodeFunction ("dova_type_value_copy"); + value_copy_function.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + value_copy_function.add_parameter (new CCodeFormalParameter ("dest", "void *")); + value_copy_function.add_parameter (new CCodeFormalParameter ("dest_index", "int32_t")); + value_copy_function.add_parameter (new CCodeFormalParameter ("src", "void *")); + value_copy_function.add_parameter (new CCodeFormalParameter ("src_index", "int32_t")); + + value_copy_function.block = new CCodeBlock (); + + var ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (priv_call, "value_copy")); + ccall.add_argument (new CCodeIdentifier ("dest")); + ccall.add_argument (new CCodeIdentifier ("dest_index")); + ccall.add_argument (new CCodeIdentifier ("src")); + ccall.add_argument (new CCodeIdentifier ("src_index")); + value_copy_function.block.add_statement (new CCodeExpressionStatement (ccall)); + + source_type_member_definition.append (value_copy_function); + + declare_set_value_copy_function (source_declarations); + declare_set_value_copy_function (header_declarations); + declare_set_value_copy_function (internal_header_declarations); + source_type_member_definition.append (create_set_value_copy_function ()); + + var value_equal_function = new CCodeFunction ("dova_type_value_equal", "bool"); + value_equal_function.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + value_equal_function.add_parameter (new CCodeFormalParameter ("value", "void *")); + value_equal_function.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + value_equal_function.add_parameter (new CCodeFormalParameter ("other", "void *")); + value_equal_function.add_parameter (new CCodeFormalParameter ("other_index", "int32_t")); + + value_equal_function.block = new CCodeBlock (); + + ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (priv_call, "value_equal")); + ccall.add_argument (new CCodeIdentifier ("value")); + ccall.add_argument (new CCodeIdentifier ("value_index")); + ccall.add_argument (new CCodeIdentifier ("other")); + ccall.add_argument (new CCodeIdentifier ("other_index")); + value_equal_function.block.add_statement (new CCodeExpressionStatement (ccall)); + + source_type_member_definition.append (value_equal_function); + + declare_set_value_equal_function (source_declarations); + declare_set_value_equal_function (header_declarations); + declare_set_value_equal_function (internal_header_declarations); + source_type_member_definition.append (create_set_value_equal_function ()); + + var value_hash_function = new CCodeFunction ("dova_type_value_hash", "int32_t"); + value_hash_function.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + value_hash_function.add_parameter (new CCodeFormalParameter ("value", "void *")); + value_hash_function.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + + value_hash_function.block = new CCodeBlock (); + + ccall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (priv_call, "value_hash")); + ccall.add_argument (new CCodeIdentifier ("value")); + ccall.add_argument (new CCodeIdentifier ("value_index")); + value_hash_function.block.add_statement (new CCodeExpressionStatement (ccall)); + + source_type_member_definition.append (value_hash_function); + + declare_set_value_hash_function (source_declarations); + declare_set_value_hash_function (header_declarations); + declare_set_value_hash_function (internal_header_declarations); + source_type_member_definition.append (create_set_value_hash_function ()); + } + + current_symbol = old_symbol; + instance_finalize_fragment = old_instance_finalize_fragment; + } + + public override void visit_interface (Interface iface) { + var old_symbol = current_symbol; + current_symbol = iface; + + generate_interface_declaration (iface, source_declarations); + + var type_priv_struct = new CCodeStruct ("_%sTypePrivate".printf (iface.get_cname ())); + + foreach (var type_param in iface.get_type_parameters ()) { + var type_param_decl = new CCodeDeclaration ("DovaType *"); + type_param_decl.add_declarator (new CCodeVariableDeclarator ("%s_type".printf (type_param.name.down ()))); + type_priv_struct.add_declaration (type_param_decl); + } + + foreach (Method m in iface.get_methods ()) { + generate_virtual_method_declaration (m, source_declarations, type_priv_struct); + } + + source_declarations.add_type_declaration (new CCodeTypeDefinition ("struct %s".printf (type_priv_struct.name), new CCodeVariableDeclarator ("%sTypePrivate".printf (iface.get_cname ())))); + source_declarations.add_type_definition (type_priv_struct); + + source_declarations.add_include ("stddef.h"); + // calloc + source_declarations.add_include ("stdlib.h"); + + var cdecl = new CCodeDeclaration ("DovaType *"); + cdecl.add_declarator (new CCodeVariableDeclarator ("%s_type".printf (iface.get_lower_case_cname ()), new CCodeConstant ("NULL"))); + cdecl.modifiers = CCodeModifiers.STATIC; + source_declarations.add_type_member_declaration (cdecl); + + var type_fun = new CCodeFunction ("%s_type_get".printf (iface.get_lower_case_cname ()), "DovaType *"); + foreach (var type_param in iface.get_type_parameters ()) { + type_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + type_fun.block = new CCodeBlock (); + + var type_init_block = new CCodeBlock (); + + var calloc_call = new CCodeFunctionCall (new CCodeIdentifier ("calloc")); + calloc_call.add_argument (new CCodeConstant ("1")); + calloc_call.add_argument (new CCodeConstant ("dova_type_get_type_size (dova_type_type_get ()) + sizeof (%sTypePrivate)".printf (iface.get_cname ()))); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("%s_type".printf (iface.get_lower_case_cname ())), calloc_call))); + + var type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (iface.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("%s_type".printf (iface.get_lower_case_cname ()))); + foreach (var type_param in iface.get_type_parameters ()) { + type_init_call.add_argument (new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + } + type_init_block.add_statement (new CCodeExpressionStatement (type_init_call)); + + type_fun.block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("%s_type".printf (iface.get_lower_case_cname ()))), type_init_block)); + + type_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("%s_type".printf (iface.get_lower_case_cname ())))); + + source_type_member_definition.append (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (iface.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + foreach (var type_param in iface.get_type_parameters ()) { + type_init_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + type_init_fun.block = new CCodeBlock (); + + foreach (DataType base_type in iface.get_prerequisites ()) { + var object_type = (ObjectType) base_type; + if (object_type.type_symbol is Interface) { + type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (object_type.type_symbol.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("type")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (type_init_call)); + } + } + + var vtable_alloc = new CCodeFunctionCall (new CCodeIdentifier ("calloc")); + vtable_alloc.add_argument (new CCodeConstant ("1")); + vtable_alloc.add_argument (new CCodeConstant ("sizeof (%sTypePrivate)".printf (iface.get_cname ()))); + + var type_get_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (iface.get_lower_case_cname ()))); + foreach (var type_param in iface.get_type_parameters ()) { + type_get_call.add_argument (new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + } + + var add_interface_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_add_interface")); + add_interface_call.add_argument (new CCodeIdentifier ("type")); + add_interface_call.add_argument (type_get_call); + add_interface_call.add_argument (vtable_alloc); + type_init_fun.block.add_statement (new CCodeExpressionStatement (add_interface_call)); + + source_type_member_definition.append (type_init_fun); + + iface.accept_children (codegen); + + current_symbol = old_symbol; + } + + public override void generate_property_accessor_declaration (PropertyAccessor acc, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (acc.prop, acc.get_cname ())) { + return; + } + + var prop = (Property) acc.prop; + + generate_type_declaration (acc.value_type, decl_space); + + CCodeFunction function; + + if (acc.readable) { + function = new CCodeFunction (acc.get_cname (), acc.value_type.get_cname ()); + } else { + function = new CCodeFunction (acc.get_cname (), "void"); + } + + if (prop.binding == MemberBinding.INSTANCE) { + DataType this_type; + if (prop.parent_symbol is Struct) { + var st = (Struct) prop.parent_symbol; + this_type = SemanticAnalyzer.get_data_type_for_symbol (st); + } else { + var t = (ObjectTypeSymbol) prop.parent_symbol; + this_type = new ObjectType (t); + } + + generate_type_declaration (this_type, decl_space); + var cselfparam = new CCodeFormalParameter ("this", this_type.get_cname ()); + + function.add_parameter (cselfparam); + } + + if (acc.writable) { + var cvalueparam = new CCodeFormalParameter ("value", acc.value_type.get_cname ()); + function.add_parameter (cvalueparam); + } + + if (prop.is_private_symbol () || acc.access == SymbolAccessibility.PRIVATE) { + function.modifiers |= CCodeModifiers.STATIC; + } + decl_space.add_type_member_declaration (function); + } + + public override void visit_property_accessor (PropertyAccessor acc) { + var old_symbol = current_symbol; + bool old_method_inner_error = current_method_inner_error; + current_symbol = acc; + current_method_inner_error = false; + + var prop = (Property) acc.prop; + + acc.accept_children (codegen); + + // do not declare overriding properties and interface implementations + if (prop.is_abstract || prop.is_virtual + || (prop.base_property == null && prop.base_interface_property == null)) { + generate_property_accessor_declaration (acc, source_declarations); + + if (!prop.is_internal_symbol () + && (acc.access == SymbolAccessibility.PUBLIC + || acc.access == SymbolAccessibility.PROTECTED)) { + generate_property_accessor_declaration (acc, header_declarations); + } + generate_property_accessor_declaration (acc, internal_header_declarations); + } + + DataType this_type; + if (prop.parent_symbol is Struct) { + var st = (Struct) prop.parent_symbol; + this_type = SemanticAnalyzer.get_data_type_for_symbol (st); + } else { + var t = (ObjectTypeSymbol) prop.parent_symbol; + this_type = new ObjectType (t); + } + var cselfparam = new CCodeFormalParameter ("this", this_type.get_cname ()); + var cvalueparam = new CCodeFormalParameter ("value", acc.value_type.get_cname ()); + + string cname = acc.get_cname (); + + if (prop.is_abstract || prop.is_virtual) { + CCodeFunction function; + if (acc.readable) { + function = new CCodeFunction (acc.get_cname (), current_return_type.get_cname ()); + } else { + function = new CCodeFunction (acc.get_cname (), "void"); + } + function.add_parameter (cselfparam); + if (acc.writable) { + function.add_parameter (cvalueparam); + } + + if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) { + // accessor function should be private if the property is an internal symbol + function.modifiers |= CCodeModifiers.STATIC; + } + + var block = new CCodeBlock (); + function.block = block; + + var vcast = get_type_private_from_type ((ObjectTypeSymbol) prop.parent_symbol, get_type_from_instance (new CCodeIdentifier ("this"))); + + if (acc.readable) { + var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "get_%s".printf (prop.name))); + vcall.add_argument (new CCodeIdentifier ("this")); + block.add_statement (new CCodeReturnStatement (vcall)); + } else { + var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, "set_%s".printf (prop.name))); + vcall.add_argument (new CCodeIdentifier ("this")); + vcall.add_argument (new CCodeIdentifier ("value")); + block.add_statement (new CCodeExpressionStatement (vcall)); + } + + source_type_member_definition.append (function); + } + + if (!prop.is_abstract) { + CCodeFunction function; + if (acc.writable) { + function = new CCodeFunction (cname, "void"); + } else { + function = new CCodeFunction (cname, acc.value_type.get_cname ()); + } + + if (prop.binding == MemberBinding.INSTANCE) { + function.add_parameter (cselfparam); + } + if (acc.writable) { + function.add_parameter (cvalueparam); + } + + if (prop.is_private_symbol () || !(acc.readable || acc.writable) || acc.access == SymbolAccessibility.PRIVATE) { + // accessor function should be private if the property is an internal symbol + function.modifiers |= CCodeModifiers.STATIC; + } + + function.block = (CCodeBlock) acc.body.ccodenode; + + if (acc.readable) { + var cdecl = new CCodeDeclaration (acc.value_type.get_cname ()); + cdecl.add_declarator (new CCodeVariableDeclarator ("result", default_value_for_type (acc.value_type, true))); + function.block.prepend_statement (cdecl); + + function.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("result"))); + } + + source_type_member_definition.append (function); + } + + current_symbol = old_symbol; + current_method_inner_error = old_method_inner_error; + } + + public override void generate_interface_declaration (Interface iface, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (iface, iface.get_cname ())) { + return; + } + + // typedef to DovaObject instead of dummy struct to avoid warnings/casts + generate_class_declaration (object_class, decl_space); + decl_space.add_type_declaration (new CCodeTypeDefinition ("DovaObject", new CCodeVariableDeclarator (iface.get_cname ()))); + + generate_class_declaration (type_class, decl_space); + + var type_fun = new CCodeFunction ("%s_type_get".printf (iface.get_lower_case_cname ()), "DovaType *"); + foreach (var type_param in iface.get_type_parameters ()) { + type_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + decl_space.add_type_member_declaration (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (iface.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + foreach (var type_param in iface.get_type_parameters ()) { + type_init_fun.add_parameter (new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType *")); + } + decl_space.add_type_member_declaration (type_init_fun); + } + + + public override bool method_has_wrapper (Method method) { + return (method.get_attribute ("NoWrapper") == null); + } + + public override string? get_custom_creturn_type (Method m) { + var attr = m.get_attribute ("CCode"); + if (attr != null) { + string type = attr.get_string ("type"); + if (type != null) { + return type; + } + } + return null; + } + + public override void generate_method_declaration (Method m, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (m, m.get_cname ())) { + return; + } + + var function = new CCodeFunction (m.get_cname ()); + + if (m.is_private_symbol ()) { + function.modifiers |= CCodeModifiers.STATIC; + if (m.is_inline) { + function.modifiers |= CCodeModifiers.INLINE; + } + } + + generate_cparameters (m, decl_space, function, null, new CCodeFunctionCall (new CCodeIdentifier ("fake"))); + + decl_space.add_type_member_declaration (function); + + if (m.is_abstract || m.is_virtual) { + var base_func = function.copy (); + base_func.name = "%sbase_%s".printf (m.parent_symbol.get_lower_case_cprefix (), m.name); + base_func.insert_parameter (0, new CCodeFormalParameter ("base_type", "DovaType *")); + decl_space.add_type_member_declaration (base_func); + + string param_list = "(%s *this".printf (((ObjectTypeSymbol) m.parent_symbol).get_cname ()); + foreach (var param in m.get_parameters ()) { + param_list += ", "; + param_list += param.parameter_type.get_cname (); + } + if (m.return_type is GenericType) { + param_list += ", void *"; + } + param_list += ")"; + + var override_func = new CCodeFunction ("%soverride_%s".printf (m.parent_symbol.get_lower_case_cprefix (), m.name)); + override_func.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + override_func.add_parameter (new CCodeFormalParameter ("(*function) %s".printf (param_list), (m.return_type is GenericType) ? "void" : m.return_type.get_cname ())); + decl_space.add_type_member_declaration (override_func); + } + + if (m is CreationMethod && m.parent_symbol is Class) { + generate_class_declaration ((Class) m.parent_symbol, decl_space); + + // _init function + function = new CCodeFunction (m.get_real_cname ()); + + if (m.is_private_symbol ()) { + function.modifiers |= CCodeModifiers.STATIC; + } + + generate_cparameters (m, decl_space, function); + + decl_space.add_type_member_declaration (function); + } + } + + CCodeExpression get_type_from_instance (CCodeExpression instance_expression) { + return new CCodeMemberAccess.pointer (new CCodeCastExpression (instance_expression, "DovaObject *"), "type"); + } + + public override void visit_method (Method m) { + var old_symbol = current_symbol; + bool old_method_inner_error = current_method_inner_error; + int old_next_temp_var_id = next_temp_var_id; + var old_temp_vars = temp_vars; + var old_temp_ref_vars = temp_ref_vars; + var old_variable_name_map = variable_name_map; + var old_try = current_try; + current_symbol = m; + current_method_inner_error = false; + next_temp_var_id = 0; + temp_vars = new ArrayList<LocalVariable> (); + temp_ref_vars = new ArrayList<LocalVariable> (); + variable_name_map = new HashMap<string,string> (str_hash, str_equal); + current_try = null; + + m.accept_children (codegen); + + current_symbol = old_symbol; + current_method_inner_error = old_method_inner_error; + next_temp_var_id = old_next_temp_var_id; + temp_vars = old_temp_vars; + temp_ref_vars = old_temp_ref_vars; + variable_name_map = old_variable_name_map; + current_try = old_try; + + generate_method_declaration (m, source_declarations); + + if (!m.is_internal_symbol ()) { + generate_method_declaration (m, header_declarations); + } + generate_method_declaration (m, internal_header_declarations); + + var function = new CCodeFunction (m.get_real_cname ()); + m.ccodenode = function; + + generate_cparameters (m, source_declarations, function); + + // generate *_real_* functions for virtual methods + if (!m.is_abstract) { + if (m.base_method != null || m.base_interface_method != null) { + // declare *_real_* function + function.modifiers |= CCodeModifiers.STATIC; + source_declarations.add_type_member_declaration (function.copy ()); + } else if (m.is_private_symbol ()) { + function.modifiers |= CCodeModifiers.STATIC; + } + + if (m.body != null) { + function.block = (CCodeBlock) m.body.ccodenode; + function.block.line = function.line; + + var cinit = new CCodeFragment (); + function.block.prepend_statement (cinit); + + foreach (FormalParameter param in m.get_parameters ()) { + if (param.ellipsis) { + break; + } + + var t = param.parameter_type.data_type; + if (t != null && t.is_reference_type ()) { + if (param.direction == ParameterDirection.OUT) { + // ensure that the passed reference for output parameter is cleared + var a = new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (param.name)), new CCodeConstant ("NULL")); + var cblock = new CCodeBlock (); + cblock.add_statement (new CCodeExpressionStatement (a)); + + var condition = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, new CCodeIdentifier (param.name), new CCodeConstant ("NULL")); + var if_statement = new CCodeIfStatement (condition, cblock); + cinit.append (if_statement); + } + } + } + + if (!(m.return_type is VoidType) && !(m.return_type is GenericType)) { + var cdecl = new CCodeDeclaration (m.return_type.get_cname ()); + cdecl.add_declarator (new CCodeVariableDeclarator ("result", default_value_for_type (m.return_type, true))); + cinit.append (cdecl); + + function.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("result"))); + } + + var st = m.parent_symbol as Struct; + if (m is CreationMethod && st != null && (st.is_boolean_type () || st.is_integer_type () || st.is_floating_type ())) { + var cdecl = new CCodeDeclaration (st.get_cname ()); + cdecl.add_declarator (new CCodeVariableDeclarator ("this", new CCodeConstant ("0"))); + cinit.append (cdecl); + + function.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("this"))); + } + + source_type_member_definition.append (function); + } + } + + if (m.is_abstract || m.is_virtual) { + generate_class_declaration ((Class) object_class, source_declarations); + + var vfunc = new CCodeFunction (m.get_cname (), (m.return_type is GenericType) ? "void" : m.return_type.get_cname ()); + vfunc.block = new CCodeBlock (); + + vfunc.add_parameter (new CCodeFormalParameter ("this", "%s *".printf (((ObjectTypeSymbol) m.parent_symbol).get_cname ()))); + foreach (FormalParameter param in m.get_parameters ()) { + string ctypename = param.parameter_type.get_cname (); + if (param.direction != ParameterDirection.IN) { + ctypename += "*"; + } + vfunc.add_parameter (new CCodeFormalParameter (param.name, ctypename)); + } + if (m.return_type is GenericType) { + vfunc.add_parameter (new CCodeFormalParameter ("result", "void *")); + } + + var vcast = get_type_private_from_type ((ObjectTypeSymbol) m.parent_symbol, get_type_from_instance (new CCodeIdentifier ("this"))); + + var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, m.vfunc_name)); + vcall.add_argument (new CCodeIdentifier ("this")); + foreach (FormalParameter param in m.get_parameters ()) { + vcall.add_argument (new CCodeIdentifier (param.name)); + } + if (m.return_type is GenericType) { + vcall.add_argument (new CCodeIdentifier ("result")); + vfunc.block.add_statement (new CCodeExpressionStatement (vcall)); + } else if (m.return_type is VoidType) { + vfunc.block.add_statement (new CCodeExpressionStatement (vcall)); + } else { + vfunc.block.add_statement (new CCodeReturnStatement (vcall)); + } + + source_type_member_definition.append (vfunc); + + + vfunc = new CCodeFunction ("%sbase_%s".printf (m.parent_symbol.get_lower_case_cprefix (), m.name), (m.return_type is GenericType) ? "void" : m.return_type.get_cname ()); + vfunc.block = new CCodeBlock (); + + vfunc.add_parameter (new CCodeFormalParameter ("base_type", "DovaType *")); + vfunc.add_parameter (new CCodeFormalParameter ("this", "%s *".printf (((ObjectTypeSymbol) m.parent_symbol).get_cname ()))); + foreach (FormalParameter param in m.get_parameters ()) { + string ctypename = param.parameter_type.get_cname (); + if (param.direction != ParameterDirection.IN) { + ctypename += "*"; + } + vfunc.add_parameter (new CCodeFormalParameter (param.name, ctypename)); + } + if (m.return_type is GenericType) { + vfunc.add_parameter (new CCodeFormalParameter ("result", "void *")); + } + + var base_type = new CCodeIdentifier ("base_type"); + + vcast = get_type_private_from_type ((ObjectTypeSymbol) m.parent_symbol, base_type); + + vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, m.vfunc_name)); + vcall.add_argument (new CCodeIdentifier ("this")); + foreach (FormalParameter param in m.get_parameters ()) { + vcall.add_argument (new CCodeIdentifier (param.name)); + } + if (m.return_type is GenericType) { + vcall.add_argument (new CCodeIdentifier ("result")); + vfunc.block.add_statement (new CCodeExpressionStatement (vcall)); + } else if (m.return_type is VoidType) { + vfunc.block.add_statement (new CCodeExpressionStatement (vcall)); + } else { + vfunc.block.add_statement (new CCodeReturnStatement (vcall)); + } + + source_type_member_definition.append (vfunc); + + + string param_list = "(%s *this".printf (((ObjectTypeSymbol) m.parent_symbol).get_cname ()); + foreach (var param in m.get_parameters ()) { + param_list += ", "; + param_list += param.parameter_type.get_cname (); + } + if (m.return_type is GenericType) { + param_list += ", void *"; + } + param_list += ")"; + + var override_func = new CCodeFunction ("%soverride_%s".printf (m.parent_symbol.get_lower_case_cprefix (), m.name)); + override_func.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + override_func.add_parameter (new CCodeFormalParameter ("(*function) %s".printf (param_list), (m.return_type is GenericType) ? "void" : m.return_type.get_cname ())); + override_func.block = new CCodeBlock (); + + vcast = get_type_private_from_type ((ObjectTypeSymbol) m.parent_symbol, new CCodeIdentifier ("type")); + + override_func.block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (vcast, m.name), new CCodeIdentifier ("function")))); + + source_type_member_definition.append (override_func); + } + + if (m.entry_point) { + // 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 (); + + var array_creation = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_new")); + array_creation.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("string_type_get"))); + array_creation.add_argument (new CCodeIdentifier ("argc")); + + var cdecl = new CCodeDeclaration ("DovaArray*"); + cdecl.add_declarator (new CCodeVariableDeclarator ("args", array_creation)); + main_block.add_statement (cdecl); + + var array_data = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_data")); + array_data.add_argument (new CCodeIdentifier ("args")); + + cdecl = new CCodeDeclaration ("string**"); + cdecl.add_declarator (new CCodeVariableDeclarator ("args_data", array_data)); + main_block.add_statement (cdecl); + + cdecl = new CCodeDeclaration ("int"); + cdecl.add_declarator (new CCodeVariableDeclarator ("argi")); + main_block.add_statement (cdecl); + + var string_creation = new CCodeFunctionCall (new CCodeIdentifier ("string_create_from_cstring")); + string_creation.add_argument (new CCodeElementAccess (new CCodeIdentifier ("argv"), new CCodeIdentifier ("argi"))); + + var loop_block = new CCodeBlock (); + loop_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeElementAccess (new CCodeIdentifier ("args_data"), new CCodeIdentifier ("argi")), string_creation))); + + var for_stmt = new CCodeForStatement (new CCodeBinaryExpression (CCodeBinaryOperator.LESS_THAN, new CCodeIdentifier ("argi"), new CCodeIdentifier ("argc")), loop_block); + for_stmt.add_initializer (new CCodeAssignment (new CCodeIdentifier ("argi"), new CCodeConstant ("0"))); + for_stmt.add_iterator (new CCodeUnaryExpression (CCodeUnaryOperator.POSTFIX_INCREMENT, new CCodeIdentifier ("argi"))); + main_block.add_statement (for_stmt); + + var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name)); + if (m.get_parameters ().size == 1) { + main_call.add_argument (new CCodeIdentifier ("args")); + } + if (m.return_type is VoidType) { + // 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); + } else { + var main_stmt = new CCodeReturnStatement (main_call); + main_stmt.line = cmain.line; + main_block.add_statement (main_stmt); + } + cmain.block = main_block; + source_type_member_definition.append (cmain); + } + } + + public override void visit_creation_method (CreationMethod m) { + bool visible = !m.is_private_symbol (); + + head.visit_method (m); + + DataType creturn_type; + if (current_type_symbol is Class) { + creturn_type = new ObjectType (current_class); + } else { + creturn_type = new VoidType (); + } + + // do not generate _new functions for creation methods of abstract classes + if (current_type_symbol is Class && !current_class.is_abstract) { + var vfunc = new CCodeFunction (m.get_cname ()); + + var vblock = new CCodeBlock (); + + var cdecl = new CCodeDeclaration ("%s *".printf (current_type_symbol.get_cname ())); + cdecl.add_declarator (new CCodeVariableDeclarator ("this")); + vblock.add_statement (cdecl); + + source_declarations.add_include ("stddef.h"); + + var type_get = new CCodeFunctionCall (new CCodeIdentifier (current_class.get_lower_case_cname () + "_type_get")); + foreach (var type_param in current_class.get_type_parameters ()) { + type_get.add_argument (new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + } + + var alloc_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_alloc")); + alloc_call.add_argument (type_get); + vblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("this"), new CCodeCastExpression (alloc_call, "%s *".printf (current_type_symbol.get_cname ()))))); + + // allocate memory for fields of generic types + // this is only a temporary measure until this can be allocated inline at the end of the instance + // this also won't work for subclasses of classes that have fields of generic types + foreach (var f in current_class.get_fields ()) { + if (f.binding != MemberBinding.INSTANCE || !(f.field_type is GenericType)) { + continue; + } + + var generic_type = (GenericType) f.field_type; + var type_get_value_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_value_size")); + type_get_value_size.add_argument (new CCodeIdentifier ("%s_type".printf (generic_type.type_parameter.name.down ()))); + + var calloc_call = new CCodeFunctionCall (new CCodeIdentifier ("calloc")); + calloc_call.add_argument (new CCodeConstant ("1")); + calloc_call.add_argument (type_get_value_size); + var priv_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_PRIVATE".printf (current_class.get_upper_case_cname (null)))); + priv_call.add_argument (new CCodeIdentifier ("this")); + + vblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (priv_call, f.name), calloc_call))); + } + + var vcall = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ())); + vcall.add_argument (new CCodeIdentifier ("this")); + vblock.add_statement (new CCodeExpressionStatement (vcall)); + + generate_cparameters (m, source_declarations, vfunc, null, vcall); + CCodeStatement cstmt = new CCodeReturnStatement (new CCodeIdentifier ("this")); + cstmt.line = vfunc.line; + vblock.add_statement (cstmt); + + if (!visible) { + vfunc.modifiers |= CCodeModifiers.STATIC; + } + + source_declarations.add_type_member_declaration (vfunc.copy ()); + + vfunc.block = vblock; + + source_type_member_definition.append (vfunc); + } + } + + 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 generate_cparameters (Method m, CCodeDeclarationSpace decl_space, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, CCodeFunctionCall? vcall = null) { + CCodeFormalParameter instance_param = null; + if (m.parent_symbol is Class && m is CreationMethod) { + if (vcall == null) { + instance_param = new CCodeFormalParameter ("this", ((Class) m.parent_symbol).get_cname () + "*"); + } + } else if (m.binding == MemberBinding.INSTANCE || (m.parent_symbol is Struct && m is CreationMethod)) { + TypeSymbol parent_type = find_parent_type (m); + var this_type = get_data_type_for_symbol (parent_type); + + generate_type_declaration (this_type, decl_space); + + if (m.base_interface_method != null && !m.is_abstract && !m.is_virtual) { + var base_type = new ObjectType ((Interface) m.base_interface_method.parent_symbol); + instance_param = new CCodeFormalParameter ("this", base_type.get_cname ()); + } else if (m.overrides) { + var base_type = new ObjectType ((Class) m.base_method.parent_symbol); + generate_type_declaration (base_type, decl_space); + instance_param = new CCodeFormalParameter ("this", base_type.get_cname ()); + } else { + if (m.parent_symbol is Struct && m is CreationMethod) { + var st = (Struct) m.parent_symbol; + if (st.is_boolean_type () || st.is_integer_type () || st.is_floating_type ()) { + // use return value + } else { + instance_param = new CCodeFormalParameter ("*this", this_type.get_cname ()); + } + } else { + instance_param = new CCodeFormalParameter ("this", this_type.get_cname ()); + } + } + } + if (instance_param != null) { + func.add_parameter (instance_param); + if (vdeclarator != null) { + vdeclarator.add_parameter (instance_param); + } + } + + if (m is CreationMethod) { + generate_class_declaration ((Class) type_class, decl_space); + + if (m.parent_symbol is Class) { + int type_param_index = 0; + var cl = (Class) m.parent_symbol; + foreach (TypeParameter type_param in cl.get_type_parameters ()) { + var cparam = new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType*"); + if (vcall != null) { + func.add_parameter (cparam); + } + type_param_index++; + } + } + } else { + int type_param_index = 0; + foreach (TypeParameter type_param in m.get_type_parameters ()) { + var cparam = new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "DovaType*"); + func.add_parameter (cparam); + if (vdeclarator != null) { + vdeclarator.add_parameter (cparam); + } + if (vcall != null) { + vcall.add_argument (new CCodeIdentifier ("%s_type".printf (type_param.name.down ()))); + } + type_param_index++; + } + } + + foreach (FormalParameter param in m.get_parameters ()) { + CCodeFormalParameter cparam; + if (!param.ellipsis) { + string ctypename = param.parameter_type.get_cname (); + + generate_type_declaration (param.parameter_type, decl_space); + + if (param.direction != ParameterDirection.IN && !(param.parameter_type is GenericType)) { + ctypename += "*"; + } + + cparam = new CCodeFormalParameter (get_variable_cname (param.name), ctypename); + } else { + cparam = new CCodeFormalParameter.with_ellipsis (); + } + + func.add_parameter (cparam); + if (vdeclarator != null) { + vdeclarator.add_parameter (cparam); + } + if (vcall != null) { + vcall.add_argument (get_variable_cexpression (param.name)); + } + } + + if (m.parent_symbol is Class && m is CreationMethod && vcall != null) { + func.return_type = ((Class) m.parent_symbol).get_cname () + "*"; + } else { + if (m.return_type is GenericType) { + func.add_parameter (new CCodeFormalParameter ("result", "void *")); + if (vdeclarator != null) { + vdeclarator.add_parameter (new CCodeFormalParameter ("result", "void *")); + } + } else { + var st = m.parent_symbol as Struct; + if (m is CreationMethod && st != null && (st.is_boolean_type () || st.is_integer_type () || st.is_floating_type ())) { + func.return_type = st.get_cname (); + } else { + func.return_type = m.return_type.get_cname (); + } + } + + generate_type_declaration (m.return_type, decl_space); + } + } + + public override void visit_element_access (ElementAccess expr) { + var array_type = expr.container.value_type as ArrayType; + if (array_type != null) { + // access to element in an array + + expr.accept_children (codegen); + + List<Expression> indices = expr.get_indices (); + var cindex = (CCodeExpression) indices[0].ccodenode; + + if (array_type.inline_allocated) { + expr.ccodenode = new CCodeElementAccess ((CCodeExpression) expr.container.ccodenode, cindex); + } else { + generate_property_accessor_declaration (((Property) array_class.scope.lookup ("data")).get_accessor, source_declarations); + + var ccontainer = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_data")); + ccontainer.add_argument ((CCodeExpression) expr.container.ccodenode); + expr.ccodenode = new CCodeElementAccess (new CCodeCastExpression (ccontainer, "%s*".printf (array_type.element_type.get_cname ())), cindex); + } + } else { + base.visit_element_access (expr); + } + } +} diff --git a/codegen/valadovastructmodule.vala b/codegen/valadovastructmodule.vala new file mode 100644 index 000000000..ba33a1a48 --- /dev/null +++ b/codegen/valadovastructmodule.vala @@ -0,0 +1,101 @@ +/* valadovastructmodule.vala + * + * Copyright (C) 2006-2009 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 <j@bitron.ch> + */ + +using GLib; + +internal class Vala.DovaStructModule : DovaBaseModule { + public DovaStructModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override void generate_struct_declaration (Struct st, CCodeDeclarationSpace decl_space) { + if (decl_space.add_symbol_declaration (st, st.get_cname ())) { + return; + } + + if (st.base_struct != null) { + generate_struct_declaration (st.base_struct, decl_space); + + decl_space.add_type_declaration (new CCodeTypeDefinition (st.base_struct.get_cname (), new CCodeVariableDeclarator (st.get_cname ()))); + return; + } + + if (st.is_boolean_type ()) { + // typedef for boolean types + decl_space.add_include ("stdbool.h"); + st.set_cname ("bool"); + return; + } else if (st.is_integer_type ()) { + // typedef for integral types + decl_space.add_include ("stdint.h"); + st.set_cname ("%sint%d_t".printf (st.signed ? "" : "u", st.width)); + return; + } else if (st.is_decimal_floating_type ()) { + // typedef for decimal floating types + st.set_cname ("_Decimal%d".printf (st.width)); + return; + } else if (st.is_floating_type ()) { + // typedef for generic floating types + st.set_cname (st.width == 64 ? "double" : "float"); + return; + } + + var instance_struct = new CCodeStruct ("_%s".printf (st.get_cname ())); + + foreach (Field f in st.get_fields ()) { + string field_ctype = f.field_type.get_cname (); + if (f.is_volatile) { + field_ctype = "volatile " + field_ctype; + } + + if (f.binding == MemberBinding.INSTANCE) { + generate_type_declaration (f.field_type, decl_space); + + instance_struct.add_field (field_ctype, f.get_cname () + f.field_type.get_cdeclarator_suffix ()); + } + } + + decl_space.add_type_declaration (new CCodeTypeDefinition ("struct _%s".printf (st.get_cname ()), new CCodeVariableDeclarator (st.get_cname ()))); + + decl_space.add_type_definition (instance_struct); + } + + public override void visit_struct (Struct st) { + var old_symbol = current_symbol; + var old_instance_finalize_fragment = instance_finalize_fragment; + current_symbol = st; + instance_finalize_fragment = new CCodeFragment (); + + generate_struct_declaration (st, source_declarations); + + if (!st.is_internal_symbol ()) { + generate_struct_declaration (st, header_declarations); + } + generate_struct_declaration (st, internal_header_declarations); + + st.accept_children (codegen); + + current_symbol = old_symbol; + instance_finalize_fragment = old_instance_finalize_fragment; + } +} + diff --git a/codegen/valadovavaluemodule.vala b/codegen/valadovavaluemodule.vala new file mode 100644 index 000000000..ca0d95e9f --- /dev/null +++ b/codegen/valadovavaluemodule.vala @@ -0,0 +1,863 @@ +/* valadovavaluemodule.vala + * + * Copyright (C) 2009-2010 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 <j@bitron.ch> + */ + +internal class Vala.DovaValueModule : DovaObjectModule { + public DovaValueModule (CCodeGenerator codegen, CCodeModule? next) { + base (codegen, next); + } + + public override void generate_class_declaration (Class cl, CCodeDeclarationSpace decl_space) { + if (cl.base_class == null || + cl.base_class.get_full_name () != "Dova.Value") { + base.generate_class_declaration (cl, decl_space); + return; + } + + if (decl_space.add_symbol_declaration (cl, cl.get_cname ())) { + return; + } + + var type_fun = new CCodeFunction ("%s_type_get".printf (cl.get_lower_case_cname ()), "DovaType *"); + if (cl.access == SymbolAccessibility.PRIVATE) { + type_fun.modifiers = CCodeModifiers.STATIC; + } + decl_space.add_type_member_declaration (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (cl.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + if (cl.access == SymbolAccessibility.PRIVATE) { + type_init_fun.modifiers = CCodeModifiers.STATIC; + } + decl_space.add_type_member_declaration (type_init_fun); + + var instance_struct = new CCodeStruct ("_%s".printf (cl.get_cname ())); + + foreach (Field f in cl.get_fields ()) { + if (f.binding == MemberBinding.INSTANCE) { + generate_type_declaration (f.field_type, decl_space); + + string field_ctype = f.field_type.get_cname (); + if (f.is_volatile) { + field_ctype = "volatile " + field_ctype; + } + + string cname = f.get_cname (); + var array_type = f.field_type as ArrayType; + if (array_type != null && array_type.inline_allocated) { + cname += "[]"; + } + + instance_struct.add_field (field_ctype, cname); + } + } + + decl_space.add_type_declaration (new CCodeTypeDefinition ("struct _%s".printf (cl.get_cname ()), new CCodeVariableDeclarator (cl.get_cname ()))); + decl_space.add_type_definition (instance_struct); + + if (cl.get_full_name () == "string") { + generate_method_declaration ((Method) cl.scope.lookup ("ref"), decl_space); + generate_method_declaration ((Method) cl.scope.lookup ("unref"), decl_space); + } + } + + public override void visit_class (Class cl) { + if (cl.base_class == null || + cl.base_class.get_full_name () != "Dova.Value") { + base.visit_class (cl); + return; + } + + var old_symbol = current_symbol; + current_symbol = cl; + + generate_class_declaration (cl, source_declarations); + + if (!cl.is_internal_symbol ()) { + generate_class_declaration (cl, header_declarations); + } + generate_class_declaration (cl, internal_header_declarations); + + + var cdecl = new CCodeDeclaration ("DovaType *"); + cdecl.add_declarator (new CCodeVariableDeclarator ("%s_type".printf (cl.get_lower_case_cname ()), new CCodeConstant ("NULL"))); + cdecl.modifiers = CCodeModifiers.STATIC; + source_declarations.add_type_member_declaration (cdecl); + + var type_fun = new CCodeFunction ("%s_type_get".printf (cl.get_lower_case_cname ()), "DovaType *"); + type_fun.block = new CCodeBlock (); + + var type_init_block = new CCodeBlock (); + + generate_method_declaration ((Method) object_class.scope.lookup ("alloc"), source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("base_type")).set_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("type_size")).get_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("type_size")).set_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("value_size")).set_accessor, source_declarations); + + generate_class_declaration ((Class) context.root.scope.lookup ("Dova").scope.lookup ("Value"), source_declarations); + + var base_type = new CCodeFunctionCall (new CCodeIdentifier ("dova_value_type_get")); + + var base_type_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_type_size")); + base_type_size.add_argument (base_type); + + var calloc_call = new CCodeFunctionCall (new CCodeIdentifier ("calloc")); + calloc_call.add_argument (new CCodeConstant ("1")); + calloc_call.add_argument (base_type_size); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ())), calloc_call))); + + generate_class_declaration ((Class) object_class, source_declarations); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeCastExpression (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ())), "DovaObject *"), "type"), new CCodeFunctionCall (new CCodeIdentifier ("dova_type_type_get"))))); + + var set_base_type = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_base_type")); + set_base_type.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + set_base_type.add_argument (base_type); + type_init_block.add_statement (new CCodeExpressionStatement (set_base_type)); + + var set_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_type_size")); + set_size.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + set_size.add_argument (base_type_size); + type_init_block.add_statement (new CCodeExpressionStatement (set_size)); + + var type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (cl.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + type_init_block.add_statement (new CCodeExpressionStatement (type_init_call)); + + type_fun.block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))), type_init_block)); + + type_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ())))); + + source_type_member_definition.append (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (cl.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + type_init_fun.block = new CCodeBlock (); + + type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_value_type_init")); + type_init_call.add_argument (new CCodeIdentifier ("type")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (type_init_call)); + + declare_set_value_copy_function (source_declarations); + + var value_copy_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_copy")); + value_copy_call.add_argument (new CCodeIdentifier ("%s_type".printf (cl.get_lower_case_cname ()))); + value_copy_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("string_copy"), "void (*)(void *, int32_t, void *, int32_t)")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (value_copy_call)); + + var function = new CCodeFunction ("string_copy", "void"); + function.modifiers = CCodeModifiers.STATIC; + function.add_parameter (new CCodeFormalParameter ("dest", "string **")); + function.add_parameter (new CCodeFormalParameter ("dest_index", "int32_t")); + function.add_parameter (new CCodeFormalParameter ("src", "string **")); + function.add_parameter (new CCodeFormalParameter ("src_index", "int32_t")); + + function.block = new CCodeBlock (); + var cfrag = new CCodeFragment (); + function.block.add_statement (cfrag); + + var dest = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("dest"), new CCodeIdentifier ("dest_index")); + var src = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("src"), new CCodeIdentifier ("src_index")); + + var unref_call = new CCodeFunctionCall (new CCodeIdentifier ("string_unref")); + unref_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest)); + var unref_block = new CCodeBlock (); + unref_block.add_statement (new CCodeExpressionStatement (unref_call)); + unref_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest), new CCodeConstant ("NULL")))); + function.block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest), unref_block)); + + var ref_call = new CCodeFunctionCall (new CCodeIdentifier ("string_ref")); + ref_call.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, src)); + var ref_block = new CCodeBlock (); + ref_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest), ref_call))); + function.block.add_statement (new CCodeIfStatement (new CCodeIdentifier ("src"), ref_block)); + + source_type_member_definition.append (function); + + if (cl.scope.lookup ("equal") is Method) { + var value_equal_fun = new CCodeFunction ("%s_value_equal".printf (cl.get_lower_case_cname ()), "bool"); + value_equal_fun.modifiers = CCodeModifiers.STATIC; + value_equal_fun.add_parameter (new CCodeFormalParameter ("value", cl.get_cname () + "**")); + value_equal_fun.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + value_equal_fun.add_parameter (new CCodeFormalParameter ("other", cl.get_cname () + "**")); + value_equal_fun.add_parameter (new CCodeFormalParameter ("other_index", "int32_t")); + value_equal_fun.block = new CCodeBlock (); + var val = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("value"), new CCodeIdentifier ("value_index")); + var other = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("other"), new CCodeIdentifier ("other_index")); + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_equal".printf (cl.get_lower_case_cname ()))); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, val)); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, other)); + value_equal_fun.block.add_statement (new CCodeReturnStatement (ccall)); + source_type_member_definition.append (value_equal_fun); + + declare_set_value_equal_function (source_declarations); + + var value_equal_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_equal")); + value_equal_call.add_argument (new CCodeIdentifier ("type")); + value_equal_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("%s_value_equal".printf (cl.get_lower_case_cname ())), "bool (*)(void *, int32_t, void *, int32_t)")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (value_equal_call)); + } + + if (cl.scope.lookup ("hash") is Method) { + var value_hash_fun = new CCodeFunction ("%s_value_hash".printf (cl.get_lower_case_cname ()), "int32_t"); + value_hash_fun.modifiers = CCodeModifiers.STATIC; + value_hash_fun.add_parameter (new CCodeFormalParameter ("value", cl.get_cname () + "**")); + value_hash_fun.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + value_hash_fun.block = new CCodeBlock (); + var val = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("value"), new CCodeIdentifier ("value_index")); + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_hash".printf (cl.get_lower_case_cname ()))); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, val)); + value_hash_fun.block.add_statement (new CCodeReturnStatement (ccall)); + source_type_member_definition.append (value_hash_fun); + + declare_set_value_hash_function (source_declarations); + + var value_hash_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_hash")); + value_hash_call.add_argument (new CCodeIdentifier ("type")); + value_hash_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("%s_value_hash".printf (cl.get_lower_case_cname ())), "int32_t (*)(void *, int32_t)")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (value_hash_call)); + } + + source_type_member_definition.append (type_init_fun); + + cl.accept_children (codegen); + + current_symbol = old_symbol; + } + + public override void visit_creation_method (CreationMethod m) { + if (current_type_symbol is Class && + (current_class.base_class == null || + current_class.base_class.get_full_name () != "Dova.Value")) { + base.visit_creation_method (m); + return; + } + + visit_method (m); + } + + public override void generate_struct_declaration (Struct st, CCodeDeclarationSpace decl_space) { + base.generate_struct_declaration (st, decl_space); + + if (decl_space.add_symbol_declaration (st, st.get_copy_function ())) { + return; + } + + decl_space.add_include ("stdint.h"); + + generate_class_declaration (type_class, decl_space); + + var type_fun = new CCodeFunction ("%s_type_get".printf (st.get_lower_case_cname ()), "DovaType *"); + if (st.access == SymbolAccessibility.PRIVATE) { + type_fun.modifiers = CCodeModifiers.STATIC; + } + decl_space.add_type_member_declaration (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (st.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + if (st.access == SymbolAccessibility.PRIVATE) { + type_init_fun.modifiers = CCodeModifiers.STATIC; + } + decl_space.add_type_member_declaration (type_init_fun); + + var function = new CCodeFunction (st.get_copy_function (), "void"); + if (st.access == SymbolAccessibility.PRIVATE) { + function.modifiers = CCodeModifiers.STATIC; + } + + function.add_parameter (new CCodeFormalParameter ("dest", st.get_cname () + "*")); + function.add_parameter (new CCodeFormalParameter ("dest_index", "int32_t")); + function.add_parameter (new CCodeFormalParameter ("src", st.get_cname () + "*")); + function.add_parameter (new CCodeFormalParameter ("src_index", "int32_t")); + + decl_space.add_type_member_declaration (function); + } + + public override void visit_struct (Struct st) { + base.visit_struct (st); + + source_declarations.add_include ("stddef.h"); + // calloc + source_declarations.add_include ("stdlib.h"); + + var cdecl = new CCodeDeclaration ("DovaType *"); + cdecl.add_declarator (new CCodeVariableDeclarator ("%s_type".printf (st.get_lower_case_cname ()), new CCodeConstant ("NULL"))); + cdecl.modifiers = CCodeModifiers.STATIC; + source_declarations.add_type_member_declaration (cdecl); + + var type_fun = new CCodeFunction ("%s_type_get".printf (st.get_lower_case_cname ()), "DovaType *"); + type_fun.block = new CCodeBlock (); + + var type_init_block = new CCodeBlock (); + + generate_method_declaration ((Method) object_class.scope.lookup ("alloc"), source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("base_type")).get_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("base_type")).set_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("object_size")).get_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("object_size")).set_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("type_size")).get_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("type_size")).set_accessor, source_declarations); + generate_property_accessor_declaration (((Property) type_class.scope.lookup ("value_size")).set_accessor, source_declarations); + + generate_class_declaration ((Class) context.root.scope.lookup ("Dova").scope.lookup ("Value"), source_declarations); + + var base_type = new CCodeFunctionCall (new CCodeIdentifier ("dova_value_type_get")); + + var base_type_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_type_size")); + base_type_size.add_argument (base_type); + + var calloc_call = new CCodeFunctionCall (new CCodeIdentifier ("calloc")); + calloc_call.add_argument (new CCodeConstant ("1")); + calloc_call.add_argument (base_type_size); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ())), calloc_call))); + + generate_class_declaration ((Class) object_class, source_declarations); + + type_init_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeMemberAccess.pointer (new CCodeCastExpression (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ())), "DovaObject *"), "type"), new CCodeFunctionCall (new CCodeIdentifier ("dova_type_type_get"))))); + + var set_base_type = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_base_type")); + set_base_type.add_argument (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ()))); + set_base_type.add_argument (base_type); + type_init_block.add_statement (new CCodeExpressionStatement (set_base_type)); + + var base_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_get_object_size")); + base_size.add_argument (base_type); + + var sizeof_call = new CCodeFunctionCall (new CCodeIdentifier ("sizeof")); + sizeof_call.add_argument (new CCodeIdentifier (st.get_cname ())); + var set_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_object_size")); + set_size.add_argument (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ()))); + set_size.add_argument (new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, base_size, sizeof_call)); + type_init_block.add_statement (new CCodeExpressionStatement (set_size)); + + set_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_size")); + set_size.add_argument (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ()))); + set_size.add_argument (sizeof_call); + type_init_block.add_statement (new CCodeExpressionStatement (set_size)); + + set_size = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_type_size")); + set_size.add_argument (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ()))); + set_size.add_argument (base_type_size); + type_init_block.add_statement (new CCodeExpressionStatement (set_size)); + + var type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_type_init".printf (st.get_lower_case_cname ()))); + type_init_call.add_argument (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ()))); + type_init_block.add_statement (new CCodeExpressionStatement (type_init_call)); + + type_fun.block.add_statement (new CCodeIfStatement (new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ()))), type_init_block)); + + type_fun.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("%s_type".printf (st.get_lower_case_cname ())))); + + source_type_member_definition.append (type_fun); + + var type_init_fun = new CCodeFunction ("%s_type_init".printf (st.get_lower_case_cname ())); + type_init_fun.add_parameter (new CCodeFormalParameter ("type", "DovaType *")); + type_init_fun.block = new CCodeBlock (); + + type_init_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_value_type_init")); + type_init_call.add_argument (new CCodeIdentifier ("type")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (type_init_call)); + + declare_set_value_copy_function (source_declarations); + + var value_copy_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_copy")); + value_copy_call.add_argument (new CCodeIdentifier ("type")); + value_copy_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("%s_copy".printf (st.get_lower_case_cname ())), "void (*)(void *, int32_t, void *, int32_t)")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (value_copy_call)); + + if (st.scope.lookup ("equal") is Method) { + var value_equal_fun = new CCodeFunction ("%s_value_equal".printf (st.get_lower_case_cname ()), "bool"); + value_equal_fun.modifiers = CCodeModifiers.STATIC; + value_equal_fun.add_parameter (new CCodeFormalParameter ("value", st.get_cname () + "*")); + value_equal_fun.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + value_equal_fun.add_parameter (new CCodeFormalParameter ("other", st.get_cname () + "*")); + value_equal_fun.add_parameter (new CCodeFormalParameter ("other_index", "int32_t")); + value_equal_fun.block = new CCodeBlock (); + var val = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("value"), new CCodeIdentifier ("value_index")); + var other = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("other"), new CCodeIdentifier ("other_index")); + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_equal".printf (st.get_lower_case_cname ()))); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, val)); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, other)); + value_equal_fun.block.add_statement (new CCodeReturnStatement (ccall)); + source_type_member_definition.append (value_equal_fun); + + declare_set_value_equal_function (source_declarations); + + var value_equal_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_equal")); + value_equal_call.add_argument (new CCodeIdentifier ("type")); + value_equal_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("%s_value_equal".printf (st.get_lower_case_cname ())), "bool (*)(void *, int32_t, void *, int32_t)")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (value_equal_call)); + } + + if (st.scope.lookup ("hash") is Method) { + var value_hash_fun = new CCodeFunction ("%s_value_hash".printf (st.get_lower_case_cname ()), "int32_t"); + value_hash_fun.modifiers = CCodeModifiers.STATIC; + value_hash_fun.add_parameter (new CCodeFormalParameter ("value", st.get_cname () + "*")); + value_hash_fun.add_parameter (new CCodeFormalParameter ("value_index", "int32_t")); + value_hash_fun.block = new CCodeBlock (); + var val = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("value"), new CCodeIdentifier ("value_index")); + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("%s_hash".printf (st.get_lower_case_cname ()))); + ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, val)); + value_hash_fun.block.add_statement (new CCodeReturnStatement (ccall)); + source_type_member_definition.append (value_hash_fun); + + declare_set_value_hash_function (source_declarations); + + var value_hash_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_set_value_hash")); + value_hash_call.add_argument (new CCodeIdentifier ("type")); + value_hash_call.add_argument (new CCodeCastExpression (new CCodeIdentifier ("%s_value_hash".printf (st.get_lower_case_cname ())), "int32_t (*)(void *, int32_t)")); + type_init_fun.block.add_statement (new CCodeExpressionStatement (value_hash_call)); + } + + source_type_member_definition.append (type_init_fun); + + add_struct_copy_function (st); + } + + void add_struct_copy_function (Struct st) { + var function = new CCodeFunction (st.get_copy_function (), "void"); + if (st.access == SymbolAccessibility.PRIVATE) { + function.modifiers = CCodeModifiers.STATIC; + } + + function.add_parameter (new CCodeFormalParameter ("dest", st.get_cname () + "*")); + function.add_parameter (new CCodeFormalParameter ("dest_index", "int32_t")); + function.add_parameter (new CCodeFormalParameter ("src", st.get_cname () + "*")); + function.add_parameter (new CCodeFormalParameter ("src_index", "int32_t")); + + var cblock = new CCodeBlock (); + var cfrag = new CCodeFragment (); + cblock.add_statement (cfrag); + + var dest = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("dest"), new CCodeIdentifier ("dest_index")); + var src = new CCodeBinaryExpression (CCodeBinaryOperator.PLUS, new CCodeIdentifier ("src"), new CCodeIdentifier ("src_index")); + + foreach (var f in st.get_fields ()) { + if (f.binding == MemberBinding.INSTANCE) { + var field = new CCodeMemberAccess.pointer (dest, f.name); + + var array_type = f.field_type as ArrayType; + if (array_type != null && array_type.fixed_length) { + for (int i = 0; i < array_type.length; i++) { + var element = new CCodeElementAccess (field, new CCodeConstant (i.to_string ())); + + if (requires_destroy (array_type.element_type)) { + cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (element, array_type.element_type))); + } + } + continue; + } + + if (requires_destroy (f.field_type)) { + var this_access = new MemberAccess.simple ("this"); + this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol); + this_access.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest); + var ma = new MemberAccess (this_access, f.name); + ma.symbol_reference = f; + ma.value_type = f.field_type.copy (); + cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (field, f.field_type, ma))); + } + } + } + + var copy_block = new CCodeBlock (); + + if (st.get_fields ().size == 0) { + copy_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, dest), new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, src)))); + } else { + foreach (var f in st.get_fields ()) { + if (f.binding == MemberBinding.INSTANCE) { + CCodeExpression copy = new CCodeMemberAccess.pointer (src, f.name); + var dest_field = new CCodeMemberAccess.pointer (dest, f.name); + + var array_type = f.field_type as ArrayType; + if (array_type != null && array_type.fixed_length) { + for (int i = 0; i < array_type.length; i++) { + CCodeExpression copy_element = new CCodeElementAccess (copy, new CCodeConstant (i.to_string ())); + var dest_field_element = new CCodeElementAccess (dest_field, new CCodeConstant (i.to_string ())); + + if (requires_copy (array_type.element_type)) { + copy_element = get_ref_cexpression (array_type.element_type, copy_element, null, f); + } + + copy_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (dest_field_element, copy_element))); + } + continue; + } + + if (requires_copy (f.field_type)) { + var this_access = new MemberAccess.simple ("this"); + this_access.value_type = get_data_type_for_symbol ((TypeSymbol) f.parent_symbol); + this_access.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, src); + var ma = new MemberAccess (this_access, f.name); + ma.symbol_reference = f; + copy = get_ref_cexpression (f.field_type, copy, ma, f); + } + + copy_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (dest_field, copy))); + } + } + } + + cblock.add_statement (new CCodeIfStatement (new CCodeIdentifier ("src"), copy_block)); + + append_temp_decl (cfrag, temp_vars); + temp_vars.clear (); + + function.block = cblock; + + source_type_member_definition.append (function); + } + + public override void visit_assignment (Assignment assignment) { + var generic_type = assignment.left.value_type as GenericType; + if (generic_type == null) { + base.visit_assignment (assignment); + return; + } + + var dest = assignment.left; + CCodeExpression cdest; + CCodeExpression dest_index = new CCodeConstant ("0"); + var src = assignment.right; + CCodeExpression csrc; + CCodeExpression src_index = new CCodeConstant ("0"); + + if (src is NullLiteral) { + // TODO destroy dest + assignment.ccodenode = new CCodeConstant ("0"); + return; + } + + var dest_ea = dest as ElementAccess; + var src_ea = src as ElementAccess; + + if (dest_ea != null) { + dest = dest_ea.container; + + var array_type = dest.value_type as ArrayType; + if (array_type != null && !array_type.inline_allocated) { + generate_property_accessor_declaration (((Property) array_class.scope.lookup ("data")).get_accessor, source_declarations); + + var data_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_data")); + data_call.add_argument ((CCodeExpression) get_ccodenode (dest)); + cdest = data_call; + } else { + cdest = (CCodeExpression) get_ccodenode (dest); + } + dest_index = (CCodeExpression) get_ccodenode (dest_ea.get_indices ().get (0)); + } else { + cdest = (CCodeExpression) get_ccodenode (dest); + } + + if (src_ea != null) { + src = src_ea.container; + + var array_type = src.value_type as ArrayType; + if (array_type != null && !array_type.inline_allocated) { + generate_property_accessor_declaration (((Property) array_class.scope.lookup ("data")).get_accessor, source_declarations); + + var data_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_data")); + data_call.add_argument ((CCodeExpression) get_ccodenode (src)); + csrc = data_call; + } else { + csrc = (CCodeExpression) get_ccodenode (src); + } + src_index = (CCodeExpression) get_ccodenode (src_ea.get_indices ().get (0)); + } else { + csrc = (CCodeExpression) get_ccodenode (src); + } + + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_value_copy")); + if (generic_type.type_parameter.parent_symbol is TypeSymbol) { + // generic type + ccall.add_argument (new CCodeMemberAccess.pointer (get_type_private_from_type ((ObjectTypeSymbol) generic_type.type_parameter.parent_symbol, new CCodeMemberAccess.pointer (new CCodeIdentifier ("this"), "type")), "%s_type".printf (generic_type.type_parameter.name.down ()))); + } else { + // generic method + ccall.add_argument (new CCodeIdentifier ("%s_type".printf (generic_type.type_parameter.name.down ()))); + } + ccall.add_argument (cdest); + ccall.add_argument (dest_index); + ccall.add_argument (csrc); + ccall.add_argument (src_index); + assignment.ccodenode = ccall; + } + + public override void visit_binary_expression (BinaryExpression expr) { + var generic_type = expr.left.value_type as GenericType; + if (generic_type == null) { + base.visit_binary_expression (expr); + return; + } + + CCodeExpression cleft; + CCodeExpression left_index = new CCodeConstant ("0"); + CCodeExpression cright; + CCodeExpression right_index = new CCodeConstant ("0"); + + var left_ea = expr.left as ElementAccess; + var right_ea = expr.right as ElementAccess; + + if (left_ea != null) { + generate_property_accessor_declaration (((Property) array_class.scope.lookup ("data")).get_accessor, source_declarations); + + var data_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_data")); + data_call.add_argument ((CCodeExpression) get_ccodenode (left_ea.container)); + cleft = data_call; + left_index = (CCodeExpression) get_ccodenode (left_ea.get_indices ().get (0)); + } else { + cleft = (CCodeExpression) get_ccodenode (expr.left); + } + + if (right_ea != null) { + generate_property_accessor_declaration (((Property) array_class.scope.lookup ("data")).get_accessor, source_declarations); + + var data_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_data")); + data_call.add_argument ((CCodeExpression) get_ccodenode (right_ea.container)); + cright = data_call; + right_index = (CCodeExpression) get_ccodenode (right_ea.get_indices ().get (0)); + } else { + cright = (CCodeExpression) get_ccodenode (expr.right); + } + + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_value_equal")); + ccall.add_argument (get_type_id_expression (generic_type)); + ccall.add_argument (cleft); + ccall.add_argument (left_index); + ccall.add_argument (cright); + ccall.add_argument (right_index); + + if (expr.operator == BinaryOperator.EQUALITY) { + expr.ccodenode = ccall; + } else { + expr.ccodenode = new CCodeUnaryExpression (CCodeUnaryOperator.LOGICAL_NEGATION, ccall); + } + } + + public override void visit_method_call (MethodCall expr) { + var ma = expr.call as MemberAccess; + if (ma == null || ma.inner == null || !(ma.inner.value_type is GenericType)) { + base.visit_method_call (expr); + return; + } + + // handle method calls on generic types + + expr.accept_children (codegen); + + if (ma.member_name == "hash") { + var val = ma.inner; + CCodeExpression cval; + CCodeExpression val_index = new CCodeConstant ("0"); + + var val_ea = val as ElementAccess; + if (val_ea != null) { + val = val_ea.container; + + generate_property_accessor_declaration (((Property) array_class.scope.lookup ("data")).get_accessor, source_declarations); + + var data_call = new CCodeFunctionCall (new CCodeIdentifier ("dova_array_get_data")); + data_call.add_argument ((CCodeExpression) get_ccodenode (val)); + cval = data_call; + val_index = (CCodeExpression) get_ccodenode (val_ea.get_indices ().get (0)); + } else { + cval = (CCodeExpression) get_ccodenode (val); + } + + var ccall = new CCodeFunctionCall (new CCodeIdentifier ("dova_type_value_hash")); + ccall.add_argument (get_type_id_expression (ma.inner.value_type)); + ccall.add_argument (cval); + ccall.add_argument (val_index); + + expr.ccodenode = ccall; + } + } + + public override void visit_list_literal (ListLiteral expr) { + expr.accept_children (codegen); + + var array_type = new ArrayType (expr.element_type, 1, expr.source_reference); + array_type.inline_allocated = true; + array_type.fixed_length = true; + array_type.length = expr.get_expressions ().size; + + var ce = new CCodeCommaExpression (); + var temp_var = get_temp_variable (array_type, true, expr); + var name_cnode = get_variable_cexpression (temp_var.name); + + temp_vars.insert (0, temp_var); + + int i = 0; + foreach (Expression e in expr.get_expressions ()) { + ce.append_expression (new CCodeAssignment (new CCodeElementAccess (name_cnode, new CCodeConstant (i.to_string ())), (CCodeExpression) e.ccodenode)); + i++; + } + + ce.append_expression (name_cnode); + + var list_creation = new CCodeFunctionCall (new CCodeIdentifier ("dova_list_new")); + list_creation.add_argument (get_type_id_expression (expr.element_type)); + list_creation.add_argument (new CCodeConstant (array_type.length.to_string ())); + list_creation.add_argument (ce); + + expr.ccodenode = list_creation; + } + + public override void visit_set_literal (SetLiteral expr) { + expr.accept_children (codegen); + + var array_type = new ArrayType (expr.element_type, 1, expr.source_reference); + array_type.inline_allocated = true; + array_type.fixed_length = true; + array_type.length = expr.get_expressions ().size; + + var ce = new CCodeCommaExpression (); + var temp_var = get_temp_variable (array_type, true, expr); + var name_cnode = get_variable_cexpression (temp_var.name); + + temp_vars.insert (0, temp_var); + + int i = 0; + foreach (Expression e in expr.get_expressions ()) { + ce.append_expression (new CCodeAssignment (new CCodeElementAccess (name_cnode, new CCodeConstant (i.to_string ())), (CCodeExpression) e.ccodenode)); + i++; + } + + ce.append_expression (name_cnode); + + var set_creation = new CCodeFunctionCall (new CCodeIdentifier ("dova_set_new")); + set_creation.add_argument (get_type_id_expression (expr.element_type)); + set_creation.add_argument (new CCodeConstant (array_type.length.to_string ())); + set_creation.add_argument (ce); + + expr.ccodenode = set_creation; + } + + public override void visit_map_literal (MapLiteral expr) { + expr.accept_children (codegen); + + var key_array_type = new ArrayType (expr.map_key_type, 1, expr.source_reference); + key_array_type.inline_allocated = true; + key_array_type.fixed_length = true; + key_array_type.length = expr.get_keys ().size; + + var key_ce = new CCodeCommaExpression (); + var key_temp_var = get_temp_variable (key_array_type, true, expr); + var key_name_cnode = get_variable_cexpression (key_temp_var.name); + + temp_vars.insert (0, key_temp_var); + + var value_array_type = new ArrayType (expr.map_value_type, 1, expr.source_reference); + value_array_type.inline_allocated = true; + value_array_type.fixed_length = true; + value_array_type.length = expr.get_values ().size; + + var value_ce = new CCodeCommaExpression (); + var value_temp_var = get_temp_variable (value_array_type, true, expr); + var value_name_cnode = get_variable_cexpression (value_temp_var.name); + + temp_vars.insert (0, value_temp_var); + + for (int i = 0; i < expr.get_keys ().size; i++) { + key_ce.append_expression (new CCodeAssignment (new CCodeElementAccess (key_name_cnode, new CCodeConstant (i.to_string ())), (CCodeExpression) expr.get_keys ().get (i).ccodenode)); + value_ce.append_expression (new CCodeAssignment (new CCodeElementAccess (value_name_cnode, new CCodeConstant (i.to_string ())), (CCodeExpression) expr.get_values ().get (i).ccodenode)); + } + + key_ce.append_expression (key_name_cnode); + value_ce.append_expression (value_name_cnode); + + var map_creation = new CCodeFunctionCall (new CCodeIdentifier ("dova_map_new")); + map_creation.add_argument (get_type_id_expression (expr.map_key_type)); + map_creation.add_argument (get_type_id_expression (expr.map_value_type)); + map_creation.add_argument (new CCodeConstant (key_array_type.length.to_string ())); + map_creation.add_argument (key_ce); + map_creation.add_argument (value_ce); + + expr.ccodenode = map_creation; + } + + public override void visit_tuple (Tuple tuple) { + tuple.accept_children (codegen); + + var type_array_type = new ArrayType (new PointerType (new VoidType ()), 1, tuple.source_reference); + type_array_type.inline_allocated = true; + type_array_type.fixed_length = true; + type_array_type.length = tuple.get_expressions ().size; + + var type_temp_var = get_temp_variable (type_array_type, true, tuple); + var type_name_cnode = get_variable_cexpression (type_temp_var.name); + temp_vars.insert (0, type_temp_var); + + var array_type = new ArrayType (new PointerType (new VoidType ()), 1, tuple.source_reference); + array_type.inline_allocated = true; + array_type.fixed_length = true; + array_type.length = tuple.get_expressions ().size; + + var temp_var = get_temp_variable (array_type, true, tuple); + var name_cnode = get_variable_cexpression (temp_var.name); + temp_vars.insert (0, temp_var); + + var type_ce = new CCodeCommaExpression (); + var ce = new CCodeCommaExpression (); + + int i = 0; + foreach (Expression e in tuple.get_expressions ()) { + var element_type = tuple.value_type.get_type_arguments ().get (i); + + type_ce.append_expression (new CCodeAssignment (new CCodeElementAccess (type_name_cnode, new CCodeConstant (i.to_string ())), get_type_id_expression (element_type))); + + var cexpr = (CCodeExpression) e.ccodenode; + + var unary = cexpr as CCodeUnaryExpression; + if (unary != null && unary.operator == CCodeUnaryOperator.POINTER_INDIRECTION) { + // *expr => expr + cexpr = unary.inner; + } else if (cexpr is CCodeIdentifier || cexpr is CCodeMemberAccess) { + cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, cexpr); + } else { + // if cexpr is e.g. a function call, we can't take the address of the expression + // tmp = expr, &tmp + + var element_temp_var = get_temp_variable (element_type); + temp_vars.insert (0, element_temp_var); + ce.append_expression (new CCodeAssignment (get_variable_cexpression (element_temp_var.name), cexpr)); + cexpr = new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier (element_temp_var.name)); + } + + ce.append_expression (new CCodeAssignment (new CCodeElementAccess (name_cnode, new CCodeConstant (i.to_string ())), cexpr)); + + i++; + } + + type_ce.append_expression (type_name_cnode); + ce.append_expression (name_cnode); + + var tuple_creation = new CCodeFunctionCall (new CCodeIdentifier ("dova_tuple_new")); + tuple_creation.add_argument (new CCodeConstant (tuple.get_expressions ().size.to_string ())); + tuple_creation.add_argument (type_ce); + tuple_creation.add_argument (ce); + + tuple.ccodenode = tuple_creation; + } +} |