CodeWriter: Write [Diagnostics] attribute
[vala-lang.git] / codegen / valaccodemethodmodule.vala
blob0b8c81af8a0969c4b1e4a3cff57f93336c6ca37b
1 /* valaccodemethodmodule.vala
3 * Copyright (C) 2007-2010 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;
26 /**
27 * The link between a method and generated code.
29 public abstract class Vala.CCodeMethodModule : CCodeStructModule {
30 public override bool method_has_wrapper (Method method) {
31 return (method.get_attribute ("NoWrapper") == null);
34 public override string? get_custom_creturn_type (Method m) {
35 var attr = m.get_attribute ("CCode");
36 if (attr != null) {
37 string type = attr.get_string ("type");
38 if (type != null) {
39 return type;
42 return null;
45 string get_creturn_type (Method m, string default_value) {
46 string type = get_custom_creturn_type (m);
47 if (type == null) {
48 return default_value;
50 return type;
53 bool is_gtypeinstance_creation_method (Method m) {
54 bool result = false;
56 var cl = m.parent_symbol as Class;
57 if (m is CreationMethod && cl != null && !cl.is_compact) {
58 result = true;
61 return result;
64 public virtual void generate_method_result_declaration (Method m, CCodeFile decl_space, CCodeFunction cfunc, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
65 var creturn_type = m.return_type;
66 if (m is CreationMethod) {
67 var cl = m.parent_symbol as Class;
68 if (cl != null) {
69 // object creation methods return the new object in C
70 // in Vala they have no return type
71 creturn_type = new ObjectType (cl);
73 } else if (m.return_type.is_real_non_null_struct_type ()) {
74 // structs are returned via out parameter
75 creturn_type = new VoidType ();
77 cfunc.return_type = get_creturn_type (m, creturn_type.get_cname ());
79 generate_type_declaration (m.return_type, decl_space);
81 if (m.return_type.is_real_non_null_struct_type ()) {
82 // structs are returned via out parameter
83 var cparam = new CCodeParameter ("result", m.return_type.get_cname () + "*");
84 cparam_map.set (get_param_pos (-3), cparam);
85 if (carg_map != null) {
86 carg_map.set (get_param_pos (-3), get_result_cexpression ());
88 } else if (!m.no_array_length && m.return_type is ArrayType) {
89 // return array length if appropriate
90 var array_type = (ArrayType) m.return_type;
92 for (int dim = 1; dim <= array_type.rank; dim++) {
93 var cparam = new CCodeParameter (get_array_length_cname ("result", dim), "int*");
94 cparam_map.set (get_param_pos (m.carray_length_parameter_position + 0.01 * dim), cparam);
95 if (carg_map != null) {
96 carg_map.set (get_param_pos (m.carray_length_parameter_position + 0.01 * dim), get_variable_cexpression (cparam.name));
99 } else if (m.return_type is DelegateType) {
100 // return delegate target if appropriate
101 var deleg_type = (DelegateType) m.return_type;
102 var d = deleg_type.delegate_symbol;
103 if (d.has_target) {
104 var cparam = new CCodeParameter (get_delegate_target_cname ("result"), "void**");
105 cparam_map.set (get_param_pos (m.cdelegate_target_parameter_position), cparam);
106 if (carg_map != null) {
107 carg_map.set (get_param_pos (m.cdelegate_target_parameter_position), get_variable_cexpression (cparam.name));
109 if (deleg_type.value_owned) {
110 cparam = new CCodeParameter (get_delegate_target_destroy_notify_cname ("result"), "GDestroyNotify*");
111 cparam_map.set (get_param_pos (m.cdelegate_target_parameter_position + 0.01), cparam);
112 if (carg_map != null) {
113 carg_map.set (get_param_pos (m.cdelegate_target_parameter_position + 0.01), get_variable_cexpression (cparam.name));
119 if (m.get_error_types ().size > 0 || (m.base_method != null && m.base_method.get_error_types ().size > 0) || (m.base_interface_method != null && m.base_interface_method.get_error_types ().size > 0)) {
120 foreach (DataType error_type in m.get_error_types ()) {
121 generate_type_declaration (error_type, decl_space);
124 var cparam = new CCodeParameter ("error", "GError**");
125 cparam_map.set (get_param_pos (-1), cparam);
126 if (carg_map != null) {
127 carg_map.set (get_param_pos (-1), new CCodeIdentifier (cparam.name));
132 public void complete_async () {
133 var state = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_state_");
134 var zero = new CCodeConstant ("0");
135 var state_is_zero = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, state, zero);
136 ccode.open_if (state_is_zero);
138 var async_result_expr = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_async_result");
140 var idle_call = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_complete_in_idle"));
141 idle_call.add_argument (async_result_expr);
142 ccode.add_expression (idle_call);
144 ccode.add_else ();
146 var direct_call = new CCodeFunctionCall (new CCodeIdentifier ("g_simple_async_result_complete"));
147 direct_call.add_argument (async_result_expr);
148 ccode.add_expression (direct_call);
150 ccode.close ();
152 var unref = new CCodeFunctionCall (new CCodeIdentifier ("g_object_unref"));
153 unref.add_argument (async_result_expr);
154 ccode.add_expression (unref);
156 ccode.add_return (new CCodeConstant ("FALSE"));
159 public override void generate_method_declaration (Method m, CCodeFile decl_space) {
160 if (m.is_async_callback) {
161 return;
163 if (add_symbol_declaration (decl_space, m, m.get_cname ())) {
164 return;
167 var function = new CCodeFunction (m.get_cname ());
169 if (m.is_private_symbol () && !m.external) {
170 function.modifiers |= CCodeModifiers.STATIC;
171 if (m.is_inline) {
172 function.modifiers |= CCodeModifiers.INLINE;
176 if (m.deprecated) {
177 function.modifiers |= CCodeModifiers.DEPRECATED;
180 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
181 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
183 var cl = m.parent_symbol as Class;
185 // do not generate _new functions for creation methods of abstract classes
186 if (!(m is CreationMethod && cl != null && cl.is_abstract)) {
187 generate_cparameters (m, decl_space, cparam_map, function, null, carg_map, new CCodeFunctionCall (new CCodeIdentifier ("fake")));
189 decl_space.add_function_declaration (function);
192 if (m is CreationMethod && cl != null) {
193 // _construct function
194 function = new CCodeFunction (m.get_real_cname ());
196 if (m.is_private_symbol ()) {
197 function.modifiers |= CCodeModifiers.STATIC;
200 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
201 generate_cparameters (m, decl_space, cparam_map, function);
203 decl_space.add_function_declaration (function);
207 void register_plugin_types (Symbol sym, Set<Symbol> registered_types) {
208 var ns = sym as Namespace;
209 var cl = sym as Class;
210 var iface = sym as Interface;
211 if (ns != null) {
212 foreach (var ns_ns in ns.get_namespaces ()) {
213 register_plugin_types (ns_ns, registered_types);
215 foreach (var ns_cl in ns.get_classes ()) {
216 register_plugin_types (ns_cl, registered_types);
218 foreach (var ns_iface in ns.get_interfaces ()) {
219 register_plugin_types (ns_iface, registered_types);
221 } else if (cl != null) {
222 register_plugin_type (cl, registered_types);
223 foreach (var cl_cl in cl.get_classes ()) {
224 register_plugin_types (cl_cl, registered_types);
226 } else if (iface != null) {
227 register_plugin_type (iface, registered_types);
228 foreach (var iface_cl in iface.get_classes ()) {
229 register_plugin_types (iface_cl, registered_types);
234 void register_plugin_type (ObjectTypeSymbol type_symbol, Set<Symbol> registered_types) {
235 if (type_symbol.external_package) {
236 return;
239 if (!registered_types.add (type_symbol)) {
240 // already registered
241 return;
244 var cl = type_symbol as Class;
245 if (cl != null) {
246 if (cl.is_compact) {
247 return;
250 // register base types first
251 foreach (var base_type in cl.get_base_types ()) {
252 register_plugin_type ((ObjectTypeSymbol) base_type.data_type, registered_types);
256 var register_call = new CCodeFunctionCall (new CCodeIdentifier ("%s_register_type".printf (type_symbol.get_lower_case_cname (null))));
257 register_call.add_argument (new CCodeIdentifier (module_init_param_name));
258 ccode.add_expression (register_call);
261 public override void visit_method (Method m) {
262 push_context (new EmitContext (m));
264 bool in_gobject_creation_method = false;
265 bool in_fundamental_creation_method = false;
267 check_type (m.return_type);
269 if (m is CreationMethod) {
270 var cl = current_type_symbol as Class;
271 if (cl != null && !cl.is_compact) {
272 if (cl.base_class == null) {
273 in_fundamental_creation_method = true;
274 } else if (gobject_type != null && cl.is_subtype_of (gobject_type)) {
275 in_gobject_creation_method = true;
280 if (m.coroutine) {
281 next_coroutine_state = 1;
284 var creturn_type = m.return_type;
285 if (m.return_type.is_real_non_null_struct_type ()) {
286 // structs are returned via out parameter
287 creturn_type = new VoidType ();
290 if (m.binding == MemberBinding.CLASS || m.binding == MemberBinding.STATIC) {
291 in_static_or_class_context = true;
295 foreach (Parameter param in m.get_parameters ()) {
296 param.accept (this);
299 // do not declare overriding methods and interface implementations
300 if (m.is_abstract || m.is_virtual
301 || (m.base_method == null && m.base_interface_method == null)) {
302 generate_method_declaration (m, cfile);
304 if (!m.is_internal_symbol ()) {
305 generate_method_declaration (m, header_file);
307 if (!m.is_private_symbol ()) {
308 generate_method_declaration (m, internal_header_file);
312 CCodeFunction function;
313 function = new CCodeFunction (m.get_real_cname ());
315 if (m.is_inline) {
316 function.modifiers |= CCodeModifiers.INLINE;
319 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
321 generate_cparameters (m, cfile, cparam_map, function);
323 // generate *_real_* functions for virtual methods
324 // also generate them for abstract methods of classes to prevent faulty subclassing
325 if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
326 if (!m.coroutine) {
327 if (m.base_method != null || m.base_interface_method != null) {
328 // declare *_real_* function
329 function.modifiers |= CCodeModifiers.STATIC;
330 cfile.add_function_declaration (function);
331 } else if (m.is_private_symbol ()) {
332 function.modifiers |= CCodeModifiers.STATIC;
334 } else {
335 if (m.body != null) {
336 function = new CCodeFunction (m.get_real_cname () + "_co", "gboolean");
338 // data struct to hold parameters, local variables, and the return value
339 function.add_parameter (new CCodeParameter ("data", Symbol.lower_case_to_camel_case (m.get_cname ()) + "Data*"));
341 function.modifiers |= CCodeModifiers.STATIC;
342 cfile.add_function_declaration (function);
347 if (m.comment != null) {
348 cfile.add_type_member_definition (new CCodeComment (m.comment.content));
351 push_function (function);
353 // generate *_real_* functions for virtual methods
354 // also generate them for abstract methods of classes to prevent faulty subclassing
355 if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
356 if (m.body != null) {
357 if (m.coroutine) {
358 ccode.open_switch (new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "_state_"));
360 // initial coroutine state
361 ccode.add_case (new CCodeConstant ("0"));
362 ccode.add_goto ("_state_0");
364 for (int state = 1; state <= m.yield_count; state++) {
365 ccode.add_case (new CCodeConstant (state.to_string ()));
366 ccode.add_goto ("_state_%d".printf (state));
370 // let gcc know that this can't happen
371 ccode.add_default ();
372 ccode.add_expression (new CCodeFunctionCall (new CCodeIdentifier ("g_assert_not_reached")));
374 ccode.close ();
376 // coroutine body
377 ccode.add_label ("_state_0");
380 if (m.closure) {
381 // add variables for parent closure blocks
382 // as closures only have one parameter for the innermost closure block
383 var closure_block = current_closure_block;
384 int block_id = get_block_id (closure_block);
385 while (true) {
386 var parent_closure_block = next_closure_block (closure_block.parent_symbol);
387 if (parent_closure_block == null) {
388 break;
390 int parent_block_id = get_block_id (parent_closure_block);
392 var parent_data = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "_data%d_".printf (parent_block_id));
393 ccode.add_declaration ("Block%dData*".printf (parent_block_id), new CCodeVariableDeclarator ("_data%d_".printf (parent_block_id)));
394 ccode.add_assignment (new CCodeIdentifier ("_data%d_".printf (parent_block_id)), parent_data);
396 closure_block = parent_closure_block;
397 block_id = parent_block_id;
400 // add self variable for closures
401 // as closures have block data parameter
402 if (m.binding == MemberBinding.INSTANCE) {
403 var cself = new CCodeMemberAccess.pointer (new CCodeIdentifier ("_data%d_".printf (block_id)), "self");
404 ccode.add_declaration ("%s *".printf (current_class.get_cname ()), new CCodeVariableDeclarator ("self"));
405 ccode.add_assignment (new CCodeIdentifier ("self"), cself);
408 // allow capturing generic type parameters
409 foreach (var type_param in m.get_type_parameters ()) {
410 string func_name;
412 func_name = "%s_type".printf (type_param.name.down ());
413 ccode.add_declaration ("GType", new CCodeVariableDeclarator (func_name));
414 ccode.add_assignment (new CCodeIdentifier (func_name), new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name));
416 func_name = "%s_dup_func".printf (type_param.name.down ());
417 ccode.add_declaration ("GBoxedCopyFunc", new CCodeVariableDeclarator (func_name));
418 ccode.add_assignment (new CCodeIdentifier (func_name), new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name));
420 func_name = "%s_destroy_func".printf (type_param.name.down ());
421 ccode.add_declaration ("GDestroyNotify", new CCodeVariableDeclarator (func_name));
422 ccode.add_assignment (new CCodeIdentifier (func_name), new CCodeMemberAccess.pointer (get_variable_cexpression ("_data%d_".printf (block_id)), func_name));
424 } else if (m.parent_symbol is Class && !m.coroutine) {
425 var cl = (Class) m.parent_symbol;
426 if (m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
427 Method base_method;
428 ReferenceType base_expression_type;
429 if (m.overrides) {
430 base_method = m.base_method;
431 base_expression_type = new ObjectType ((Class) base_method.parent_symbol);
432 } else {
433 base_method = m.base_interface_method;
434 base_expression_type = new ObjectType ((Interface) base_method.parent_symbol);
436 var self_target_type = new ObjectType (cl);
437 CCodeExpression cself = transform_expression (new CCodeIdentifier ("base"), base_expression_type, self_target_type);
439 ccode.add_declaration ("%s *".printf (cl.get_cname ()), new CCodeVariableDeclarator ("self"));
440 ccode.add_assignment (new CCodeIdentifier ("self"), cself);
441 } else if (m.binding == MemberBinding.INSTANCE
442 && !(m is CreationMethod)) {
443 create_method_type_check_statement (m, creturn_type, cl, true, "self");
447 foreach (Parameter param in m.get_parameters ()) {
448 if (param.ellipsis) {
449 break;
452 if (param.direction != ParameterDirection.OUT) {
453 var t = param.variable_type.data_type;
454 if (t != null && t.is_reference_type ()) {
455 create_method_type_check_statement (m, creturn_type, t, !param.variable_type.nullable, get_variable_cname (param.name));
457 } else if (!m.coroutine) {
458 // declare local variable for out parameter to allow assignment even when caller passes NULL
459 var vardecl = new CCodeVariableDeclarator.zero (get_variable_cname ("_" + param.name), default_value_for_type (param.variable_type, true));
460 ccode.add_declaration (param.variable_type.get_cname (), vardecl);
462 if (param.variable_type is ArrayType) {
463 // create variables to store array dimensions
464 var array_type = (ArrayType) param.variable_type;
466 if (!array_type.fixed_length) {
467 for (int dim = 1; dim <= array_type.rank; dim++) {
468 vardecl = new CCodeVariableDeclarator.zero (get_array_length_cname (get_variable_cname ("_" + param.name), dim), new CCodeConstant ("0"));
469 ccode.add_declaration ("int", vardecl);
472 } else if (param.variable_type is DelegateType) {
473 var deleg_type = (DelegateType) param.variable_type;
474 var d = deleg_type.delegate_symbol;
475 if (d.has_target) {
476 // create variable to store delegate target
477 vardecl = new CCodeVariableDeclarator.zero (get_delegate_target_cname (get_variable_cname ("_" + param.name)), new CCodeConstant ("NULL"));
478 ccode.add_declaration ("void *", vardecl);
480 if (deleg_type.value_owned) {
481 vardecl = new CCodeVariableDeclarator.zero (get_delegate_target_destroy_notify_cname (get_variable_cname ("_" + param.name)), new CCodeConstant ("NULL"));
482 ccode.add_declaration ("GDestroyNotify", vardecl);
489 if (!(m.return_type is VoidType) && !m.return_type.is_real_non_null_struct_type () && !m.coroutine) {
490 var vardecl = new CCodeVariableDeclarator ("result", default_value_for_type (m.return_type, true));
491 vardecl.init0 = true;
492 ccode.add_declaration (m.return_type.get_cname (), vardecl);
495 if (m is CreationMethod) {
496 if (in_gobject_creation_method) {
497 ccode.add_declaration ("%s *".printf (((Class) current_type_symbol).get_cname ()), new CCodeVariableDeclarator.zero ("self", new CCodeConstant ("NULL")));
498 } else if (is_gtypeinstance_creation_method (m)) {
499 var cl = (Class) m.parent_symbol;
500 ccode.add_declaration (cl.get_cname () + "*", new CCodeVariableDeclarator.zero ("self", new CCodeConstant ("NULL")));
502 if (cl.is_fundamental ()) {
503 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_type_create_instance"));
504 ccall.add_argument (new CCodeIdentifier ("object_type"));
505 ccode.add_assignment (new CCodeIdentifier ("self"), new CCodeCastExpression (ccall, cl.get_cname () + "*"));
507 /* type, dup func, and destroy func fields for generic types */
508 foreach (TypeParameter type_param in current_class.get_type_parameters ()) {
509 CCodeIdentifier param_name;
510 CCodeAssignment assign;
512 var priv_access = new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv");
514 param_name = new CCodeIdentifier ("%s_type".printf (type_param.name.down ()));
515 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
516 ccode.add_expression (assign);
518 param_name = new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ()));
519 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
520 ccode.add_expression (assign);
522 param_name = new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ()));
523 assign = new CCodeAssignment (new CCodeMemberAccess.pointer (priv_access, param_name.name), param_name);
524 ccode.add_expression (assign);
527 } else if (current_type_symbol is Class) {
528 var cl = (Class) m.parent_symbol;
529 ccode.add_declaration (cl.get_cname () + "*", new CCodeVariableDeclarator ("self"));
531 if (!((CreationMethod) m).chain_up) {
532 // TODO implicitly chain up to base class as in add_object_creation
533 var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_slice_new0"));
534 ccall.add_argument (new CCodeIdentifier (cl.get_cname ()));
535 ccode.add_assignment (new CCodeIdentifier ("self"), ccall);
538 if (cl.base_class == null) {
539 // derived compact classes do not have fields
540 var cinitcall = new CCodeFunctionCall (new CCodeIdentifier ("%s_instance_init".printf (cl.get_lower_case_cname (null))));
541 cinitcall.add_argument (new CCodeIdentifier ("self"));
542 ccode.add_expression (cinitcall);
544 } else {
545 var st = (Struct) m.parent_symbol;
547 // memset needs string.h
548 cfile.add_include ("string.h");
549 var czero = new CCodeFunctionCall (new CCodeIdentifier ("memset"));
550 czero.add_argument (new CCodeIdentifier ("self"));
551 czero.add_argument (new CCodeConstant ("0"));
552 czero.add_argument (new CCodeIdentifier ("sizeof (%s)".printf (st.get_cname ())));
553 ccode.add_expression (czero);
557 if (context.module_init_method == m && in_plugin) {
558 // GTypeModule-based plug-in, register types
559 register_plugin_types (context.root, new HashSet<Symbol> ());
562 foreach (Expression precondition in m.get_preconditions ()) {
563 create_precondition_statement (m, creturn_type, precondition);
568 if (m.body != null) {
569 m.body.emit (this);
572 // generate *_real_* functions for virtual methods
573 // also generate them for abstract methods of classes to prevent faulty subclassing
574 if (!m.is_abstract || (m.is_abstract && current_type_symbol is Class)) {
575 /* Methods imported from a plain C file don't
576 * have a body, e.g. Vala.Parser.parse_file () */
577 if (m.body != null) {
578 if (current_method_inner_error) {
579 /* always separate error parameter and inner_error local variable
580 * as error may be set to NULL but we're always interested in inner errors
582 if (m.coroutine) {
583 closure_struct.add_field ("GError *", "_inner_error_");
585 // no initialization necessary, closure struct is zeroed
586 } else {
587 ccode.add_declaration ("GError *", new CCodeVariableDeclarator.zero ("_inner_error_", new CCodeConstant ("NULL")));
591 if (m.coroutine) {
592 // epilogue
593 complete_async ();
596 if (!(m.return_type is VoidType) && !m.return_type.is_real_non_null_struct_type () && !m.coroutine) {
597 // add dummy return if exit block is known to be unreachable to silence C compiler
598 if (m.return_block != null && m.return_block.get_predecessors ().size == 0) {
599 ccode.add_return (new CCodeIdentifier ("result"));
603 if (m is CreationMethod) {
604 if (current_type_symbol is Class) {
605 creturn_type = new ObjectType (current_class);
606 } else {
607 creturn_type = new VoidType ();
611 if (current_type_symbol is Class && gobject_type != null && current_class.is_subtype_of (gobject_type)
612 && current_class.get_type_parameters ().size > 0
613 && !((CreationMethod) m).chain_up) {
614 var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.GREATER_THAN, new CCodeIdentifier ("__params_it"), new CCodeIdentifier ("__params"));
615 var cdofreeparam = new CCodeBlock ();
616 cdofreeparam.add_statement (new CCodeExpressionStatement (new CCodeUnaryExpression (CCodeUnaryOperator.PREFIX_DECREMENT, new CCodeIdentifier ("__params_it"))));
617 var cunsetcall = new CCodeFunctionCall (new CCodeIdentifier ("g_value_unset"));
618 cunsetcall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeMemberAccess.pointer (new CCodeIdentifier ("__params_it"), "value")));
619 cdofreeparam.add_statement (new CCodeExpressionStatement (cunsetcall));
620 ccode.add_statement (new CCodeWhileStatement (ccond, cdofreeparam));
622 var cfreeparams = new CCodeFunctionCall (new CCodeIdentifier ("g_free"));
623 cfreeparams.add_argument (new CCodeIdentifier ("__params"));
624 ccode.add_expression (cfreeparams);
627 if (current_type_symbol is Class) {
628 CCodeExpression cresult = new CCodeIdentifier ("self");
629 if (get_custom_creturn_type (m) != null) {
630 cresult = new CCodeCastExpression (cresult, get_custom_creturn_type (m));
633 ccode.add_return (cresult);
637 cfile.add_function (ccode);
641 if (m.is_abstract && current_type_symbol is Class) {
642 // generate helpful error message if a sublcass does not implement an abstract method.
643 // This is only meaningful for subclasses implemented in C since the vala compiler would
644 // complain during compile time of such en error.
646 // add a typecheck statement for "self"
647 create_method_type_check_statement (m, creturn_type, current_type_symbol, true, "self");
649 // add critical warning that this method should not have been called
650 var type_from_instance_call = new CCodeFunctionCall (new CCodeIdentifier ("G_TYPE_FROM_INSTANCE"));
651 type_from_instance_call.add_argument (new CCodeIdentifier ("self"));
653 var type_name_call = new CCodeFunctionCall (new CCodeIdentifier ("g_type_name"));
654 type_name_call.add_argument (type_from_instance_call);
656 var error_string = "\"Type `%%s' does not implement abstract method `%s'\"".printf (m.get_cname ());
658 var cerrorcall = new CCodeFunctionCall (new CCodeIdentifier ("g_critical"));
659 cerrorcall.add_argument (new CCodeConstant (error_string));
660 cerrorcall.add_argument (type_name_call);
662 ccode.add_expression (cerrorcall);
664 // add return statement
665 return_default_value (creturn_type);
667 cfile.add_function (ccode);
671 in_static_or_class_context = false;
673 pop_context ();
675 if ((m.is_abstract || m.is_virtual) && !m.coroutine &&
676 /* If the method is a signal handler, the declaration
677 * is not needed. -- the name should be reserved for the
678 * emitter! */
679 m.signal_reference == null) {
681 cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
682 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
684 generate_vfunc (m, creturn_type, cparam_map, carg_map);
687 if (m.entry_point) {
688 // m is possible entry point, add appropriate startup code
689 var cmain = new CCodeFunction ("main", "int");
690 cmain.line = function.line;
691 cmain.add_parameter (new CCodeParameter ("argc", "int"));
692 cmain.add_parameter (new CCodeParameter ("argv", "char **"));
693 var main_block = new CCodeBlock ();
695 if (context.profile == Profile.GOBJECT) {
696 if (context.mem_profiler) {
697 var mem_profiler_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_mem_set_vtable"));
698 mem_profiler_init_call.line = cmain.line;
699 mem_profiler_init_call.add_argument (new CCodeConstant ("glib_mem_profiler_table"));
700 main_block.add_statement (new CCodeExpressionStatement (mem_profiler_init_call));
703 if (context.thread) {
704 var thread_init_call = new CCodeFunctionCall (new CCodeIdentifier ("g_thread_init"));
705 thread_init_call.line = cmain.line;
706 thread_init_call.add_argument (new CCodeConstant ("NULL"));
707 main_block.add_statement (new CCodeExpressionStatement (thread_init_call));
710 var type_init_call = new CCodeExpressionStatement (new CCodeFunctionCall (new CCodeIdentifier ("g_type_init")));
711 type_init_call.line = cmain.line;
712 main_block.add_statement (type_init_call);
715 var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name));
716 if (m.get_parameters ().size == 1) {
717 main_call.add_argument (new CCodeIdentifier ("argv"));
718 main_call.add_argument (new CCodeIdentifier ("argc"));
720 if (m.return_type is VoidType) {
721 // method returns void, always use 0 as exit code
722 var main_stmt = new CCodeExpressionStatement (main_call);
723 main_stmt.line = cmain.line;
724 main_block.add_statement (main_stmt);
725 var ret_stmt = new CCodeReturnStatement (new CCodeConstant ("0"));
726 ret_stmt.line = cmain.line;
727 main_block.add_statement (ret_stmt);
728 } else {
729 var main_stmt = new CCodeReturnStatement (main_call);
730 main_stmt.line = cmain.line;
731 main_block.add_statement (main_stmt);
733 cmain.block = main_block;
734 cfile.add_function (cmain);
738 public virtual CCodeParameter generate_parameter (Parameter param, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression>? carg_map) {
739 CCodeParameter cparam;
740 if (!param.ellipsis) {
741 string ctypename = param.variable_type.get_cname ();
743 generate_type_declaration (param.variable_type, decl_space);
745 // pass non-simple structs always by reference
746 if (param.variable_type.data_type is Struct) {
747 var st = (Struct) param.variable_type.data_type;
748 if (!st.is_simple_type () && param.direction == ParameterDirection.IN) {
749 if (st.is_immutable && !param.variable_type.value_owned) {
750 ctypename = "const " + ctypename;
753 if (!param.variable_type.nullable) {
754 ctypename += "*";
759 if (param.direction != ParameterDirection.IN) {
760 ctypename += "*";
763 cparam = new CCodeParameter (get_variable_cname (param.name), ctypename);
764 } else {
765 cparam = new CCodeParameter.with_ellipsis ();
768 cparam_map.set (get_param_pos (param.cparameter_position, param.ellipsis), cparam);
769 if (carg_map != null && !param.ellipsis) {
770 carg_map.set (get_param_pos (param.cparameter_position, param.ellipsis), get_variable_cexpression (param.name));
773 return cparam;
776 public override void generate_cparameters (Method m, CCodeFile decl_space, Map<int,CCodeParameter> cparam_map, CCodeFunction func, CCodeFunctionDeclarator? vdeclarator = null, Map<int,CCodeExpression>? carg_map = null, CCodeFunctionCall? vcall = null, int direction = 3) {
777 if (m.closure) {
778 var closure_block = current_closure_block;
779 int block_id = get_block_id (closure_block);
780 var instance_param = new CCodeParameter ("_data%d_".printf (block_id), "Block%dData*".printf (block_id));
781 cparam_map.set (get_param_pos (m.cinstance_parameter_position), instance_param);
782 } else if (m.parent_symbol is Class && m is CreationMethod) {
783 var cl = (Class) m.parent_symbol;
784 if (!cl.is_compact && vcall == null) {
785 cparam_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeParameter ("object_type", "GType"));
787 } else if (m.binding == MemberBinding.INSTANCE || (m.parent_symbol is Struct && m is CreationMethod)) {
788 TypeSymbol parent_type = find_parent_type (m);
789 DataType this_type;
790 if (parent_type is Class) {
791 this_type = new ObjectType ((Class) parent_type);
792 } else if (parent_type is Interface) {
793 this_type = new ObjectType ((Interface) parent_type);
794 } else if (parent_type is Struct) {
795 this_type = new StructValueType ((Struct) parent_type);
796 } else if (parent_type is Enum) {
797 this_type = new EnumValueType ((Enum) parent_type);
798 } else {
799 assert_not_reached ();
802 generate_type_declaration (this_type, decl_space);
804 CCodeParameter instance_param = null;
805 if (m.base_interface_method != null && !m.is_abstract && !m.is_virtual) {
806 var base_type = new ObjectType ((Interface) m.base_interface_method.parent_symbol);
807 instance_param = new CCodeParameter ("base", base_type.get_cname ());
808 } else if (m.overrides) {
809 var base_type = new ObjectType ((Class) m.base_method.parent_symbol);
810 instance_param = new CCodeParameter ("base", base_type.get_cname ());
811 } else {
812 if (m.parent_symbol is Struct && !((Struct) m.parent_symbol).is_simple_type ()) {
813 instance_param = new CCodeParameter ("*self", this_type.get_cname ());
814 } else {
815 instance_param = new CCodeParameter ("self", this_type.get_cname ());
818 cparam_map.set (get_param_pos (m.cinstance_parameter_position), instance_param);
819 } else if (m.binding == MemberBinding.CLASS) {
820 TypeSymbol parent_type = find_parent_type (m);
821 DataType this_type;
822 this_type = new ClassType ((Class) parent_type);
823 var class_param = new CCodeParameter ("klass", this_type.get_cname ());
824 cparam_map.set (get_param_pos (m.cinstance_parameter_position), class_param);
827 if (is_gtypeinstance_creation_method (m)) {
828 // memory management for generic types
829 int type_param_index = 0;
830 var cl = (Class) m.parent_symbol;
831 foreach (TypeParameter type_param in cl.get_type_parameters ()) {
832 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeParameter ("%s_type".printf (type_param.name.down ()), "GType"));
833 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
834 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
835 if (carg_map != null) {
836 carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
837 carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
838 carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
840 type_param_index++;
842 } else if (!m.closure) {
843 int type_param_index = 0;
844 foreach (var type_param in m.get_type_parameters ()) {
845 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeParameter ("%s_type".printf (type_param.name.down ()), "GType"));
846 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
847 cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
848 if (carg_map != null) {
849 carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
850 carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
851 carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
853 type_param_index++;
857 foreach (Parameter param in m.get_parameters ()) {
858 if (param.direction != ParameterDirection.OUT) {
859 if ((direction & 1) == 0) {
860 // no in paramters
861 continue;
863 } else {
864 if ((direction & 2) == 0) {
865 // no out paramters
866 continue;
870 generate_parameter (param, decl_space, cparam_map, carg_map);
873 if ((direction & 2) != 0) {
874 generate_method_result_declaration (m, decl_space, func, cparam_map, carg_map);
877 // append C parameters in the right order
878 int last_pos = -1;
879 int min_pos;
880 while (true) {
881 min_pos = -1;
882 foreach (int pos in cparam_map.get_keys ()) {
883 if (pos > last_pos && (min_pos == -1 || pos < min_pos)) {
884 min_pos = pos;
887 if (min_pos == -1) {
888 break;
890 func.add_parameter (cparam_map.get (min_pos));
891 if (vdeclarator != null) {
892 vdeclarator.add_parameter (cparam_map.get (min_pos));
894 if (vcall != null) {
895 var arg = carg_map.get (min_pos);
896 if (arg != null) {
897 vcall.add_argument (arg);
900 last_pos = min_pos;
904 public void generate_vfunc (Method m, DataType return_type, Map<int,CCodeParameter> cparam_map, Map<int,CCodeExpression> carg_map, string suffix = "", int direction = 3) {
905 push_context (new EmitContext ());
907 string cname = m.get_cname ();
908 if (suffix == "_finish" && cname.has_suffix ("_async")) {
909 cname = cname.substring (0, cname.length - "_async".length);
911 var vfunc = new CCodeFunction (cname + suffix);
913 CCodeFunctionCall vcast = null;
914 if (m.parent_symbol is Interface) {
915 var iface = (Interface) m.parent_symbol;
917 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_INTERFACE".printf (iface.get_upper_case_cname (null))));
918 } else {
919 var cl = (Class) m.parent_symbol;
921 vcast = new CCodeFunctionCall (new CCodeIdentifier ("%s_GET_CLASS".printf (cl.get_upper_case_cname (null))));
923 vcast.add_argument (new CCodeIdentifier ("self"));
925 cname = m.vfunc_name;
926 if (suffix == "_finish" && cname.has_suffix ("_async")) {
927 cname = cname.substring (0, cname.length - "_async".length);
929 var vcall = new CCodeFunctionCall (new CCodeMemberAccess.pointer (vcast, cname + suffix));
930 carg_map.set (get_param_pos (m.cinstance_parameter_position), new CCodeIdentifier ("self"));
932 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall, direction);
934 push_function (vfunc);
936 foreach (Expression precondition in m.get_preconditions ()) {
937 create_precondition_statement (m, return_type, precondition);
940 if (return_type is VoidType || return_type.is_real_non_null_struct_type ()) {
941 ccode.add_expression (vcall);
942 } else if (m.get_postconditions ().size == 0) {
943 /* pass method return value */
944 ccode.add_return (vcall);
945 } else {
946 /* store method return value for postconditions */
947 ccode.add_declaration (get_creturn_type (m, return_type.get_cname ()), new CCodeVariableDeclarator ("result"));
948 ccode.add_assignment (new CCodeIdentifier ("result"), vcall);
951 if (m.get_postconditions ().size > 0) {
952 foreach (Expression postcondition in m.get_postconditions ()) {
953 create_postcondition_statement (postcondition);
956 if (!(return_type is VoidType)) {
957 ccode.add_return (new CCodeIdentifier ("result"));
961 cfile.add_function (vfunc);
963 pop_context ();
966 private void create_method_type_check_statement (Method m, DataType return_type, TypeSymbol t, bool non_null, string var_name) {
967 if (!m.coroutine) {
968 create_type_check_statement (m, return_type, t, non_null, var_name);
972 private void create_precondition_statement (CodeNode method_node, DataType ret_type, Expression precondition) {
973 var ccheck = new CCodeFunctionCall ();
975 precondition.emit (this);
977 ccheck.add_argument (get_cvalue (precondition));
979 if (method_node is CreationMethod) {
980 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
981 ccheck.add_argument (new CCodeConstant ("NULL"));
982 } else if (method_node is Method && ((Method) method_node).coroutine) {
983 // _co function
984 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
985 ccheck.add_argument (new CCodeConstant ("FALSE"));
986 } else if (ret_type is VoidType) {
987 /* void function */
988 ccheck.call = new CCodeIdentifier ("g_return_if_fail");
989 } else {
990 ccheck.call = new CCodeIdentifier ("g_return_val_if_fail");
992 var cdefault = default_value_for_type (ret_type, false);
993 if (cdefault != null) {
994 ccheck.add_argument (cdefault);
995 } else {
996 return;
1000 ccode.add_expression (ccheck);
1003 private TypeSymbol? find_parent_type (Symbol sym) {
1004 while (sym != null) {
1005 if (sym is TypeSymbol) {
1006 return (TypeSymbol) sym;
1008 sym = sym.parent_symbol;
1010 return null;
1013 public override void visit_creation_method (CreationMethod m) {
1014 bool visible = !m.is_private_symbol ();
1016 visit_method (m);
1018 if (m.source_type == SourceFileType.FAST) {
1019 return;
1022 // do not generate _new functions for creation methods of abstract classes
1023 if (current_type_symbol is Class && !current_class.is_compact && !current_class.is_abstract) {
1024 var vfunc = new CCodeFunction (m.get_cname ());
1026 var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal);
1027 var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal);
1029 var vblock = new CCodeBlock ();
1031 var vcall = new CCodeFunctionCall (new CCodeIdentifier (m.get_real_cname ()));
1032 vcall.add_argument (new CCodeIdentifier (current_class.get_type_id ()));
1034 generate_cparameters (m, cfile, cparam_map, vfunc, null, carg_map, vcall);
1035 CCodeStatement cstmt = new CCodeReturnStatement (vcall);
1036 cstmt.line = vfunc.line;
1037 vblock.add_statement (cstmt);
1039 if (!visible) {
1040 vfunc.modifiers |= CCodeModifiers.STATIC;
1043 vfunc.block = vblock;
1045 cfile.add_function (vfunc);
1050 // vim:sw=8 noet