1 /* valaccodememberaccessmodule.vala
3 * Copyright (C) 2006-2009 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>
27 internal class Vala
.CCodeMemberAccessModule
: CCodeControlFlowModule
{
28 public CCodeMemberAccessModule (CCodeGenerator codegen
, CCodeModule? next
) {
32 public override void visit_member_access (MemberAccess expr
) {
33 expr
.accept_children (codegen
);
35 CCodeExpression pub_inst
= null;
36 DataType base_type
= null;
38 if (expr
.inner
!= null) {
39 pub_inst
= (CCodeExpression
) expr
.inner
.ccodenode
;
41 if (expr
.inner
.value_type
!= null) {
42 base_type
= expr
.inner
.value_type
;
46 if (expr
.symbol_reference is Method
) {
47 var m
= (Method
) expr
.symbol_reference
;
49 if (!(m is DynamicMethod
|| m is ArrayMoveMethod
|| m is ArrayResizeMethod
)) {
50 generate_method_declaration (m
, source_declarations
);
52 if (!m
.external
&& m
.external_package
) {
53 // internal VAPI methods
54 // only add them once per source file
55 if (add_generated_external_symbol (m
)) {
61 if (expr
.inner is BaseAccess
) {
62 if (m
.base_method
!= null) {
63 var base_class
= (Class
) m
.base_method
.parent_symbol
;
64 var vcast
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_CLASS".printf (base_class
.get_upper_case_cname (null))));
65 vcast
.add_argument (new
CCodeIdentifier ("%s_parent_class".printf (current_class
.get_lower_case_cname (null))));
67 expr
.ccodenode
= new CCodeMemberAccess
.pointer (vcast
, m
.vfunc_name
);
69 } else if (m
.base_interface_method
!= null) {
70 var base_iface
= (Interface
) m
.base_interface_method
.parent_symbol
;
71 string parent_iface_var
= "%s_%s_parent_iface".printf (current_class
.get_lower_case_cname (null), base_iface
.get_lower_case_cname (null));
73 expr
.ccodenode
= new CCodeMemberAccess
.pointer (new
CCodeIdentifier (parent_iface_var
), m
.vfunc_name
);
78 if (m
.base_method
!= null) {
79 if (!head
.method_has_wrapper (m
.base_method
)) {
81 if (expr
.inner
!= null && !expr
.inner
.is_pure ()) {
82 // instance expression has side-effects
83 // store in temp. variable
84 var temp_var
= get_temp_variable (expr
.inner
.value_type
);
85 temp_vars
.insert (0, temp_var
);
86 var ctemp
= get_variable_cexpression (temp_var
.name
);
87 inst
= new
CCodeAssignment (ctemp
, pub_inst
);
88 expr
.inner
.ccodenode
= ctemp
;
90 var base_class
= (Class
) m
.base_method
.parent_symbol
;
91 var vclass
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_GET_CLASS".printf (base_class
.get_upper_case_cname (null))));
92 vclass
.add_argument (inst
);
93 expr
.ccodenode
= new CCodeMemberAccess
.pointer (vclass
, m
.name
);
95 expr
.ccodenode
= new
CCodeIdentifier (m
.base_method
.get_cname ());
97 } else if (m
.base_interface_method
!= null) {
98 expr
.ccodenode
= new
CCodeIdentifier (m
.base_interface_method
.get_cname ());
99 } else if (m is CreationMethod
) {
100 expr
.ccodenode
= new
CCodeIdentifier (m
.get_real_cname ());
102 expr
.ccodenode
= new
CCodeIdentifier (m
.get_cname ());
104 } else if (expr
.symbol_reference is ArrayLengthField
) {
105 expr
.ccodenode
= head
.get_array_length_cexpression (expr
.inner
, 1);
106 } else if (expr
.symbol_reference is Field
) {
107 var f
= (Field
) expr
.symbol_reference
;
108 if (f
.binding
== MemberBinding
.INSTANCE
) {
109 var instance_target_type
= get_data_type_for_symbol ((TypeSymbol
) f
.parent_symbol
);
111 var cl
= instance_target_type
.data_type as Class
;
112 bool is_gtypeinstance
= ((instance_target_type
.data_type
== cl
) && (cl
== null || !cl
.is_compact
));
114 CCodeExpression inst
;
115 if (is_gtypeinstance
&& f
.access
== SymbolAccessibility
.PRIVATE
) {
116 inst
= new CCodeMemberAccess
.pointer (pub_inst
, "priv");
119 generate_class_struct_declaration (cl
, source_declarations
);
123 if (instance_target_type
.data_type
.is_reference_type () || (expr
.inner
!= null && expr
.inner
.value_type is PointerType
)) {
124 expr
.ccodenode
= new CCodeMemberAccess
.pointer (inst
, f
.get_cname ());
126 expr
.ccodenode
= new
CCodeMemberAccess (inst
, f
.get_cname ());
128 } else if (f
.binding
== MemberBinding
.CLASS
) {
129 var cl
= (Class
) f
.parent_symbol
;
130 var cast
= new
CCodeFunctionCall (new
CCodeIdentifier (cl
.get_upper_case_cname (null) + "_CLASS"));
132 CCodeExpression klass
;
133 if (expr
.inner
== null) {
134 if (in_static_or_class_context
) {
135 // Accessing the field from a static or class constructor
136 klass
= new
CCodeIdentifier ("klass");
138 // Accessing the field from within an instance method
139 var k
= new
CCodeFunctionCall (new
CCodeIdentifier ("G_OBJECT_GET_CLASS"));
140 k
.add_argument (new
CCodeIdentifier ("self"));
144 // Accessing the field of an instance
145 var k
= new
CCodeFunctionCall (new
CCodeIdentifier ("G_OBJECT_GET_CLASS"));
146 k
.add_argument ((CCodeExpression
) expr
.inner
.ccodenode
);
149 cast
.add_argument (klass
);
151 if (f
.access
== SymbolAccessibility
.PRIVATE
) {
152 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_GET_CLASS_PRIVATE".printf (cl
.get_upper_case_cname ())));
153 var ccall2
= new
CCodeFunctionCall (new
CCodeIdentifier ("G_TYPE_FROM_CLASS"));
154 ccall2
.add_argument (cast
);
155 ccall
.add_argument (ccall2
);
156 expr
.ccodenode
= new CCodeMemberAccess
.pointer (ccall
, f
.get_cname ());
158 expr
.ccodenode
= new CCodeMemberAccess
.pointer (cast
, f
.get_cname ());
162 generate_field_declaration (f
, source_declarations
);
164 expr
.ccodenode
= new
CCodeIdentifier (f
.get_cname ());
166 } else if (expr
.symbol_reference is Constant
) {
167 var c
= (Constant
) expr
.symbol_reference
;
169 generate_constant_declaration (c
, source_declarations
);
171 string fn
= c
.get_full_name ();
172 if (fn
== "GLib.Log.FILE") {
173 string s
= Path
.get_basename (expr
.source_reference
.file
.filename
);
174 expr
.ccodenode
= new
CCodeConstant ("\"%s\"".printf (s
));
175 } else if (fn
== "GLib.Log.LINE") {
176 int i
= expr
.source_reference
.first_line
;
177 expr
.ccodenode
= new
CCodeConstant ("%d".printf (i
));
178 } else if (fn
== "GLib.Log.METHOD") {
180 if (current_method
!= null) {
181 s
= current_method
.get_full_name ();
183 expr
.ccodenode
= new
CCodeConstant ("\"%s\"".printf (s
));
185 expr
.ccodenode
= new
CCodeIdentifier (c
.get_cname ());
187 } else if (expr
.symbol_reference is Property
) {
188 var prop
= (Property
) expr
.symbol_reference
;
190 if (!(prop is DynamicProperty
)) {
191 generate_property_accessor_declaration (prop
.get_accessor
, source_declarations
);
193 if (!prop
.external
&& prop
.external_package
) {
194 // internal VAPI properties
195 // only add them once per source file
196 if (add_generated_external_symbol (prop
)) {
197 visit_property (prop
);
202 if (expr
.inner is BaseAccess
) {
203 if (prop
.base_property
!= null) {
204 var base_class
= (Class
) prop
.base_property
.parent_symbol
;
205 var vcast
= new
CCodeFunctionCall (new
CCodeIdentifier ("%s_CLASS".printf (base_class
.get_upper_case_cname (null))));
206 vcast
.add_argument (new
CCodeIdentifier ("%s_parent_class".printf (current_class
.get_lower_case_cname (null))));
208 var ccall
= new
CCodeFunctionCall (new CCodeMemberAccess
.pointer (vcast
, "get_%s".printf (prop
.name
)));
209 ccall
.add_argument ((CCodeExpression
) expr
.inner
.ccodenode
);
210 expr
.ccodenode
= ccall
;
212 } else if (prop
.base_interface_property
!= null) {
213 var base_iface
= (Interface
) prop
.base_interface_property
.parent_symbol
;
214 string parent_iface_var
= "%s_%s_parent_iface".printf (current_class
.get_lower_case_cname (null), base_iface
.get_lower_case_cname (null));
216 var ccall
= new
CCodeFunctionCall (new CCodeMemberAccess
.pointer (new
CCodeIdentifier (parent_iface_var
), "get_%s".printf (prop
.name
)));
217 ccall
.add_argument ((CCodeExpression
) expr
.inner
.ccodenode
);
218 expr
.ccodenode
= ccall
;
223 if (prop
.get_accessor
.automatic_body
&&
224 current_type_symbol
== prop
.parent_symbol
&&
225 prop
.base_property
== null &&
226 prop
.base_interface_property
== null &&
227 !(prop
.property_type is ArrayType
|| prop
.property_type is DelegateType
)) {
228 CCodeExpression inst
;
229 inst
= new CCodeMemberAccess
.pointer (pub_inst
, "priv");
230 expr
.ccodenode
= new CCodeMemberAccess
.pointer (inst
, prop
.field
.get_cname());
231 } else if (!prop
.no_accessor_method
) {
232 var base_property
= prop
;
233 if (prop
.base_property
!= null) {
234 base_property
= prop
.base_property
;
235 } else if (prop
.base_interface_property
!= null) {
236 base_property
= prop
.base_interface_property
;
239 if (prop is DynamicProperty
) {
240 getter_cname
= head
.get_dynamic_property_getter_cname ((DynamicProperty
) prop
);
242 getter_cname
= base_property
.get_accessor
.get_cname ();
244 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier (getter_cname
));
246 if (prop
.binding
== MemberBinding
.INSTANCE
) {
247 if (prop
.parent_symbol is Struct
) {
248 // we need to pass struct instance by reference
249 var unary
= pub_inst as CCodeUnaryExpression
;
250 if (unary
!= null && unary
.operator
== CCodeUnaryOperator
.POINTER_INDIRECTION
) {
252 pub_inst
= unary
.inner
;
253 } else if (pub_inst is CCodeIdentifier
|| pub_inst is CCodeMemberAccess
) {
254 pub_inst
= new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, pub_inst
);
256 // if instance is e.g. a function call, we can't take the address of the expression
257 // (tmp = expr, &tmp)
258 var ccomma
= new
CCodeCommaExpression ();
260 var temp_var
= get_temp_variable (expr
.inner
.target_type
);
261 temp_vars
.insert (0, temp_var
);
262 ccomma
.append_expression (new
CCodeAssignment (get_variable_cexpression (temp_var
.name
), pub_inst
));
263 ccomma
.append_expression (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, get_variable_cexpression (temp_var
.name
)));
269 ccall
.add_argument (pub_inst
);
272 // Property access to real struct types is handled differently
273 // The value is returned by out parameter
274 if (base_property
.property_type
.is_real_struct_type ()) {
275 var ccomma
= new
CCodeCommaExpression ();
276 var temp_var
= get_temp_variable (base_property
.get_accessor
.value_type
);
277 var ctemp
= get_variable_cexpression (temp_var
.name
);
278 temp_vars
.add (temp_var
);
279 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
280 ccomma
.append_expression (ccall
);
281 ccomma
.append_expression (ctemp
);
282 expr
.ccodenode
= ccomma
;
284 var array_type
= base_property
.property_type as ArrayType
;
285 if (array_type
!= null && !base_property
.no_array_length
) {
286 for (int dim
= 1; dim
<= array_type
.rank
; dim
++) {
287 var temp_var
= get_temp_variable (int_type
);
288 var ctemp
= get_variable_cexpression (temp_var
.name
);
289 temp_vars
.add (temp_var
);
290 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
291 expr
.append_array_size (ctemp
);
294 var delegate_type
= base_property
.property_type as DelegateType
;
295 if (delegate_type
!= null && delegate_type
.delegate_symbol
.has_target
) {
296 var temp_var
= get_temp_variable (new
PointerType (new
VoidType ()));
297 var ctemp
= get_variable_cexpression (temp_var
.name
);
298 temp_vars
.add (temp_var
);
299 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
300 expr
.delegate_target
= ctemp
;
304 expr
.ccodenode
= ccall
;
307 var ccall
= new
CCodeFunctionCall (new
CCodeIdentifier ("g_object_get"));
308 ccall
.add_argument (pub_inst
);
310 // property name is second argument of g_object_get
311 ccall
.add_argument (prop
.get_canonical_cconstant ());
313 // g_object_get always returns owned values
314 // therefore, property getters of properties
315 // without accessor methods need to be marked as owned
316 if (!prop
.get_accessor
.value_type
.value_owned
) {
317 // only report error for types where there actually
318 // is a difference between `owned' and `unowned'
319 var owned_value_type
= prop
.get_accessor
.value_type
.copy ();
320 owned_value_type
.value_owned
= true;
321 if (requires_copy (owned_value_type
)) {
322 Report
.error (prop
.get_accessor
.source_reference
, "unowned return value for getter of property `%s' not supported without accessor".printf (prop
.get_full_name ()));
326 var ccomma
= new
CCodeCommaExpression ();
327 var temp_var
= get_temp_variable (expr
.value_type
);
328 var ctemp
= get_variable_cexpression (temp_var
.name
);
329 temp_vars
.add (temp_var
);
330 ccall
.add_argument (new
CCodeUnaryExpression (CCodeUnaryOperator
.ADDRESS_OF
, ctemp
));
331 ccall
.add_argument (new
CCodeConstant ("NULL"));
332 ccomma
.append_expression (ccall
);
333 ccomma
.append_expression (ctemp
);
334 expr
.ccodenode
= ccomma
;
336 } else if (expr
.symbol_reference is EnumValue
) {
337 var ev
= (EnumValue
) expr
.symbol_reference
;
339 generate_enum_declaration ((Enum
) ev
.parent_symbol
, source_declarations
);
341 expr
.ccodenode
= new
CCodeConstant (ev
.get_cname ());
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 expr
.ccodenode
= new
CCodeIdentifier ("result");
347 } else if (local
.captured
) {
348 // captured variables are stored on the heap
349 var block
= (Block
) local
.parent_symbol
;
350 expr
.ccodenode
= new CCodeMemberAccess
.pointer (new
CCodeIdentifier ("_data%d_".printf (get_block_id (block
))), get_variable_cname (local
.name
));
352 expr
.ccodenode
= get_variable_cexpression (local
.name
);
354 } else if (expr
.symbol_reference is FormalParameter
) {
355 var p
= (FormalParameter
) expr
.symbol_reference
;
356 if (p
.name
== "this") {
357 if (current_method
!= null && current_method
.coroutine
) {
359 expr
.ccodenode
= new CCodeMemberAccess
.pointer (new
CCodeIdentifier ("data"), "self");
361 var st
= current_type_symbol as Struct
;
362 if (st
!= null && !st
.is_simple_type ()) {
363 expr
.ccodenode
= new
CCodeIdentifier ("(*self)");
365 expr
.ccodenode
= new
CCodeIdentifier ("self");
370 // captured variables are stored on the heap
371 var block
= ((Method
) p
.parent_symbol
).body
;
372 expr
.ccodenode
= new CCodeMemberAccess
.pointer (new
CCodeIdentifier ("_data%d_".printf (get_block_id (block
))), get_variable_cname (p
.name
));
373 } else if (current_method
!= null && current_method
.coroutine
) {
375 expr
.ccodenode
= get_variable_cexpression (p
.name
);
377 var type_as_struct
= p
.parameter_type
.data_type as Struct
;
378 if (p
.direction
!= ParameterDirection
.IN
379 || (type_as_struct
!= null && !type_as_struct
.is_simple_type () && !p
.parameter_type
.nullable
)) {
380 expr
.ccodenode
= new
CCodeIdentifier ("(*%s)".printf (get_variable_cname (p
.name
)));
382 // Property setters of non simple structs shall replace all occurences
383 // of the "value" formal parameter with a dereferencing version of that
385 if (current_property_accessor
!= null &&
386 current_property_accessor
.writable
&&
387 current_property_accessor
.value_parameter
== p
&&
388 current_property_accessor
.prop
.property_type
.is_real_struct_type ()) {
389 expr
.ccodenode
= new
CCodeIdentifier ("(*value)");
391 expr
.ccodenode
= get_variable_cexpression (p
.name
);