1 /* valaccodememberaccessmodule.vala
3 * Copyright (C) 2006-2010 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
25 public class Vala
.CCodeMemberAccessModule
: CCodeControlFlowModule
{
26 public override void visit_member_access (MemberAccess expr
) {
27 CCodeExpression pub_inst
= null;
29 if (expr
.inner
!= null) {
30 pub_inst
= (CCodeExpression
) expr
.inner
.ccodenode
;
33 if (expr
.symbol_reference is Method
) {
34 var m
= (Method
) expr
.symbol_reference
;
36 if (!(m is DynamicMethod
|| m is ArrayMoveMethod
|| m is ArrayResizeMethod
)) {
37 generate_method_declaration (m
, cfile
);
39 if (!m
.external
&& m
.external_package
) {
40 // internal VAPI methods
41 // only add them once per source file
42 if (add_generated_external_symbol (m
)) {
48 if (expr
.inner is BaseAccess
) {
49 if (m
.base_method
!= null) {
50 var base_class
= (Class
) m
.base_method
.parent_symbol
;
51 var vcast
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_CLASS".printf (base_class
.get_upper_case_cname (null))));
52 vcast
.add_argument (new
CCodeIdentifier ("%s_parent_class".printf (current_class
.get_lower_case_cname (null))));
54 expr
.ccodenode
= new CCodeMemberAccess
.pointer (vcast
, m
.vfunc_name
);
56 } else if (m
.base_interface_method
!= null) {
57 var base_iface
= (Interface
) m
.base_interface_method
.parent_symbol
;
58 string parent_iface_var
= "%s_%s_parent_iface".printf (current_class
.get_lower_case_cname (null), base_iface
.get_lower_case_cname (null));
60 expr
.ccodenode
= new CCodeMemberAccess
.pointer (new
CCodeIdentifier (parent_iface_var
), m
.vfunc_name
);
65 if (m
.base_method
!= null) {
66 if (!method_has_wrapper (m
.base_method
)) {
68 if (expr
.inner
!= null && !expr
.inner
.is_pure ()) {
69 // instance expression has side-effects
70 // store in temp. variable
71 var temp_var
= get_temp_variable (expr
.inner
.value_type
, true, null, false);
72 emit_temp_var (temp_var
);
73 var ctemp
= get_variable_cexpression (temp_var
.name
);
74 inst
= new
CCodeAssignment (ctemp
, pub_inst
);
75 expr
.inner
.ccodenode
= ctemp
;
77 var base_class
= (Class
) m
.base_method
.parent_symbol
;
78 var vclass
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_GET_CLASS".printf (base_class
.get_upper_case_cname (null))));
79 vclass
.add_argument (inst
);
80 expr
.ccodenode
= new CCodeMemberAccess
.pointer (vclass
, m
.name
);
82 expr
.ccodenode
= new
CCodeIdentifier (m
.base_method
.get_cname ());
84 } else if (m
.base_interface_method
!= null) {
85 expr
.ccodenode
= new
CCodeIdentifier (m
.base_interface_method
.get_cname ());
86 } else if (m is CreationMethod
) {
87 expr
.ccodenode
= new
CCodeIdentifier (m
.get_real_cname ());
89 expr
.ccodenode
= new
CCodeIdentifier (m
.get_cname ());
91 } else if (expr
.symbol_reference is ArrayLengthField
) {
92 if (expr
.value_type is ArrayType
&& !(expr
.parent_node is ElementAccess
)) {
93 Report
.error (expr
.source_reference
, "unsupported use of length field of multi-dimensional array");
95 expr
.ccodenode
= get_array_length_cexpression (expr
.inner
, 1);
96 } else if (expr
.symbol_reference is Field
) {
97 var f
= (Field
) expr
.symbol_reference
;
98 if (f
.binding
== MemberBinding
.INSTANCE
) {
99 var instance_target_type
= get_data_type_for_symbol ((TypeSymbol
) f
.parent_symbol
);
101 var cl
= instance_target_type
.data_type as Class
;
102 bool is_gtypeinstance
= ((instance_target_type
.data_type
== cl
) && (cl
== null || !cl
.is_compact
));
104 CCodeExpression inst
;
105 if (is_gtypeinstance
&& f
.access
== SymbolAccessibility
.PRIVATE
) {
106 inst
= new CCodeMemberAccess
.pointer (pub_inst
, "priv");
109 generate_class_struct_declaration (cl
, cfile
);
113 if (instance_target_type
.data_type
.is_reference_type () || (expr
.inner
!= null && expr
.inner
.value_type is PointerType
)) {
114 expr
.ccodenode
= new CCodeMemberAccess
.pointer (inst
, f
.get_cname ());
116 if (inst is CCodeCommaExpression
) {
117 var ccomma
= inst as CCodeCommaExpression
;
118 var inner
= ccomma
.get_inner ();
119 var last
= inner
.get (inner
.size
- 1);
120 ccomma
.set_expression (inner
.size
- 1, new
CCodeMemberAccess (last
, f
.get_cname ()));
121 expr
.ccodenode
= ccomma
;
123 expr
.ccodenode
= new
CCodeMemberAccess (inst
, f
.get_cname ());
126 } else if (f
.binding
== MemberBinding
.CLASS
) {
127 var cl
= (Class
) f
.parent_symbol
;
128 var cast
= new
CCodeFunctionCall (new
CCodeIdentifier (cl
.get_upper_case_cname (null) + "_CLASS"));
130 CCodeExpression klass
;
131 if (expr
.inner
== null) {
132 if (in_static_or_class_context
) {
133 // Accessing the field from a static or class constructor
134 klass
= new
CCodeIdentifier ("klass");
136 // Accessing the field from within an instance method
137 var k
= new
CCodeFunctionCall (new
CCodeIdentifier ("G_OBJECT_GET_CLASS"));
138 k
.add_argument (new
CCodeIdentifier ("self"));
142 // Accessing the field of an instance
143 var k
= new
CCodeFunctionCall (new
CCodeIdentifier ("G_OBJECT_GET_CLASS"));
144 k
.add_argument ((CCodeExpression
) expr
.inner
.ccodenode
);
147 cast
.add_argument (klass
);
149 if (f
.access
== SymbolAccessibility
.PRIVATE
) {
150 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf (cl
.get_upper_case_cname ())));
151 ccall
.add_argument (klass
);
152 expr
.ccodenode
= new CCodeMemberAccess
.pointer (ccall
, f
.get_cname ());
154 expr
.ccodenode
= new CCodeMemberAccess
.pointer (cast
, f
.get_cname ());
158 generate_field_declaration (f
, cfile
);
160 expr
.ccodenode
= new
CCodeIdentifier (f
.get_cname ());
162 } else if (expr
.symbol_reference is EnumValue
) {
163 var ev
= (EnumValue
) expr
.symbol_reference
;
165 generate_enum_declaration ((Enum
) ev
.parent_symbol
, cfile
);
167 expr
.ccodenode
= new
CCodeConstant (ev
.get_cname ());
168 } else if (expr
.symbol_reference is Constant
) {
169 var c
= (Constant
) expr
.symbol_reference
;
171 generate_constant_declaration (c
, cfile
,
172 c
.source_reference
!= null && expr
.source_reference
!= null &&
173 c
.source_reference
.file
== expr
.source_reference
.file
);
175 string fn
= c
.get_full_name ();
176 if (fn
== "GLib.Log.FILE") {
177 string s
= Path
.get_basename (expr
.source_reference
.file
.filename
);
178 expr
.ccodenode
= new
CCodeConstant ("\"%s\"".printf (s
));
179 } else if (fn
== "GLib.Log.LINE") {
180 int i
= expr
.source_reference
.first_line
;
181 expr
.ccodenode
= new
CCodeConstant ("%d".printf (i
));
182 } else if (fn
== "GLib.Log.METHOD") {
184 if (current_method
!= null) {
185 s
= current_method
.get_full_name ();
187 expr
.ccodenode
= new
CCodeConstant ("\"%s\"".printf (s
));
189 expr
.ccodenode
= new
CCodeIdentifier (c
.get_cname ());
191 } else if (expr
.symbol_reference is Property
) {
192 var prop
= (Property
) expr
.symbol_reference
;
194 if (!(prop is DynamicProperty
)) {
195 generate_property_accessor_declaration (prop
.get_accessor
, cfile
);
197 if (!prop
.external
&& prop
.external_package
) {
198 // internal VAPI properties
199 // only add them once per source file
200 if (add_generated_external_symbol (prop
)) {
201 visit_property (prop
);
206 if (expr
.inner is BaseAccess
) {
207 if (prop
.base_property
!= null) {
208 var base_class
= (Class
) prop
.base_property
.parent_symbol
;
209 var vcast
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_CLASS".printf (base_class
.get_upper_case_cname (null))));
210 vcast
.add_argument (new
CCodeIdentifier ("%s_parent_class".printf (current_class
.get_lower_case_cname (null))));
212 var ccall
= new
CCodeFunctionCall (new CCodeMemberAccess
.pointer (vcast
, "get_%s".printf (prop
.name
)));
213 ccall
.add_argument ((CCodeExpression
) expr
.inner
.ccodenode
);
214 expr
.ccodenode
= ccall
;
216 } else if (prop
.base_interface_property
!= null) {
217 var base_iface
= (Interface
) prop
.base_interface_property
.parent_symbol
;
218 string parent_iface_var
= "%s_%s_parent_iface".printf (current_class
.get_lower_case_cname (null), base_iface
.get_lower_case_cname (null));
220 var ccall
= new
CCodeFunctionCall (new CCodeMemberAccess
.pointer (new
CCodeIdentifier (parent_iface_var
), "get_%s".printf (prop
.name
)));
221 ccall
.add_argument ((CCodeExpression
) expr
.inner
.ccodenode
);
222 expr
.ccodenode
= ccall
;
227 if (prop
.binding
== MemberBinding
.INSTANCE
&&
228 prop
.get_accessor
.automatic_body
&&
229 current_type_symbol
== prop
.parent_symbol
&&
230 current_type_symbol is Class
&&
231 prop
.base_property
== null &&
232 prop
.base_interface_property
== null &&
233 !(prop
.property_type is ArrayType
|| prop
.property_type is DelegateType
)) {
234 CCodeExpression inst
;
235 inst
= new CCodeMemberAccess
.pointer (pub_inst
, "priv");
236 expr
.ccodenode
= new CCodeMemberAccess
.pointer (inst
, prop
.field
.get_cname());
237 } else if (!prop
.no_accessor_method
) {
238 var base_property
= prop
;
239 if (prop
.base_property
!= null) {
240 base_property
= prop
.base_property
;
241 } else if (prop
.base_interface_property
!= null) {
242 base_property
= prop
.base_interface_property
;
245 if (prop is DynamicProperty
) {
246 getter_cname
= get_dynamic_property_getter_cname ((DynamicProperty
) prop
);
248 getter_cname
= base_property
.get_accessor
.get_cname ();
250 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier (getter_cname
));
252 if (prop
.binding
== MemberBinding
.INSTANCE
) {
253 if (prop
.parent_symbol is Struct
) {
254 // we need to pass struct instance by reference
255 var unary
= pub_inst as CCodeUnaryExpression
;
256 if (unary
!= null && unary
.operator
== CCodeUnaryOperator
.POINTER_INDIRECTION
) {
258 pub_inst
= unary
.inner
;
259 } else if (pub_inst is CCodeIdentifier
|| pub_inst is CCodeMemberAccess
) {
260 pub_inst
= new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, pub_inst
);
262 // if instance is e.g. a function call, we can't take the address of the expression
263 // (tmp = expr, &tmp)
264 var ccomma
= new
CCodeCommaExpression ();
266 var temp_var
= get_temp_variable (expr
.inner
.target_type
, true, null, false);
267 emit_temp_var (temp_var
);
268 ccomma
.append_expression (new
CCodeAssignment (get_variable_cexpression (temp_var
.name
), pub_inst
));
269 ccomma
.append_expression (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, get_variable_cexpression (temp_var
.name
)));
275 ccall
.add_argument (pub_inst
);
278 // Property access to real struct types is handled differently
279 // The value is returned by out parameter
280 if (base_property
.property_type
.is_real_non_null_struct_type ()) {
281 var ccomma
= new
CCodeCommaExpression ();
282 var temp_var
= get_temp_variable (base_property
.get_accessor
.value_type
);
283 var ctemp
= get_variable_cexpression (temp_var
.name
);
284 emit_temp_var (temp_var
);
285 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
286 ccomma
.append_expression (ccall
);
287 ccomma
.append_expression (ctemp
);
288 expr
.ccodenode
= ccomma
;
290 var array_type
= base_property
.property_type as ArrayType
;
291 if (array_type
!= null && !base_property
.no_array_length
) {
292 for (int dim
= 1; dim
<= array_type
.rank
; dim
++) {
293 var temp_var
= get_temp_variable (int_type
);
294 var ctemp
= get_variable_cexpression (temp_var
.name
);
295 emit_temp_var (temp_var
);
296 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
297 expr
.append_array_size (ctemp
);
300 var delegate_type
= base_property
.property_type as DelegateType
;
301 if (delegate_type
!= null && delegate_type
.delegate_symbol
.has_target
) {
302 var temp_var
= get_temp_variable (new
PointerType (new
VoidType ()));
303 var ctemp
= get_variable_cexpression (temp_var
.name
);
304 emit_temp_var (temp_var
);
305 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
306 expr
.delegate_target
= ctemp
;
310 expr
.ccodenode
= ccall
;
313 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_object_get"));
314 ccall
.add_argument (pub_inst
);
316 // property name is second argument of g_object_get
317 ccall
.add_argument (prop
.get_canonical_cconstant ());
319 // g_object_get always returns owned values
320 // therefore, property getters of properties
321 // without accessor methods need to be marked as owned
322 if (!prop
.get_accessor
.value_type
.value_owned
) {
323 // only report error for types where there actually
324 // is a difference between `owned' and `unowned'
325 var owned_value_type
= prop
.get_accessor
.value_type
.copy ();
326 owned_value_type
.value_owned
= true;
327 if (requires_copy (owned_value_type
)) {
328 Report
.error (prop
.get_accessor
.source_reference
, "unowned return value for getter of property `%s' not supported without accessor".printf (prop
.get_full_name ()));
332 var ccomma
= new
CCodeCommaExpression ();
333 var temp_var
= get_temp_variable (expr
.value_type
);
334 var ctemp
= get_variable_cexpression (temp_var
.name
);
335 emit_temp_var (temp_var
);
336 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
337 ccall
.add_argument (new
CCodeConstant ("NULL"));
338 ccomma
.append_expression (ccall
);
339 ccomma
.append_expression (ctemp
);
340 expr
.ccodenode
= ccomma
;
342 } else if (expr
.symbol_reference is LocalVariable
) {
343 var local
= (LocalVariable
) expr
.symbol_reference
;
344 if (local
.is_result
) {
345 // used in postconditions
346 // structs are returned as out parameter
347 if (local
.variable_type
!= null && local
.variable_type
.is_real_non_null_struct_type ()) {
348 expr
.ccodenode
= new
CCodeUnaryExpression (CCodeUnaryOperator
.POINTER_INDIRECTION
, new
CCodeIdentifier ("result"));
350 expr
.ccodenode
= new
CCodeIdentifier ("result");
352 } else if (local
.captured
) {
353 // captured variables are stored on the heap
354 var block
= (Block
) local
.parent_symbol
;
355 expr
.ccodenode
= new CCodeMemberAccess
.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block
))), get_variable_cname (local
.name
));
357 expr
.ccodenode
= get_variable_cexpression (local
.name
);
359 if (expr
.parent_node is ReturnStatement
&&
360 current_return_type
.value_owned
&&
361 local
.variable_type
.value_owned
&&
362 !variable_accessible_in_finally (local
)) {
363 /* return expression is local variable taking ownership and
364 * current method is transferring ownership */
366 // don't ref expression
367 expr
.value_type
.value_owned
= true;
369 // don't unref variable
370 local
.active
= false;
373 } else if (expr
.symbol_reference is FormalParameter
) {
374 var p
= (FormalParameter
) expr
.symbol_reference
;
375 if (p
.name
== "this") {
376 if (current_method
!= null && current_method
.coroutine
) {
378 expr
.ccodenode
= new CCodeMemberAccess
.pointer (new
CCodeIdentifier ("data"), "self");
380 var st
= current_type_symbol as Struct
;
381 if (st
!= null && !st
.is_simple_type ()) {
382 expr
.ccodenode
= new
CCodeIdentifier ("(*self)");
384 expr
.ccodenode
= new
CCodeIdentifier ("self");
389 // captured variables are stored on the heap
390 var block
= p
.parent_symbol as Block
;
392 block
= ((Method
) p
.parent_symbol
).body
;
394 expr
.ccodenode
= new CCodeMemberAccess
.pointer (get_variable_cexpression ("_data%d_".printf (get_block_id (block
))), get_variable_cname (p
.name
));
395 } else if (current_method
!= null && current_method
.coroutine
) {
397 expr
.ccodenode
= get_variable_cexpression (p
.name
);
399 var type_as_struct
= p
.variable_type
.data_type as Struct
;
400 if (p
.direction
!= ParameterDirection
.IN
401 || (type_as_struct
!= null && !type_as_struct
.is_simple_type () && !p
.variable_type
.nullable
)) {
402 expr
.ccodenode
= new
CCodeUnaryExpression (CCodeUnaryOperator
.POINTER_INDIRECTION
, new
CCodeIdentifier (get_variable_cname (p
.name
)));
404 // Property setters of non simple structs shall replace all occurences
405 // of the "value" formal parameter with a dereferencing version of that
407 if (current_property_accessor
!= null &&
408 current_property_accessor
.writable
&&
409 current_property_accessor
.value_parameter
== p
&&
410 current_property_accessor
.prop
.property_type
.is_real_struct_type () &&
411 !current_property_accessor
.prop
.property_type
.nullable
) {
412 expr
.ccodenode
= new
CCodeUnaryExpression (CCodeUnaryOperator
.POINTER_INDIRECTION
, new
CCodeIdentifier ("value"));
414 expr
.ccodenode
= get_variable_cexpression (p
.name
);