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
20 * Jürg Billeter <j@bitron.ch>
21 * Raffaele Sandrini <raffaele@sandrini.ch>
28 * The link between a method and generated code.
30 internal class Vala
.CCodeMethodModule
: CCodeStructModule
{
31 public CCodeMethodModule (CCodeGenerator codegen
, CCodeModule? 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");
42 string type
= attr
.get_string ("type");
50 string get_creturn_type (Method m
, string default_value
) {
51 string type
= get_custom_creturn_type (m
);
58 bool is_gtypeinstance_creation_method (Method m
) {
61 var cl
= m
.parent_symbol as Class
;
62 if (m is CreationMethod
&& cl
!= null && !cl
.is_compact
) {
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
;
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
;
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
) {
170 if (decl_space
.add_symbol_declaration (m
, m
.get_cname ())) {
174 var function
= new
CCodeFunction (m
.get_cname ());
176 if (m
.is_private_symbol ()) {
177 function
.modifiers
|= CCodeModifiers
.STATIC
;
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
;
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
);
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
) {
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
);
283 cblock
.add_statement (stmt
.ccodenode
);
285 if (last_stmt
== stmt
) {
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
) {
301 if (stmt
.ccodenode is CCodeFragment
) {
302 foreach (CCodeNode cstmt
in ((CCodeFragment
) stmt
.ccodenode
).get_children ()) {
303 cblock
.add_statement (cstmt
);
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
;
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
;
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
)) {
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
);
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")));
405 cswitch
.add_statement (function
.block
);
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
);
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
);
422 var parent_closure_block
= next_closure_block (closure_block
.parent_symbol
);
423 if (parent_closure_block
== null) {
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
)) {
451 ReferenceType base_expression_type
;
453 base_method
= m
.base_method
;
454 base_expression_type
= new
ObjectType ((Class
) base_method
.parent_symbol
);
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
) {
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
);
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
515 closure_struct
.add_field ("GError *", "_inner_error_");
517 // no initialization necessary, closure struct is zeroed
519 var cdecl
= new
CCodeDeclaration ("GError *");
520 cdecl
.add_declarator (new
CCodeVariableDeclarator ("_inner_error_", new
CCodeConstant ("NULL")));
521 cinit
.append (cdecl
);
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
));
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
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
);
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
);
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
) {
756 if (param
.direction
!= ParameterDirection
.IN
) {
760 param
.ccodenode
= new
CCodeFormalParameter (get_variable_cname (param
.name
), ctypename
);
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) {
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
);
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
);
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 ());
807 if (m
.parent_symbol is Struct
&& !((Struct
) m
.parent_symbol
).is_simple_type ()) {
808 instance_param
= new
CCodeFormalParameter ("*self", this_type
.get_cname ());
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
);
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 ())));
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 ())));
852 foreach (FormalParameter param
in m
.get_parameters ()) {
853 if (param
.direction
!= ParameterDirection
.OUT
) {
854 if ((direction
& 1) == 0) {
859 if ((direction
& 2) == 0) {
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
877 foreach (int pos
in cparam_map
.get_keys ()) {
878 if (pos
> last_pos
&& (min_pos
== -1 || pos
< min_pos
)) {
885 func
.add_parameter (cparam_map
.get (min_pos
));
886 if (vdeclarator
!= null) {
887 vdeclarator
.add_parameter (cparam_map
.get (min_pos
));
890 var arg
= carg_map
.get (min_pos
);
892 vcall
.add_argument (arg
);
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))));
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
);
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
));
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
) {
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
) {
978 ccheck
.call
= new
CCodeIdentifier ("g_return_if_fail");
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
);
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
;
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"));
1014 ccall
.add_argument (new
CCodeConstant ("__params_it - __params"));
1015 ccall
.add_argument (new
CCodeConstant ("__params"));
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
);
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
);
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
);