gio-2.0: Fix g_file_mount_enclosing_volume binding
[vala-lang.git] / codegen / valaccodemethodmodule.vala
blobaa2c1fe4b30224a416b1dcdffc244546c8b3b75d
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);
78 } else if (m.return_type.is_real_non_null_struct_type ()) {
79 // structs are returned via out parameter
80 creturn_type = new VoidType ();
82 cfunc.return_type = get_creturn_type (m, creturn_type.get_cname ());
84 generate_type_declaration (m.return_type, decl_space);
86 if (m.return_type.is_real_non_null_struct_type ()) {
87 // structs are returned via out parameter
88 var cparam = new CCodeFormalParameter ("result", m.return_type.get_cname () + "*");
89 cparam_map.set (get_param_pos (-3), cparam);
90 if (carg_map != null) {
91 carg_map.set (get_param_pos (-3), get_result_cexpression ());
93 } else if (!m.no_array_length && m.return_type is ArrayType) {
94 // return array length if appropriate
95 var array_type = (ArrayType) m.return_type;
97 for (int dim = 1; dim <= array_type.rank; dim++) {
98 var cparam = new CCodeFormalParameter (head.get_array_length_cname ("result", dim), "int*");
99 cparam_map.set (get_param_pos (m.carray_length_parameter_position + 0.01 * dim), cparam);
100 if (carg_map != null) {
101 carg_map.set (get_param_pos (m.carray_length_parameter_position + 0.01 * dim), get_variable_cexpression (cparam.name));
104 } else if (m.return_type is DelegateType) {
105 // return delegate target if appropriate
106 var deleg_type = (DelegateType) m.return_type;
107 var d = deleg_type.delegate_symbol;
108 if (d.has_target) {
109 var cparam = new CCodeFormalParameter (get_delegate_target_cname ("result"), "void**");
110 cparam_map.set (get_param_pos (m.cdelegate_target_parameter_position), cparam);
111 if (carg_map != null) {
112 carg_map.set (get_param_pos (m.cdelegate_target_parameter_position), get_variable_cexpression (cparam.name));
114 if (deleg_type.value_owned) {
115 cparam = new CCodeFormalParameter (get_delegate_target_destroy_notify_cname ("result"), "GDestroyNotify*");
116 cparam_map.set (get_param_pos (m.cdelegate_target_parameter_position + 0.01), cparam);
117 if (carg_map != null) {
118 carg_map.set (get_param_pos (m.cdelegate_target_parameter_position + 0.01), get_variable_cexpression (cparam.name));
124 if (m.get_error_types ().size > 0 || (m.base_method != null && m.base_method.get_error_types ().size > 0)) {
125 foreach (DataType error_type in m.get_error_types ()) {
126 generate_type_declaration (error_type, decl_space);
129 var cparam = new CCodeFormalParameter ("error", "GError**");
130 cparam_map.set (get_param_pos (-1), cparam);
131 if (carg_map != null) {
132 carg_map.set (get_param_pos (-1), new CCodeIdentifier (cparam.name));
137 public CCodeStatement complete_async () {
138 var complete_block = new CCodeBlock ();
140 var direct_block = new CCodeBlock ();
141 var direct_call = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_complete"));
142 var async_result_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_async_result");
143 direct_call.add_argument (async_result_expr);
144 direct_block.add_statement (new CCodeExpressionStatement (direct_call));
146 var idle_block = new CCodeBlock ();
147 var idle_call = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_complete_in_idle"));
148 idle_call.add_argument (async_result_expr);
149 idle_block.add_statement (new CCodeExpressionStatement (idle_call));
151 var state = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_state_");
152 var zero = new CCodeConstant ("0");
153 var state_is_zero = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, state, zero);
154 var dispatch = new CCodeIfStatement (state_is_zero, idle_block, direct_block);
155 complete_block.add_statement (dispatch);
157 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
158 unref.add_argument (async_result_expr);
159 complete_block.add_statement (new CCodeExpressionStatement (unref));
161 complete_block.add_statement (new CCodeReturnStatement (new CCodeConstant ("FALSE")));
163 return complete_block;
166 public override void generate_method_declaration (Method m, CCodeDeclarationSpace decl_space) {
167 if (m.is_async_callback) {
168 return;
170 if (decl_space.add_symbol_declaration (m, m.get_cname ())) {
171 return;
174 var function = new CCodeFunction (m.get_cname ());
176 if (m.is_private_symbol ()) {
177 function.modifiers |= CCodeModifiers.STATIC;
178 if (m.is_inline) {
179 function.modifiers |= CCodeModifiers.INLINE;
183 var cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
184 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
186 var cl = m.parent_symbol as Class;
188 // do not generate _new functions for creation methods of abstract classes
189 if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
190 generate_cparameters (m, decl_space, cparam_map, function, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")));
192 decl_space.add_type_member_declaration (function);
195 if (m is CreationMethod && cl != null) {
196 // _construct function
197 function = new CCodeFunction (m.get_real_cname ());
199 if (m.is_private_symbol ()) {
200 function.modifiers |= CCodeModifiers.STATIC;
203 cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
204 generate_cparameters (m, decl_space, cparam_map, function);
206 decl_space.add_type_member_declaration (function);
210 public override void visit_method (Method m) {
211 var old_symbol = current_symbol;
212 bool old_method_inner_error = current_method_inner_error;
213 int old_next_temp_var_id = next_temp_var_id;
214 var old_temp_vars = temp_vars;
215 var old_temp_ref_vars = temp_ref_vars;
216 var old_variable_name_map = variable_name_map;
217 var old_try = current_try;
218 current_symbol = m;
219 current_method_inner_error = false;
220 next_temp_var_id = 0;
221 temp_vars = new ArrayList<LocalVariable> ();
222 temp_ref_vars = new ArrayList<LocalVariable> ();
223 variable_name_map = new HashMap<string,string> (str_hash, str_equal);
224 current_try = null;
226 bool in_gobject_creation_method = false;
227 bool in_fundamental_creation_method = false;
229 check_type (m.return_type);
231 if (m is CreationMethod) {
232 var cl = current_type_symbol as Class;
233 if (cl != null && !cl.is_compact) {
234 if (cl.base_class == null) {
235 in_fundamental_creation_method = true;
236 } else if (gobject_type != null && cl.is_subtype_of (gobject_type)) {
237 in_gobject_creation_method = true;
242 var creturn_type = m.return_type;
243 if (m.return_type.is_real_non_null_struct_type ()) {
244 // structs are returned via out parameter
245 creturn_type = new VoidType ();
248 if (m.binding == MemberBinding.CLASS || m.binding == MemberBinding.STATIC) {
249 in_static_or_class_context = true;
251 m.accept_children (codegen);
252 in_static_or_class_context = false;
254 if (m is CreationMethod) {
255 if (in_gobject_creation_method && m.body != null) {
256 if (!((CreationMethod) m).chain_up) {
257 if (m.body.captured) {
258 Report.error (m.source_reference, "Closures are not supported in GObject-style creation methods");
261 var cblock = new CCodeBlock ();
263 // last property assignment statement
264 CodeNode last_stmt = null;
266 // set construct properties
267 foreach (CodeNode stmt in m.body.get_statements ()) {
268 var expr_stmt = stmt as ExpressionStatement;
269 if (expr_stmt != null) {
270 var prop = expr_stmt.assigned_property ();
271 if (prop != null && prop.set_accessor.construction) {
272 last_stmt = stmt;
276 if (last_stmt != null) {
277 foreach (CodeNode stmt in m.body.get_statements ()) {
278 if (stmt.ccodenode is CCodeFragment) {
279 foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) {
280 cblock.add_statement (cstmt);
282 } else {
283 cblock.add_statement (stmt.ccodenode);
285 if (last_stmt == stmt) {
286 break;
291 add_object_creation (cblock, ((CreationMethod) m).n_construction_params > 0 || current_class.get_type_parameters ().size > 0);
293 // other initialization code
294 foreach (CodeNode stmt in m.body.get_statements ()) {
295 if (last_stmt != null) {
296 if (last_stmt == stmt) {
297 last_stmt = null;
299 continue;
301 if (stmt.ccodenode is CCodeFragment) {
302 foreach (CCodeNode cstmt in ((CCodeFragment) stmt.ccodenode).get_children ()) {
303 cblock.add_statement (cstmt);
305 } else {
306 cblock.add_statement (stmt.ccodenode);
310 foreach (LocalVariable local in m.body.get_local_variables ()) {
311 if (!local.floating && requires_destroy (local.variable_type)) {
312 var ma = new MemberAccess.simple (local.name);
313 ma.symbol_reference = local;
314 cblock.add_statement (new CCodeExpressionStatement (get_unref_expression (get_variable_cexpression (local.name), local.variable_type, ma)));
318 m.body.ccodenode = cblock;
319 } else {
320 var cblock = (CCodeBlock) m.body.ccodenode;
322 var cdeclaration = new CCodeDeclaration ("%s *".printf (((Class) current_type_symbol).get_cname ()));
323 cdeclaration.add_declarator (new CCodeVariableDeclarator ("self"));
325 cblock.prepend_statement (cdeclaration);
330 bool inner_error = current_method_inner_error;
332 current_symbol = old_symbol;
333 current_method_inner_error = old_method_inner_error;
334 next_temp_var_id = old_next_temp_var_id;
335 temp_vars = old_temp_vars;
336 temp_ref_vars = old_temp_ref_vars;
337 variable_name_map = old_variable_name_map;
338 current_try = old_try;
340 // do not declare overriding methods and interface implementations
341 if (m.is_abstract || m.is_virtual
342 || (m.base_method == null && m.base_interface_method == null)) {
343 generate_method_declaration (m, source_declarations);
345 if (!m.is_internal_symbol ()) {
346 generate_method_declaration (m, header_declarations);
348 if (!m.is_private_symbol ()) {
349 generate_method_declaration (m, internal_header_declarations);
353 function = new CCodeFunction (m.get_real_cname ());
354 m.ccodenode = function;
356 if (m.is_inline) {
357 function.modifiers |= CCodeModifiers.INLINE;
360 var cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
362 generate_cparameters (m, source_declarations, cparam_map, function);
364 // generate *_real_* functions for virtual methods
365 // also generate them for abstract methods of classes to prevent faulty subclassing
366 if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
367 if (!m.coroutine) {
368 if (m.base_method != null || m.base_interface_method != null) {
369 // declare *_real_* function
370 function.modifiers |= CCodeModifiers.STATIC;
371 source_declarations.add_type_member_declaration (function.copy ());
372 } else if (m.is_private_symbol ()) {
373 function.modifiers |= CCodeModifiers.STATIC;
377 /* Methods imported from a plain C file don't
378 * have a body, e.g. Vala.Parser.parse_file () */
379 if (m.body != null) {
380 function.block = (CCodeBlock) m.body.ccodenode;
381 function.block.line = function.line;
383 var cinit = new CCodeFragment ();
384 function.block.prepend_statement (cinit);
386 if (m.coroutine) {
387 var co_function = new CCodeFunction (m.get_real_cname () + "_co", "gboolean");
389 // data struct to hold parameters, local variables, and the return value
390 co_function.add_parameter (new CCodeFormalParameter ("data", Symbol.lower_case_to_camel_case (m.get_cname ()) + "Data*"));
392 co_function.modifiers |= CCodeModifiers.STATIC;
393 source_declarations.add_type_member_declaration (co_function.copy ());
395 var cswitch = new CCodeSwitchStatement (new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_state_"));
397 // let gcc know that this can't happen
398 cswitch.add_statement (new CCodeLabel ("default"));
399 cswitch.add_statement (new CCodeExpressionStatement (new CCodeFunctionCall (new CCodeIdentifier ("g_assert_not_reached"))));
401 // initial coroutine state
402 cswitch.add_statement (new CCodeCaseStatement (new CCodeConstant ("0")));
404 // coroutine body
405 cswitch.add_statement (function.block);
407 // epilogue
408 cswitch.add_statement (complete_async ());
410 co_function.block = new CCodeBlock ();
411 co_function.block.add_statement (cswitch);
413 source_type_member_definition.append (co_function);
416 if (m.closure) {
417 // add variables for parent closure blocks
418 // as closures only have one parameter for the innermost closure block
419 var closure_block = current_closure_block;
420 int block_id = get_block_id (closure_block);
421 while (true) {
422 var parent_closure_block = next_closure_block (closure_block.parent_symbol);
423 if (parent_closure_block == null) {
424 break;
426 int parent_block_id = get_block_id (parent_closure_block);
428 var parent_data = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id));
429 var cdecl = new CCodeDeclaration ("Block%dData*".printf (parent_block_id));
430 cdecl.add_declarator (new CCodeVariableDeclarator ("_data%d_".printf (parent_block_id), parent_data));
432 cinit.append (cdecl);
434 closure_block = parent_closure_block;
435 block_id = parent_block_id;
438 // add self variable for closures
439 // as closures have block data parameter
440 if (m.binding == MemberBinding.INSTANCE) {
441 var cself = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self");
442 var cdecl = new CCodeDeclaration ("%s *".printf (current_class.get_cname ()));
443 cdecl.add_declarator (new CCodeVariableDeclarator ("self", cself));
445 cinit.append (cdecl);
447 } else if (m.parent_symbol is Class && !m.coroutine) {
448 var cl = (Class) m.parent_symbol;
449 if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
450 Method base_method;
451 ReferenceType base_expression_type;
452 if (m.overrides) {
453 base_method = m.base_method;
454 base_expression_type = new ObjectType ((Class) base_method.parent_symbol);
455 } else {
456 base_method = m.base_interface_method;
457 base_expression_type = new ObjectType ((Interface) base_method.parent_symbol);
459 var self_target_type = new ObjectType (cl);
460 CCodeExpression cself = transform_expression (new CCodeIdentifier ("base"), base_expression_type, self_target_type);
462 var cdecl = new CCodeDeclaration ("%s *".printf (cl.get_cname ()));
463 cdecl.add_declarator (new CCodeVariableDeclarator ("self", cself));
465 cinit.append (cdecl);
466 } else if (m.binding == MemberBinding.INSTANCE
467 && !(m is CreationMethod)) {
468 var ccheckstmt = create_method_type_check_statement (m, creturn_type, cl, true, "self");
469 if (ccheckstmt != null) {
470 ccheckstmt.line = function.line;
471 cinit.append (ccheckstmt);
475 foreach (FormalParameter param in m.get_parameters ()) {
476 if (param.ellipsis) {
477 break;
480 var t = param.parameter_type.data_type;
481 if (t != null && t.is_reference_type ()) {
482 if (param.direction != ParameterDirection.OUT) {
483 var type_check = create_method_type_check_statement (m, creturn_type, t, !param.parameter_type.nullable, get_variable_cname (param.name));
484 if (type_check != null) {
485 type_check.line = function.line;
486 cinit.append (type_check);
488 } else if (!m.coroutine) {
489 // ensure that the passed reference for output parameter is cleared
490 var a = new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, get_variable_cexpression (param.name)), new CCodeConstant ("NULL"));
491 var cblock = new CCodeBlock ();
492 cblock.add_statement (new CCodeExpressionStatement (a));
494 var condition = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, get_variable_cexpression (param.name), new CCodeConstant ("NULL"));
495 var if_statement = new CCodeIfStatement (condition, cblock);
496 cinit.append (if_statement);
501 if (!(m.return_type is VoidType) && !m.return_type.is_real_non_null_struct_type () && !m.coroutine) {
502 // do not declare result variable if exit block is known to be unreachable
503 if (m.exit_block == null || m.exit_block.get_predecessors ().size > 0) {
504 var cdecl = new CCodeDeclaration (m.return_type.get_cname ());
505 cdecl.add_declarator (new CCodeVariableDeclarator ("result"));
506 cinit.append (cdecl);
510 if (inner_error) {
511 /* always separate error parameter and inner_error local variable
512 * as error may be set to NULL but we're always interested in inner errors
514 if (m.coroutine) {
515 closure_struct.add_field ("GError *", "_inner_error_");
517 // no initialization necessary, closure struct is zeroed
518 } else {
519 var cdecl = new CCodeDeclaration ("GError *");
520 cdecl.add_declarator (new CCodeVariableDeclarator ("_inner_error_", new CCodeConstant ("NULL")));
521 cinit.append (cdecl);
525 if (!m.coroutine) {
526 source_type_member_definition.append (function);
529 if (m is CreationMethod) {
530 if (in_gobject_creation_method) {
531 int n_params = ((CreationMethod) m).n_construction_params;
533 if (!((CreationMethod) m).chain_up) {
534 if (n_params > 0 || current_class.get_type_parameters ().size > 0) {
535 // declare construction parameter array
536 var cparamsinit = new CCodeFunctionCall (new CCodeIdentifier ("g_new0"));
537 cparamsinit.add_argument (new CCodeIdentifier ("GParameter"));
538 cparamsinit.add_argument (new CCodeConstant ((n_params + 3 * current_class.get_type_parameters ().size).to_string ()));
540 var cdecl = new CCodeDeclaration ("GParameter *");
541 cdecl.add_declarator (new CCodeVariableDeclarator ("__params", cparamsinit));
542 cinit.append (cdecl);
544 cdecl = new CCodeDeclaration ("GParameter *");
545 cdecl.add_declarator (new CCodeVariableDeclarator ("__params_it", new CCodeIdentifier ("__params")));
546 cinit.append (cdecl);
549 /* type, dup func, and destroy func properties for generic types */
550 foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
551 CCodeConstant prop_name;
552 CCodeIdentifier param_name;
554 prop_name = new CCodeConstant ("\"%s-type\"".printf (type_param.name.down ()));
555 param_name = new CCodeIdentifier ("%s_type".printf (type_param.name.down ()));
556 cinit.append (new CCodeExpressionStatement (get_construct_property_assignment (prop_name, new IntegerType ((Struct) gtype_type), param_name)));
558 prop_name = new CCodeConstant ("\"%s-dup-func\"".printf (type_param.name.down ()));
559 param_name = new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ()));
560 cinit.append (new CCodeExpressionStatement (get_construct_property_assignment (prop_name, new PointerType (new VoidType ()), param_name)));
562 prop_name = new CCodeConstant ("\"%s-destroy-func\"".printf (type_param.name.down ()));
563 param_name = new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ()));
564 cinit.append (new CCodeExpressionStatement (get_construct_property_assignment (prop_name, new PointerType (new VoidType ()), param_name)));
567 } else if (is_gtypeinstance_creation_method (m)) {
568 var cl = (Class) m.parent_symbol;
569 var cdeclaration = new CCodeDeclaration (cl.get_cname () + "*");
570 var cdecl = new CCodeVariableDeclarator ("self");
571 cdeclaration.add_declarator (cdecl);
572 cinit.append (cdeclaration);
574 if (!((CreationMethod) m).chain_up) {
575 // TODO implicitly chain up to base class as in add_object_creation
576 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_create_instance"));
577 ccall.add_argument (new CCodeIdentifier ("object_type"));
578 cdecl.initializer = new CCodeCastExpression (ccall, cl.get_cname () + "*");
580 /* type, dup func, and destroy func fields for generic types */
581 foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
582 CCodeIdentifier param_name;
583 CCodeAssignment assign;
585 var priv_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv");
587 param_name = new CCodeIdentifier ("%s_type".printf (type_param.name.down ()));
588 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
589 cinit.append (new CCodeExpressionStatement (assign));
591 param_name = new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ()));
592 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
593 cinit.append (new CCodeExpressionStatement (assign));
595 param_name = new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ()));
596 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
597 cinit.append (new CCodeExpressionStatement (assign));
600 } else if (current_type_symbol is Class) {
601 var cl = (Class) m.parent_symbol;
602 var cdeclaration = new CCodeDeclaration (cl.get_cname () + "*");
603 var cdecl = new CCodeVariableDeclarator ("self");
604 cdeclaration.add_declarator (cdecl);
605 cinit.append (cdeclaration);
607 if (!((CreationMethod) m).chain_up) {
608 // TODO implicitly chain up to base class as in add_object_creation
609 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
610 ccall.add_argument (new CCodeIdentifier (cl.get_cname ()));
611 cdecl.initializer = ccall;
614 if (cl.base_class == null) {
615 // derived compact classes do not have fields
616 var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (cl.get_lower_case_cname (null))));
617 cinitcall.add_argument (new CCodeIdentifier ("self"));
618 cinit.append (new CCodeExpressionStatement (cinitcall));
620 } else {
621 var st = (Struct) m.parent_symbol;
623 // memset needs string.h
624 source_declarations.add_include ("string.h");
625 var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
626 czero.add_argument (new CCodeIdentifier ("self"));
627 czero.add_argument (new CCodeConstant ("0"));
628 czero.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (st.get_cname ())));
629 cinit.append (new CCodeExpressionStatement (czero));
633 if (context.module_init_method == m && in_plugin) {
634 // GTypeModule-based plug-in, register types
635 cinit.append (module_init_fragment);
638 foreach (Expression precondition in m.get_preconditions ()) {
639 var check_stmt = create_precondition_statement (m, creturn_type, precondition);
640 if (check_stmt != null) {
641 cinit.append (check_stmt);
644 } else if (m.is_abstract) {
645 // generate helpful error message if a sublcass does not implement an abstract method.
646 // This is only meaningful for subclasses implemented in C since the vala compiler would
647 // complain during compile time of such en error.
649 var cblock = new CCodeBlock ();
651 // add a typecheck statement for "self"
652 var check_stmt = create_method_type_check_statement (m, creturn_type, current_type_symbol, true, "self");
653 if (check_stmt != null) {
654 cblock.add_statement (check_stmt);
657 // add critical warning that this method should not have been called
658 var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE"));
659 type_from_instance_call.add_argument (new CCodeIdentifier ("self"));
661 var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name"));
662 type_name_call.add_argument (type_from_instance_call);
664 var error_string = "\"Type `%%s' does not implement abstract method `%s'\"".printf (m.get_cname ());
666 var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
667 cerrorcall.add_argument (new CCodeConstant (error_string));
668 cerrorcall.add_argument (type_name_call);
670 cblock.add_statement (new CCodeExpressionStatement (cerrorcall));
672 // add return statement
673 cblock.add_statement (new CCodeReturnStatement (default_value_for_type (creturn_type, false)));
675 function.block = cblock;
676 source_type_member_definition.append (function);
680 if ((m.is_abstract || m.is_virtual) && !m.coroutine &&
681 /* If the method is a signal handler, the declaration
682 * is not needed. -- the name should be reserved for the
683 * emitter! */
684 m.signal_reference == null) {
686 cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
687 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
689 generate_vfunc (m, creturn_type, cparam_map, carg_map);
692 if (m.entry_point) {
693 // m is possible entry point, add appropriate startup code
694 var cmain = new CCodeFunction ("main", "int");
695 cmain.line = function.line;
696 cmain.add_parameter (new CCodeFormalParameter ("argc", "int"));
697 cmain.add_parameter (new CCodeFormalParameter ("argv", "char **"));
698 var main_block = new CCodeBlock ();
700 if (context.profile == Profile.GOBJECT) {
701 if (context.thread) {
702 var thread_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_thread_init"));
703 thread_init_call.line = cmain.line;
704 thread_init_call.add_argument (new CCodeConstant ("NULL"));
705 main_block.add_statement (new CCodeExpressionStatement (thread_init_call));
708 var type_init_call = new CCodeExpressionStatement (new CCodeFunctionCall (new CCodeIdentifier ("g_type_init")));
709 type_init_call.line = cmain.line;
710 main_block.add_statement (type_init_call);
713 var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name));
714 if (m.get_parameters ().size == 1) {
715 main_call.add_argument (new CCodeIdentifier ("argv"));
716 main_call.add_argument (new CCodeIdentifier ("argc"));
718 if (m.return_type is VoidType) {
719 // method returns void, always use 0 as exit code
720 var main_stmt = new CCodeExpressionStatement (main_call);
721 main_stmt.line = cmain.line;
722 main_block.add_statement (main_stmt);
723 var ret_stmt = new CCodeReturnStatement (new CCodeConstant ("0"));
724 ret_stmt.line = cmain.line;
725 main_block.add_statement (ret_stmt);
726 } else {
727 var main_stmt = new CCodeReturnStatement (main_call);
728 main_stmt.line = cmain.line;
729 main_block.add_statement (main_stmt);
731 cmain.block = main_block;
732 source_type_member_definition.append (cmain);
736 public virtual void generate_parameter (FormalParameter param, CCodeDeclarationSpace decl_space, Map<int,CCodeFormalParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
737 if (!param.ellipsis) {
738 string ctypename = param.parameter_type.get_cname ();
740 generate_type_declaration (param.parameter_type, decl_space);
742 // pass non-simple structs always by reference
743 if (param.parameter_type.data_type is Struct) {
744 var st = (Struct) param.parameter_type.data_type;
745 if (!st.is_simple_type () && param.direction == ParameterDirection.IN) {
746 if (st.is_immutable && !param.parameter_type.value_owned) {
747 ctypename = "const " + ctypename;
750 if (!param.parameter_type.nullable) {
751 ctypename += "*";
756 if (param.direction != ParameterDirection.IN) {
757 ctypename += "*";
760 param.ccodenode = new CCodeFormalParameter (get_variable_cname (param.name), ctypename);
761 } else {
762 param.ccodenode = new CCodeFormalParameter.with_ellipsis ();
765 cparam_map.set (get_param_pos (param.cparameter_position), (CCodeFormalParameter) param.ccodenode);
766 if (carg_map != null && !param.ellipsis) {
767 carg_map.set (get_param_pos (param.cparameter_position), get_variable_cexpression (param.name));
771 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) {
772 if (m.closure) {
773 var closure_block = current_closure_block;
774 int block_id = get_block_id (closure_block);
775 var instance_param = new CCodeFormalParameter ("_data%d_".printf (block_id), "Block%dData*".printf (block_id));
776 cparam_map.set (get_param_pos (m.cinstance_parameter_position), instance_param);
777 } else if (m.parent_symbol is Class && m is CreationMethod) {
778 var cl = (Class) m.parent_symbol;
779 if (!cl.is_compact && vcall == null) {
780 cparam_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeFormalParameter ("object_type", "GType"));
782 } else if (m.binding == MemberBinding.INSTANCE || (m.parent_symbol is Struct && m is CreationMethod)) {
783 TypeSymbol parent_type = find_parent_type (m);
784 DataType this_type;
785 if (parent_type is Class) {
786 this_type = new ObjectType ((Class) parent_type);
787 } else if (parent_type is Interface) {
788 this_type = new ObjectType ((Interface) parent_type);
789 } else if (parent_type is Struct) {
790 this_type = new StructValueType ((Struct) parent_type);
791 } else if (parent_type is Enum) {
792 this_type = new EnumValueType ((Enum) parent_type);
793 } else {
794 assert_not_reached ();
797 generate_type_declaration (this_type, decl_space);
799 CCodeFormalParameter instance_param = null;
800 if (m.base_interface_method != null && !m.is_abstract && !m.is_virtual) {
801 var base_type = new ObjectType ((Interface) m.base_interface_method.parent_symbol);
802 instance_param = new CCodeFormalParameter ("base", base_type.get_cname ());
803 } else if (m.overrides) {
804 var base_type = new ObjectType ((Class) m.base_method.parent_symbol);
805 instance_param = new CCodeFormalParameter ("base", base_type.get_cname ());
806 } else {
807 if (m.parent_symbol is Struct && !((Struct) m.parent_symbol).is_simple_type ()) {
808 instance_param = new CCodeFormalParameter ("*self", this_type.get_cname ());
809 } else {
810 instance_param = new CCodeFormalParameter ("self", this_type.get_cname ());
813 cparam_map.set (get_param_pos (m.cinstance_parameter_position), instance_param);
814 } else if (m.binding == MemberBinding.CLASS) {
815 TypeSymbol parent_type = find_parent_type (m);
816 DataType this_type;
817 this_type = new ClassType ((Class) parent_type);
818 var class_param = new CCodeFormalParameter ("klass", this_type.get_cname ());
819 cparam_map.set (get_param_pos (m.cinstance_parameter_position), class_param);
822 if (is_gtypeinstance_creation_method (m)) {
823 // memory management for generic types
824 int type_param_index = 0;
825 var cl = (Class) m.parent_symbol;
826 foreach (TypeParameter type_param in cl.get_type_parameters ()) {
827 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType"));
828 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
829 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
830 if (carg_map != null) {
831 carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
832 carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
833 carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
835 type_param_index++;
837 } else {
838 int type_param_index = 0;
839 foreach (var type_param in m.get_type_parameters ()) {
840 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType"));
841 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
842 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
843 if (carg_map != null) {
844 carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
845 carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
846 carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
848 type_param_index++;
852 foreach (FormalParameter param in m.get_parameters ()) {
853 if (param.direction != ParameterDirection.OUT) {
854 if ((direction & 1) == 0) {
855 // no in paramters
856 continue;
858 } else {
859 if ((direction & 2) == 0) {
860 // no out paramters
861 continue;
865 generate_parameter (param, decl_space, cparam_map, carg_map);
868 if ((direction & 2) != 0) {
869 generate_method_result_declaration (m, decl_space, func, cparam_map, carg_map);
872 // append C parameters in the right order
873 int last_pos = -1;
874 int min_pos;
875 while (true) {
876 min_pos = -1;
877 foreach (int pos in cparam_map.get_keys ()) {
878 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
879 min_pos = pos;
882 if (min_pos == -1) {
883 break;
885 func.add_parameter (cparam_map.get (min_pos));
886 if (vdeclarator != null) {
887 vdeclarator.add_parameter (cparam_map.get (min_pos));
889 if (vcall != null) {
890 var arg = carg_map.get (min_pos);
891 if (arg != null) {
892 vcall.add_argument (arg);
895 last_pos = min_pos;
899 public void generate_vfunc (Method m, DataType return_type, Map<int,CCodeFormalParameter> cparam_map, Map<int,CCodeExpression> carg_map, string suffix = "", int direction = 3) {
900 var vfunc = new CCodeFunction (m.get_cname () + suffix);
901 if (function != null) {
902 vfunc.line = function.line;
905 var vblock = new CCodeBlock ();
907 foreach (Expression precondition in m.get_preconditions ()) {
908 var check_stmt = create_precondition_statement (m, return_type, precondition);
909 if (check_stmt != null) {
910 vblock.add_statement (check_stmt);
914 CCodeFunctionCall vcast = null;
915 if (m.parent_symbol is Interface) {
916 var iface = (Interface) m.parent_symbol;
918 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null))));
919 } else {
920 var cl = (Class) m.parent_symbol;
922 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null))));
924 vcast.add_argument (new CCodeIdentifier ("self"));
926 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, m.vfunc_name + suffix));
927 carg_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeIdentifier ("self"));
929 generate_cparameters (m, source_declarations, cparam_map, vfunc, null, carg_map, vcall, direction);
931 CCodeStatement cstmt;
932 if (return_type is VoidType) {
933 cstmt = new CCodeExpressionStatement (vcall);
934 } else if (m.get_postconditions ().size == 0) {
935 /* pass method return value */
936 cstmt = new CCodeReturnStatement (vcall);
937 } else {
938 /* store method return value for postconditions */
939 var cdecl = new CCodeDeclaration (get_creturn_type (m, return_type.get_cname ()));
940 cdecl.add_declarator (new CCodeVariableDeclarator ("result", vcall));
941 cstmt = cdecl;
943 cstmt.line = vfunc.line;
944 vblock.add_statement (cstmt);
946 if (m.get_postconditions ().size > 0) {
947 foreach (Expression postcondition in m.get_postconditions ()) {
948 vblock.add_statement (create_postcondition_statement (postcondition));
951 if (!(return_type is VoidType)) {
952 var cret_stmt = new CCodeReturnStatement (new CCodeIdentifier ("result"));
953 cret_stmt.line = vfunc.line;
954 vblock.add_statement (cret_stmt);
958 vfunc.block = vblock;
960 source_type_member_definition.append (vfunc);
963 private CCodeStatement? create_method_type_check_statement (Method m, DataType return_type, TypeSymbol t, bool non_null, string var_name) {
964 if (m.coroutine) {
965 return null;
966 } else {
967 return create_type_check_statement (m, return_type, t, non_null, var_name);
971 private CCodeStatement? create_precondition_statement (CodeNode method_node, DataType ret_type, Expression precondition) {
972 var ccheck = new CCodeFunctionCall ();
974 ccheck.add_argument ((CCodeExpression) precondition.ccodenode);
976 if (ret_type is VoidType) {
977 /* void function */
978 ccheck.call = new CCodeIdentifier ("g_return_if_fail");
979 } else {
980 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
982 var cdefault = default_value_for_type (ret_type, false);
983 if (cdefault != null) {
984 ccheck.add_argument (cdefault);
985 } else {
986 return null;
990 return new CCodeExpressionStatement (ccheck);
993 private TypeSymbol? find_parent_type (Symbol sym) {
994 while (sym != null) {
995 if (sym is TypeSymbol) {
996 return (TypeSymbol) sym;
998 sym = sym.parent_symbol;
1000 return null;
1003 private void add_object_creation (CCodeBlock b, bool has_params) {
1004 var cl = (Class) current_type_symbol;
1006 if (!has_params && cl.base_class != gobject_type) {
1007 // possibly report warning or error about missing base call
1010 var cdecl = new CCodeVariableDeclarator ("self");
1011 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_object_newv"));
1012 ccall.add_argument (new CCodeIdentifier ("object_type"));
1013 if (has_params) {
1014 ccall.add_argument (new CCodeConstant ("__params_it - __params"));
1015 ccall.add_argument (new CCodeConstant ("__params"));
1016 } else {
1017 ccall.add_argument (new CCodeConstant ("0"));
1018 ccall.add_argument (new CCodeConstant ("NULL"));
1020 cdecl.initializer = ccall;
1022 var cdeclaration = new CCodeDeclaration ("%s *".printf (cl.get_cname ()));
1023 cdeclaration.add_declarator (cdecl);
1025 b.add_statement (cdeclaration);
1028 public override void visit_creation_method (CreationMethod m) {
1029 bool visible = !m.is_private_symbol ();
1031 head.visit_method (m);
1033 DataType creturn_type;
1034 if (current_type_symbol is Class) {
1035 creturn_type = new ObjectType (current_class);
1036 } else {
1037 creturn_type = new VoidType ();
1040 // do not generate _new functions for creation methods of abstract classes
1041 if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) {
1042 var vfunc = new CCodeFunction (m.get_cname ());
1043 vfunc.line = function.line;
1045 var cparam_map = new HashMap<int,CCodeFormalParameter> (direct_hash, direct_equal);
1046 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
1048 var vblock = new CCodeBlock ();
1050 var vcall = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
1051 vcall.add_argument (new CCodeIdentifier (current_class.get_type_id ()));
1053 generate_cparameters (m, source_declarations, cparam_map, vfunc, null, carg_map, vcall);
1054 CCodeStatement cstmt = new CCodeReturnStatement (vcall);
1055 cstmt.line = vfunc.line;
1056 vblock.add_statement (cstmt);
1058 if (!visible) {
1059 vfunc.modifiers |= CCodeModifiers.STATIC;
1062 vfunc.block = vblock;
1064 source_type_member_definition.append (vfunc);
1067 if (current_type_symbol is Class && gobject_type != null && current_class.is_subtype_of (gobject_type)
1068 && (((CreationMethod) m).n_construction_params > 0 || current_class.get_type_parameters ().size > 0)
1069 && !((CreationMethod) m).chain_up) {
1070 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, new CCodeIdentifier ("__params_it"), new CCodeIdentifier ("__params"));
1071 var cdofreeparam = new CCodeBlock ();
1072 cdofreeparam.add_statement (new CCodeExpressionStatement (new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_DECREMENT, new CCodeIdentifier ("__params_it"))));
1073 var cunsetcall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_unset"));
1074 cunsetcall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("__params_it"), "value")));
1075 cdofreeparam.add_statement (new CCodeExpressionStatement (cunsetcall));
1076 function.block.add_statement (new CCodeWhileStatement (ccond, cdofreeparam));
1078 var cfreeparams = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
1079 cfreeparams.add_argument (new CCodeIdentifier ("__params"));
1080 function.block.add_statement (new CCodeExpressionStatement (cfreeparams));
1083 if (current_type_symbol is Class) {
1084 CCodeExpression cresult = new CCodeIdentifier ("self");
1085 if (get_custom_creturn_type (m) != null) {
1086 cresult = new CCodeCastExpression (cresult, get_custom_creturn_type (m));
1089 var creturn = new CCodeReturnStatement ();
1090 creturn.return_expression = cresult;
1091 function.block.add_statement (creturn);
1096 // vim:sw=8 noet