Fix crash with virtual async methods
[vala-lang.git] / codegen / valaccodemethodmodule.vala
blob281c44acf2044597d075f92be021403f860264f5
1 /* valaccodemethodmodule.vala
3 * Copyright (C) 2007-2009 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
21 * Raffaele Sandrini <raffaele@sandrini.ch>
24 using GLib;
25 using Gee;
27 /**
28 * The link between a method and generated code.
30 internal class Vala.CCodeMethodModule : CCodeStructModule {
31 public CCodeMethodModule (CCodeGenerator codegen, CCodeModule? next) {
32 base (codegen, next);
35 public override bool method_has_wrapper (Method method) {
36 return (method.get_attribute ("NoWrapper") == null);
39 public override string? get_custom_creturn_type (Method m) {
40 var attr = m.get_attribute ("CCode");
41 if (attr != null) {
42 string type = attr.get_string ("type");
43 if (type != null) {
44 return type;
47 return null;
50 string get_creturn_type (Method m, string default_value) {
51 string type = get_custom_creturn_type (m);
52 if (type == null) {
53 return default_value;
55 return type;
58 bool is_gtypeinstance_creation_method (Method m) {
59 bool result = false;
61 var cl = m.parent_symbol as Class;
62 if (m is CreationMethod && cl != null && !cl.is_compact) {
63 result = true;
66 return result;
69 public virtual void generate_method_result_declaration (Method m, CCodeDeclarationSpace decl_space, CCodeFunction cfunc, Map<int,CCodeFormalParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
70 var creturn_type = m.return_type;
71 if (m is CreationMethod) {
72 var cl = m.parent_symbol as Class;
73 if (cl != null) {
74 // object creation methods return the new object in C
75 // in Vala they have no return type
76 creturn_type = new ObjectType (cl);
79 cfunc.return_type = get_creturn_type (m, creturn_type.get_cname ());
81 generate_type_declaration (m.return_type, decl_space);
83 if (!m.no_array_length && m.return_type is ArrayType) {
84 // return array length if appropriate
85 var array_type = (ArrayType) m.return_type;
87 for (int dim = 1; dim <= array_type.rank; dim++) {
88 var cparam = new CCodeFormalParameter (head.get_array_length_cname ("result", dim), "int*");
89 cparam_map.set (get_param_pos (m.carray_length_parameter_position + 0.01 * dim), cparam);
90 if (carg_map != null) {
91 carg_map.set (get_param_pos (m.carray_length_parameter_position + 0.01 * dim), get_variable_cexpression (cparam.name));
94 } else if (m.return_type is DelegateType) {
95 // return delegate target if appropriate
96 var deleg_type = (DelegateType) m.return_type;
97 var d = deleg_type.delegate_symbol;
98 if (d.has_target) {
99 var cparam = new CCodeFormalParameter (get_delegate_target_cname ("result"), "void**");
100 cparam_map.set (get_param_pos (m.cdelegate_target_parameter_position), cparam);
101 if (carg_map != null) {
102 carg_map.set (get_param_pos (m.cdelegate_target_parameter_position), get_variable_cexpression (cparam.name));
107 if (m.get_error_types ().size > 0 || (m.base_method != null && m.base_method.get_error_types ().size > 0)) {
108 foreach (DataType error_type in m.get_error_types ()) {
109 generate_type_declaration (error_type, decl_space);
112 var cparam = new CCodeFormalParameter ("error", "GError**");
113 cparam_map.set (get_param_pos (-1), cparam);
114 if (carg_map != null) {
115 carg_map.set (get_param_pos (-1), new CCodeIdentifier (cparam.name));
120 public CCodeStatement complete_async () {
121 var complete_block = new CCodeBlock ();
123 var direct_block = new CCodeBlock ();
124 var direct_call = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_complete"));
125 var async_result_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_async_result");
126 direct_call.add_argument (async_result_expr);
127 direct_block.add_statement (new CCodeExpressionStatement (direct_call));
129 var idle_block = new CCodeBlock ();
130 var idle_call = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_complete_in_idle"));
131 idle_call.add_argument (async_result_expr);
132 idle_block.add_statement (new CCodeExpressionStatement (idle_call));
134 var state = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "state");
135 var zero = new CCodeConstant ("0");
136 var state_is_zero = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, state, zero);
137 var dispatch = new CCodeIfStatement (state_is_zero, idle_block, direct_block);
138 complete_block.add_statement (dispatch);
140 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
141 unref.add_argument (async_result_expr);
142 complete_block.add_statement (new CCodeExpressionStatement (unref));
144 complete_block.add_statement (new CCodeReturnStatement (new CCodeConstant ("FALSE")));
146 return complete_block;
149 public override void generate_method_declaration (Method m, CCodeDeclarationSpace decl_space) {
150 if (m.is_async_callback) {
151 return;
153 if (decl_space.add_symbol_declaration (m, m.get_cname ())) {
154 return;
157 var function = new CCodeFunction (m.get_cname ());
159 if (m.is_private_symbol ()) {
160 function.modifiers |= CCodeModifiers.STATIC;
161 if (m.is_inline) {
162 function.modifiers |= CCodeModifiers.INLINE;
166 var cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
167 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
169 var cl = m.parent_symbol as Class;
171 // do not generate _new functions for creation methods of abstract classes
172 if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
173 generate_cparameters (m, decl_space, cparam_map, function, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")));
175 decl_space.add_type_member_declaration (function);
178 if (m is CreationMethod && cl != null) {
179 // _construct function
180 function = new CCodeFunction (m.get_real_cname ());
182 if (m.is_private_symbol ()) {
183 function.modifiers |= CCodeModifiers.STATIC;
186 cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
187 generate_cparameters (m, decl_space, cparam_map, function);
189 decl_space.add_type_member_declaration (function);
193 public override void visit_method (Method m) {
194 var old_symbol = current_symbol;
195 bool old_method_inner_error = current_method_inner_error;
196 int old_next_temp_var_id = next_temp_var_id;
197 var old_temp_vars = temp_vars;
198 var old_temp_ref_vars = temp_ref_vars;
199 var old_variable_name_map = variable_name_map;
200 var old_try = current_try;
201 current_symbol = m;
202 current_method_inner_error = false;
203 next_temp_var_id = 0;
204 temp_vars = new ArrayList<LocalVariable> ();
205 temp_ref_vars = new ArrayList<LocalVariable> ();
206 variable_name_map = new HashMap<string,string> (str_hash, str_equal);
207 current_try = null;
209 bool in_gobject_creation_method = false;
210 bool in_fundamental_creation_method = false;
212 check_type (m.return_type);
214 if (m is CreationMethod) {
215 var cl = current_type_symbol as Class;
216 if (cl != null && !cl.is_compact) {
217 if (cl.base_class == null) {
218 in_fundamental_creation_method = true;
219 } else if (gobject_type != null && cl.is_subtype_of (gobject_type)) {
220 in_gobject_creation_method = true;
225 var creturn_type = current_return_type;
227 if (m.binding == MemberBinding.CLASS || m.binding == MemberBinding.STATIC) {
228 in_static_or_class_context = true;
230 m.accept_children (codegen);
231 in_static_or_class_context = false;
233 if (m is CreationMethod) {
234 if (in_gobject_creation_method && m.body != null) {
235 var cblock = new CCodeBlock ();
237 // last property assignment statement
238 CodeNode last_stmt = null;
240 if (!((CreationMethod) m).chain_up) {
241 // set construct properties
242 foreach (CodeNode stmt in m.body.get_statements ()) {
243 var expr_stmt = stmt as ExpressionStatement;
244 if (expr_stmt != null) {
245 var prop = expr_stmt.assigned_property ();
246 if (prop != null && prop.set_accessor.construction) {
247 last_stmt = stmt;
251 if (last_stmt != null) {
252 foreach (CodeNode stmt in m.body.get_statements ()) {
253 if (stmt.ccodenode is CCodeFragment) {
254 foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) {
255 cblock.add_statement (cstmt);
257 } else {
258 cblock.add_statement (stmt.ccodenode);
260 if (last_stmt == stmt) {
261 break;
266 add_object_creation (cblock, ((CreationMethod) m).n_construction_params > 0 || current_class.get_type_parameters ().size > 0);
267 } else {
268 var cdeclaration = new CCodeDeclaration ("%s *".printf (((Class) current_type_symbol).get_cname ()));
269 cdeclaration.add_declarator (new CCodeVariableDeclarator ("self"));
271 cblock.add_statement (cdeclaration);
274 // other initialization code
275 foreach (CodeNode stmt in m.body.get_statements ()) {
276 if (last_stmt != null) {
277 if (last_stmt == stmt) {
278 last_stmt = null;
280 continue;
282 if (stmt.ccodenode is CCodeFragment) {
283 foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) {
284 cblock.add_statement (cstmt);
286 } else {
287 cblock.add_statement (stmt.ccodenode);
291 foreach (LocalVariable local in m.body.get_local_variables ()) {
292 if (!local.floating && requires_destroy (local.variable_type)) {
293 var ma = new MemberAccess.simple (local.name);
294 ma.symbol_reference = local;
295 cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)));
299 m.body.ccodenode = cblock;
303 bool inner_error = current_method_inner_error;
305 current_symbol = old_symbol;
306 current_method_inner_error = old_method_inner_error;
307 next_temp_var_id = old_next_temp_var_id;
308 temp_vars = old_temp_vars;
309 temp_ref_vars = old_temp_ref_vars;
310 variable_name_map = old_variable_name_map;
311 current_try = old_try;
313 // do not declare overriding methods and interface implementations
314 if (m.is_abstract || m.is_virtual
315 || (m.base_method == null && m.base_interface_method == null)) {
316 generate_method_declaration (m, source_declarations);
318 if (!m.is_internal_symbol ()) {
319 generate_method_declaration (m, header_declarations);
321 generate_method_declaration (m, internal_header_declarations);
324 function = new CCodeFunction (m.get_real_cname ());
325 m.ccodenode = function;
327 if (m.is_inline) {
328 function.modifiers |= CCodeModifiers.INLINE;
331 var cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
333 generate_cparameters (m, source_declarations, cparam_map, function);
335 // generate *_real_* functions for virtual methods
336 // also generate them for abstract methods of classes to prevent faulty subclassing
337 if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
338 if (!m.coroutine) {
339 if (m.base_method != null || m.base_interface_method != null) {
340 // declare *_real_* function
341 function.modifiers |= CCodeModifiers.STATIC;
342 source_declarations.add_type_member_declaration (function.copy ());
343 } else if (m.is_private_symbol ()) {
344 function.modifiers |= CCodeModifiers.STATIC;
348 /* Methods imported from a plain C file don't
349 * have a body, e.g. Vala.Parser.parse_file () */
350 if (m.body != null) {
351 function.block = (CCodeBlock) m.body.ccodenode;
352 function.block.line = function.line;
354 var cinit = new CCodeFragment ();
355 function.block.prepend_statement (cinit);
357 if (m.coroutine) {
358 var co_function = new CCodeFunction (m.get_real_cname () + "_co", "gboolean");
360 // data struct to hold parameters, local variables, and the return value
361 co_function.add_parameter (new CCodeFormalParameter ("data", Symbol.lower_case_to_camel_case (m.get_cname ()) + "Data*"));
363 co_function.modifiers |= CCodeModifiers.STATIC;
364 source_declarations.add_type_member_declaration (co_function.copy ());
366 var cswitch = new CCodeSwitchStatement (new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "state"));
368 // let gcc know that this can't happen
369 cswitch.add_statement (new CCodeLabel ("default"));
370 cswitch.add_statement (new CCodeExpressionStatement (new CCodeFunctionCall (new CCodeIdentifier ("g_assert_not_reached"))));
372 // initial coroutine state
373 cswitch.add_statement (new CCodeCaseStatement (new CCodeConstant ("0")));
375 // coroutine body
376 cswitch.add_statement (function.block);
378 // epilogue
379 cswitch.add_statement (complete_async ());
381 co_function.block = new CCodeBlock ();
382 co_function.block.add_statement (cswitch);
384 source_type_member_definition.append (co_function);
387 if (m.parent_symbol is Class && !m.coroutine) {
388 var cl = (Class) m.parent_symbol;
389 if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
390 Method base_method;
391 ReferenceType base_expression_type;
392 if (m.overrides) {
393 base_method = m.base_method;
394 base_expression_type = new ObjectType ((Class) base_method.parent_symbol);
395 } else {
396 base_method = m.base_interface_method;
397 base_expression_type = new ObjectType ((Interface) base_method.parent_symbol);
399 var self_target_type = new ObjectType (cl);
400 CCodeExpression cself = transform_expression (new CCodeIdentifier ("base"), base_expression_type, self_target_type);
402 var cdecl = new CCodeDeclaration ("%s *".printf (cl.get_cname ()));
403 cdecl.add_declarator (new CCodeVariableDeclarator ("self", cself));
405 cinit.append (cdecl);
406 } else if (m.binding == MemberBinding.INSTANCE
407 && !(m is CreationMethod)) {
408 var ccheckstmt = create_method_type_check_statement (m, creturn_type, cl, true, "self");
409 if (ccheckstmt != null) {
410 ccheckstmt.line = function.line;
411 cinit.append (ccheckstmt);
415 foreach (FormalParameter param in m.get_parameters ()) {
416 if (param.ellipsis) {
417 break;
420 var t = param.parameter_type.data_type;
421 if (t != null && t.is_reference_type ()) {
422 if (param.direction != ParameterDirection.OUT) {
423 var type_check = create_method_type_check_statement (m, creturn_type, t, !param.parameter_type.nullable, get_variable_cname (param.name));
424 if (type_check != null) {
425 type_check.line = function.line;
426 cinit.append (type_check);
428 } else {
429 // ensure that the passed reference for output parameter is cleared
430 var a = new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (param.name)), new CCodeConstant ("NULL"));
431 var cblock = new CCodeBlock ();
432 cblock.add_statement (new CCodeExpressionStatement (a));
434 var condition = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_variable_cexpression (param.name), new CCodeConstant ("NULL"));
435 var if_statement = new CCodeIfStatement (condition, cblock);
436 cinit.append (if_statement);
441 if (!(m.return_type is VoidType) && !m.coroutine) {
442 var cdecl = new CCodeDeclaration (m.return_type.get_cname ());
443 cdecl.add_declarator (new CCodeVariableDeclarator ("result"));
444 cinit.append (cdecl);
447 if (inner_error) {
448 /* always separate error parameter and inner_error local variable
449 * as error may be set to NULL but we're always interested in inner errors
451 if (m.coroutine) {
452 closure_struct.add_field ("GError *", "_inner_error_");
454 // no initialization necessary, closure struct is zeroed
455 } else {
456 var cdecl = new CCodeDeclaration ("GError *");
457 cdecl.add_declarator (new CCodeVariableDeclarator ("_inner_error_", new CCodeConstant ("NULL")));
458 cinit.append (cdecl);
462 if (!m.coroutine) {
463 source_type_member_definition.append (function);
466 if (m is CreationMethod) {
467 if (in_gobject_creation_method) {
468 int n_params = ((CreationMethod) m).n_construction_params;
470 if (!((CreationMethod) m).chain_up) {
471 if (n_params > 0 || current_class.get_type_parameters ().size > 0) {
472 // declare construction parameter array
473 var cparamsinit = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
474 cparamsinit.add_argument (new CCodeIdentifier ("GParameter"));
475 cparamsinit.add_argument (new CCodeConstant ((n_params + 3 * current_class.get_type_parameters ().size).to_string ()));
477 var cdecl = new CCodeDeclaration ("GParameter *");
478 cdecl.add_declarator (new CCodeVariableDeclarator ("__params", cparamsinit));
479 cinit.append (cdecl);
481 cdecl = new CCodeDeclaration ("GParameter *");
482 cdecl.add_declarator (new CCodeVariableDeclarator ("__params_it", new CCodeIdentifier ("__params")));
483 cinit.append (cdecl);
486 /* type, dup func, and destroy func properties for generic types */
487 foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
488 CCodeConstant prop_name;
489 CCodeIdentifier param_name;
491 prop_name = new CCodeConstant ("\"%s-type\"".printf (type_param.name.down ()));
492 param_name = new CCodeIdentifier ("%s_type".printf (type_param.name.down ()));
493 cinit.append (new CCodeExpressionStatement (get_construct_property_assignment (prop_name, new IntegerType ((Struct) gtype_type), param_name)));
495 prop_name = new CCodeConstant ("\"%s-dup-func\"".printf (type_param.name.down ()));
496 param_name = new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ()));
497 cinit.append (new CCodeExpressionStatement (get_construct_property_assignment (prop_name, new PointerType (new VoidType ()), param_name)));
499 prop_name = new CCodeConstant ("\"%s-destroy-func\"".printf (type_param.name.down ()));
500 param_name = new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ()));
501 cinit.append (new CCodeExpressionStatement (get_construct_property_assignment (prop_name, new PointerType (new VoidType ()), param_name)));
504 } else if (is_gtypeinstance_creation_method (m)) {
505 var cl = (Class) m.parent_symbol;
506 var cdeclaration = new CCodeDeclaration (cl.get_cname () + "*");
507 var cdecl = new CCodeVariableDeclarator ("self");
508 cdeclaration.add_declarator (cdecl);
509 cinit.append (cdeclaration);
511 if (!((CreationMethod) m).chain_up) {
512 // TODO implicitly chain up to base class as in add_object_creation
513 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_create_instance"));
514 ccall.add_argument (new CCodeIdentifier ("object_type"));
515 cdecl.initializer = new CCodeCastExpression (ccall, cl.get_cname () + "*");
517 /* type, dup func, and destroy func fields for generic types */
518 foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
519 CCodeIdentifier param_name;
520 CCodeAssignment assign;
522 var priv_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv");
524 param_name = new CCodeIdentifier ("%s_type".printf (type_param.name.down ()));
525 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
526 cinit.append (new CCodeExpressionStatement (assign));
528 param_name = new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ()));
529 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
530 cinit.append (new CCodeExpressionStatement (assign));
532 param_name = new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ()));
533 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
534 cinit.append (new CCodeExpressionStatement (assign));
537 } else if (current_type_symbol is Class) {
538 var cl = (Class) m.parent_symbol;
539 var cdeclaration = new CCodeDeclaration (cl.get_cname () + "*");
540 var cdecl = new CCodeVariableDeclarator ("self");
541 cdeclaration.add_declarator (cdecl);
542 cinit.append (cdeclaration);
544 if (!((CreationMethod) m).chain_up) {
545 // TODO implicitly chain up to base class as in add_object_creation
546 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
547 ccall.add_argument (new CCodeIdentifier (cl.get_cname ()));
548 cdecl.initializer = ccall;
551 if (cl.base_class == null) {
552 // derived compact classes do not have fields
553 var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (cl.get_lower_case_cname (null))));
554 cinitcall.add_argument (new CCodeIdentifier ("self"));
555 cinit.append (new CCodeExpressionStatement (cinitcall));
557 } else {
558 var st = (Struct) m.parent_symbol;
560 // memset needs string.h
561 source_declarations.add_include ("string.h");
562 var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
563 czero.add_argument (new CCodeIdentifier ("self"));
564 czero.add_argument (new CCodeConstant ("0"));
565 czero.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (st.get_cname ())));
566 cinit.append (new CCodeExpressionStatement (czero));
570 if (context.module_init_method == m && in_plugin) {
571 // GTypeModule-based plug-in, register types
572 cinit.append (module_init_fragment);
575 foreach (Expression precondition in m.get_preconditions ()) {
576 var check_stmt = create_precondition_statement (m, creturn_type, precondition);
577 if (check_stmt != null) {
578 cinit.append (check_stmt);
581 } else if (m.is_abstract) {
582 // generate helpful error message if a sublcass does not implement an abstract method.
583 // This is only meaningful for subclasses implemented in C since the vala compiler would
584 // complain during compile time of such en error.
586 var cblock = new CCodeBlock ();
588 // add a typecheck statement for "self"
589 var check_stmt = create_method_type_check_statement (m, creturn_type, current_type_symbol, true, "self");
590 if (check_stmt != null) {
591 cblock.add_statement (check_stmt);
594 // add critical warning that this method should not have been called
595 var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE"));
596 type_from_instance_call.add_argument (new CCodeIdentifier ("self"));
598 var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name"));
599 type_name_call.add_argument (type_from_instance_call);
601 var error_string = "\"Type `%%s' does not implement abstract method `%s'\"".printf (m.get_cname ());
603 var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
604 cerrorcall.add_argument (new CCodeConstant (error_string));
605 cerrorcall.add_argument (type_name_call);
607 cblock.add_statement (new CCodeExpressionStatement (cerrorcall));
609 // add return statement
610 cblock.add_statement (new CCodeReturnStatement (default_value_for_type (creturn_type, false)));
612 function.block = cblock;
613 source_type_member_definition.append (function);
617 if ((m.is_abstract || m.is_virtual) && !m.coroutine &&
618 /* If the method is a signal handler, the declaration
619 * is not needed. -- the name should be reserved for the
620 * emitter! */
621 m.signal_reference == null) {
623 cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
624 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
626 generate_vfunc (m, creturn_type, cparam_map, carg_map);
629 if (m.entry_point) {
630 // m is possible entry point, add appropriate startup code
631 var cmain = new CCodeFunction ("main", "int");
632 cmain.line = function.line;
633 cmain.add_parameter (new CCodeFormalParameter ("argc", "int"));
634 cmain.add_parameter (new CCodeFormalParameter ("argv", "char **"));
635 var main_block = new CCodeBlock ();
637 if (context.profile == Profile.GOBJECT) {
638 if (context.thread) {
639 var thread_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_thread_init"));
640 thread_init_call.line = cmain.line;
641 thread_init_call.add_argument (new CCodeConstant ("NULL"));
642 main_block.add_statement (new CCodeExpressionStatement (thread_init_call));
645 var type_init_call = new CCodeExpressionStatement (new CCodeFunctionCall (new CCodeIdentifier ("g_type_init")));
646 type_init_call.line = cmain.line;
647 main_block.add_statement (type_init_call);
650 var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name));
651 if (m.get_parameters ().size == 1) {
652 main_call.add_argument (new CCodeIdentifier ("argv"));
653 main_call.add_argument (new CCodeIdentifier ("argc"));
655 if (m.return_type is VoidType) {
656 // method returns void, always use 0 as exit code
657 var main_stmt = new CCodeExpressionStatement (main_call);
658 main_stmt.line = cmain.line;
659 main_block.add_statement (main_stmt);
660 var ret_stmt = new CCodeReturnStatement (new CCodeConstant ("0"));
661 ret_stmt.line = cmain.line;
662 main_block.add_statement (ret_stmt);
663 } else {
664 var main_stmt = new CCodeReturnStatement (main_call);
665 main_stmt.line = cmain.line;
666 main_block.add_statement (main_stmt);
668 cmain.block = main_block;
669 source_type_member_definition.append (cmain);
673 public virtual void generate_parameter (FormalParameter param, CCodeDeclarationSpace decl_space, Map<int,CCodeFormalParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
674 if (!param.ellipsis) {
675 string ctypename = param.parameter_type.get_cname ();
677 generate_type_declaration (param.parameter_type, decl_space);
679 // pass non-simple structs always by reference
680 if (param.parameter_type.data_type is Struct) {
681 var st = (Struct) param.parameter_type.data_type;
682 if (!st.is_simple_type () && param.direction == ParameterDirection.IN) {
683 if (st.use_const && !param.parameter_type.value_owned) {
684 ctypename = "const " + ctypename;
687 if (!param.parameter_type.nullable) {
688 ctypename += "*";
693 if (param.direction != ParameterDirection.IN) {
694 ctypename += "*";
697 param.ccodenode = new CCodeFormalParameter (get_variable_cname (param.name), ctypename);
698 } else {
699 param.ccodenode = new CCodeFormalParameter.with_ellipsis ();
702 cparam_map.set (get_param_pos (param.cparameter_position), (CCodeFormalParameter) param.ccodenode);
703 if (carg_map != null && !param.ellipsis) {
704 carg_map.set (get_param_pos (param.cparameter_position), get_variable_cexpression (param.name));
708 public override void generate_cparameters (Method m, CCodeDeclarationSpace decl_space, Map<int,CCodeFormalParameter> cparam_map, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, Map<int,CCodeExpression>? carg_map = null, CCodeFunctionCall? vcall = null, int direction = 3) {
709 if (m.parent_symbol is Class && m is CreationMethod) {
710 var cl = (Class) m.parent_symbol;
711 if (!cl.is_compact && vcall == null) {
712 cparam_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeFormalParameter ("object_type", "GType"));
714 } else if (m.binding == MemberBinding.INSTANCE || (m.parent_symbol is Struct && m is CreationMethod)) {
715 TypeSymbol parent_type = find_parent_type (m);
716 DataType this_type;
717 if (parent_type is Class) {
718 this_type = new ObjectType ((Class) parent_type);
719 } else if (parent_type is Interface) {
720 this_type = new ObjectType ((Interface) parent_type);
721 } else if (parent_type is Struct) {
722 this_type = new StructValueType ((Struct) parent_type);
723 } else if (parent_type is Enum) {
724 this_type = new EnumValueType ((Enum) parent_type);
725 } else {
726 assert_not_reached ();
729 generate_type_declaration (this_type, decl_space);
731 CCodeFormalParameter instance_param = null;
732 if (m.base_interface_method != null && !m.is_abstract && !m.is_virtual) {
733 var base_type = new ObjectType ((Interface) m.base_interface_method.parent_symbol);
734 instance_param = new CCodeFormalParameter ("base", base_type.get_cname ());
735 } else if (m.overrides) {
736 var base_type = new ObjectType ((Class) m.base_method.parent_symbol);
737 instance_param = new CCodeFormalParameter ("base", base_type.get_cname ());
738 } else {
739 if (m.parent_symbol is Struct && !((Struct) m.parent_symbol).is_simple_type ()) {
740 instance_param = new CCodeFormalParameter ("*self", this_type.get_cname ());
741 } else {
742 instance_param = new CCodeFormalParameter ("self", this_type.get_cname ());
745 cparam_map.set (get_param_pos (m.cinstance_parameter_position), instance_param);
746 } else if (m.binding == MemberBinding.CLASS) {
747 TypeSymbol parent_type = find_parent_type (m);
748 DataType this_type;
749 this_type = new ClassType ((Class) parent_type);
750 var class_param = new CCodeFormalParameter ("klass", this_type.get_cname ());
751 cparam_map.set (get_param_pos (m.cinstance_parameter_position), class_param);
754 if (is_gtypeinstance_creation_method (m)) {
755 // memory management for generic types
756 int type_param_index = 0;
757 var cl = (Class) m.parent_symbol;
758 foreach (TypeParameter type_param in cl.get_type_parameters ()) {
759 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType"));
760 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
761 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
762 if (carg_map != null) {
763 carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
764 carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
765 carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
767 type_param_index++;
769 } else {
770 int type_param_index = 0;
771 if (m.binding != MemberBinding.INSTANCE && m.parent_symbol is ObjectTypeSymbol) {
772 // support static methods in generic types
773 var type_symbol = (ObjectTypeSymbol) m.parent_symbol;
774 foreach (var type_param in type_symbol.get_type_parameters ()) {
775 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType"));
776 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
777 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
778 if (carg_map != null) {
779 carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
780 carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
781 carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
783 type_param_index++;
786 foreach (var type_param in m.get_type_parameters ()) {
787 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType"));
788 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
789 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
790 if (carg_map != null) {
791 carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
792 carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
793 carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
795 type_param_index++;
799 foreach (FormalParameter param in m.get_parameters ()) {
800 if (param.direction != ParameterDirection.OUT) {
801 if ((direction & 1) == 0) {
802 // no in paramters
803 continue;
805 } else {
806 if ((direction & 2) == 0) {
807 // no out paramters
808 continue;
812 generate_parameter (param, decl_space, cparam_map, carg_map);
815 if ((direction & 2) != 0) {
816 generate_method_result_declaration (m, decl_space, func, cparam_map, carg_map);
819 // append C parameters in the right order
820 int last_pos = -1;
821 int min_pos;
822 while (true) {
823 min_pos = -1;
824 foreach (int pos in cparam_map.get_keys ()) {
825 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
826 min_pos = pos;
829 if (min_pos == -1) {
830 break;
832 func.add_parameter (cparam_map.get (min_pos));
833 if (vdeclarator != null) {
834 vdeclarator.add_parameter (cparam_map.get (min_pos));
836 if (vcall != null) {
837 var arg = carg_map.get (min_pos);
838 if (arg != null) {
839 vcall.add_argument (arg);
842 last_pos = min_pos;
846 public void generate_vfunc (Method m, DataType return_type, Map<int,CCodeFormalParameter> cparam_map, Map<int,CCodeExpression> carg_map, string suffix = "", int direction = 3) {
847 var vfunc = new CCodeFunction (m.get_cname () + suffix);
848 if (function != null) {
849 vfunc.line = function.line;
852 var vblock = new CCodeBlock ();
854 foreach (Expression precondition in m.get_preconditions ()) {
855 var check_stmt = create_precondition_statement (m, return_type, precondition);
856 if (check_stmt != null) {
857 vblock.add_statement (check_stmt);
861 CCodeFunctionCall vcast = null;
862 if (m.parent_symbol is Interface) {
863 var iface = (Interface) m.parent_symbol;
865 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null))));
866 } else {
867 var cl = (Class) m.parent_symbol;
869 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null))));
871 vcast.add_argument (new CCodeIdentifier ("self"));
873 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, m.vfunc_name + suffix));
874 carg_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeIdentifier ("self"));
876 generate_cparameters (m, source_declarations, cparam_map, vfunc, null, carg_map, vcall, direction);
878 CCodeStatement cstmt;
879 if (return_type is VoidType) {
880 cstmt = new CCodeExpressionStatement (vcall);
881 } else if (m.get_postconditions ().size == 0) {
882 /* pass method return value */
883 cstmt = new CCodeReturnStatement (vcall);
884 } else {
885 /* store method return value for postconditions */
886 var cdecl = new CCodeDeclaration (get_creturn_type (m, return_type.get_cname ()));
887 cdecl.add_declarator (new CCodeVariableDeclarator ("result", vcall));
888 cstmt = cdecl;
890 cstmt.line = vfunc.line;
891 vblock.add_statement (cstmt);
893 if (m.get_postconditions ().size > 0) {
894 foreach (Expression postcondition in m.get_postconditions ()) {
895 vblock.add_statement (create_postcondition_statement (postcondition));
898 if (!(return_type is VoidType)) {
899 var cret_stmt = new CCodeReturnStatement (new CCodeIdentifier ("result"));
900 cret_stmt.line = vfunc.line;
901 vblock.add_statement (cret_stmt);
905 vfunc.block = vblock;
907 source_type_member_definition.append (vfunc);
910 private CCodeStatement? create_method_type_check_statement (Method m, DataType return_type, TypeSymbol t, bool non_null, string var_name) {
911 if (m.coroutine) {
912 return null;
913 } else {
914 return create_type_check_statement (m, return_type, t, non_null, var_name);
918 private CCodeStatement? create_precondition_statement (CodeNode method_node, DataType ret_type, Expression precondition) {
919 var ccheck = new CCodeFunctionCall ();
921 ccheck.add_argument ((CCodeExpression) precondition.ccodenode);
923 if (ret_type is VoidType) {
924 /* void function */
925 ccheck.call = new CCodeIdentifier ("g_return_if_fail");
926 } else {
927 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
929 var cdefault = default_value_for_type (ret_type, false);
930 if (cdefault != null) {
931 ccheck.add_argument (cdefault);
932 } else {
933 return null;
937 return new CCodeExpressionStatement (ccheck);
940 private TypeSymbol? find_parent_type (Symbol sym) {
941 while (sym != null) {
942 if (sym is TypeSymbol) {
943 return (TypeSymbol) sym;
945 sym = sym.parent_symbol;
947 return null;
950 private void add_object_creation (CCodeBlock b, bool has_params) {
951 var cl = (Class) current_type_symbol;
953 if (!has_params && cl.base_class != gobject_type) {
954 // possibly report warning or error about missing base call
957 var cdecl = new CCodeVariableDeclarator ("self");
958 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_newv"));
959 ccall.add_argument (new CCodeIdentifier ("object_type"));
960 if (has_params) {
961 ccall.add_argument (new CCodeConstant ("__params_it - __params"));
962 ccall.add_argument (new CCodeConstant ("__params"));
963 } else {
964 ccall.add_argument (new CCodeConstant ("0"));
965 ccall.add_argument (new CCodeConstant ("NULL"));
967 cdecl.initializer = ccall;
969 var cdeclaration = new CCodeDeclaration ("%s *".printf (cl.get_cname ()));
970 cdeclaration.add_declarator (cdecl);
972 b.add_statement (cdeclaration);
975 public override void visit_creation_method (CreationMethod m) {
976 bool visible = !m.is_private_symbol ();
978 head.visit_method (m);
980 DataType creturn_type;
981 if (current_type_symbol is Class) {
982 creturn_type = new ObjectType (current_class);
983 } else {
984 creturn_type = new VoidType ();
987 // do not generate _new functions for creation methods of abstract classes
988 if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) {
989 var vfunc = new CCodeFunction (m.get_cname ());
990 vfunc.line = function.line;
992 var cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
993 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
995 var vblock = new CCodeBlock ();
997 var vcall = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
998 vcall.add_argument (new CCodeIdentifier (current_class.get_type_id ()));
1000 generate_cparameters (m, source_declarations, cparam_map, vfunc, null, carg_map, vcall);
1001 CCodeStatement cstmt = new CCodeReturnStatement (vcall);
1002 cstmt.line = vfunc.line;
1003 vblock.add_statement (cstmt);
1005 if (!visible) {
1006 vfunc.modifiers |= CCodeModifiers.STATIC;
1009 vfunc.block = vblock;
1011 source_type_member_definition.append (vfunc);
1014 if (current_type_symbol is Class && gobject_type != null && current_class.is_subtype_of (gobject_type)
1015 && (((CreationMethod) m).n_construction_params > 0 || current_class.get_type_parameters ().size > 0)
1016 && !((CreationMethod) m).chain_up) {
1017 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, new CCodeIdentifier ("__params_it"), new CCodeIdentifier ("__params"));
1018 var cdofreeparam = new CCodeBlock ();
1019 cdofreeparam.add_statement (new CCodeExpressionStatement (new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_DECREMENT, new CCodeIdentifier ("__params_it"))));
1020 var cunsetcall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_unset"));
1021 cunsetcall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("__params_it"), "value")));
1022 cdofreeparam.add_statement (new CCodeExpressionStatement (cunsetcall));
1023 function.block.add_statement (new CCodeWhileStatement (ccond, cdofreeparam));
1025 var cfreeparams = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
1026 cfreeparams.add_argument (new CCodeIdentifier ("__params"));
1027 function.block.add_statement (new CCodeExpressionStatement (cfreeparams));
1030 if (current_type_symbol is Class) {
1031 CCodeExpression cresult = new CCodeIdentifier ("self");
1032 if (get_custom_creturn_type (m) != null) {
1033 cresult = new CCodeCastExpression (cresult, get_custom_creturn_type (m));
1036 var creturn = new CCodeReturnStatement ();
1037 creturn.return_expression = cresult;
1038 function.block.add_statement (creturn);
1043 // vim:sw=8 noet