Move source file adding logic to CodeContext
[vala-lang.git] / codegen / valaccodememberaccessmodule.vala
blobfbdbbe5a90a53986926ffe9bfb0018e3e0a0fdc5
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
20 * Author:
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)) {
43 visit_method (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);
55 return;
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);
61 return;
65 if (m.base_method != null) {
66 if (!method_has_wrapper (m.base_method)) {
67 var inst = pub_inst;
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);
81 } else {
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 ());
88 } else {
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");
107 } else {
108 if (cl != null) {
109 generate_class_struct_declaration (cl, cfile);
111 inst = pub_inst;
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 ());
115 } else {
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;
122 } else {
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");
135 } else {
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"));
139 klass = k;
141 } else {
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);
145 klass = k;
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 ());
153 } else {
154 expr.ccodenode = new CCodeMemberAccess.pointer (cast, f.get_cname ());
157 } else {
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") {
183 string s = "";
184 if (current_method != null) {
185 s = current_method.get_full_name ();
187 expr.ccodenode = new CCodeConstant ("\"%s\"".printf (s));
188 } else {
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;
215 return;
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;
223 return;
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;
244 string getter_cname;
245 if (prop is DynamicProperty) {
246 getter_cname = get_dynamic_property_getter_cname ((DynamicProperty) prop);
247 } else {
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) {
257 // *expr => expr
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);
261 } else {
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)));
271 pub_inst = ccomma;
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;
289 } else {
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);
299 } else {
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;
312 } else {
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"));
349 } else {
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));
356 } else {
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) {
377 // use closure
378 expr.ccodenode = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "self");
379 } else {
380 var st = current_type_symbol as Struct;
381 if (st != null && !st.is_simple_type ()) {
382 expr.ccodenode = new CCodeIdentifier ("(*self)");
383 } else {
384 expr.ccodenode = new CCodeIdentifier ("self");
387 } else {
388 if (p.captured) {
389 // captured variables are stored on the heap
390 var block = p.parent_symbol as Block;
391 if (block == null) {
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) {
396 // use closure
397 expr.ccodenode = get_variable_cexpression (p.name);
398 } else {
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)));
403 } else {
404 // Property setters of non simple structs shall replace all occurences
405 // of the "value" formal parameter with a dereferencing version of that
406 // parameter.
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"));
413 } else {
414 expr.ccodenode = get_variable_cexpression (p.name);