GAsync: Another temp variable fix
[vala-lang.git] / codegen / valaccodememberaccessmodule.vala
blob6c7dc86f4778b9c0b267522fac49388911a2994c
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
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Raffaele Sandrini <raffaele@sandrini.ch>
25 using GLib;
27 internal class Vala.CCodeMemberAccessModule : CCodeControlFlowModule {
28 public CCodeMemberAccessModule (CCodeGenerator codegen, CCodeModule? next) {
29 base (codegen, 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)) {
56 visit_method (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);
68 return;
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);
74 return;
78 if (m.base_method != null) {
79 if (!head.method_has_wrapper (m.base_method)) {
80 var inst = pub_inst;
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);
94 } else {
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 ());
101 } else {
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");
117 } else {
118 if (cl != null) {
119 generate_class_struct_declaration (cl, source_declarations);
121 inst = pub_inst;
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 ());
125 } else {
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");
137 } else {
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"));
141 klass = k;
143 } else {
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);
147 klass = k;
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 ());
157 } else {
158 expr.ccodenode = new CCodeMemberAccess.pointer (cast, f.get_cname ());
161 } else {
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") {
179 string s = "";
180 if (current_method != null) {
181 s = current_method.get_full_name ();
183 expr.ccodenode = new CCodeConstant ("\"%s\"".printf (s));
184 } else {
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;
211 return;
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;
219 return;
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;
238 string getter_cname;
239 if (prop is DynamicProperty) {
240 getter_cname = head.get_dynamic_property_getter_cname ((DynamicProperty) prop);
241 } else {
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) {
251 // *expr => expr
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);
255 } else {
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)));
265 pub_inst = ccomma;
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;
283 } else {
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);
293 } else {
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;
306 } else {
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));
351 } else {
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) {
358 // use closure
359 expr.ccodenode = new CCodeMemberAccess.pointer (new CCodeIdentifier ("data"), "self");
360 } else {
361 var st = current_type_symbol as Struct;
362 if (st != null && !st.is_simple_type ()) {
363 expr.ccodenode = new CCodeIdentifier ("(*self)");
364 } else {
365 expr.ccodenode = new CCodeIdentifier ("self");
368 } else {
369 if (p.captured) {
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) {
374 // use closure
375 expr.ccodenode = get_variable_cexpression (p.name);
376 } else {
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)));
381 } else {
382 // Property setters of non simple structs shall replace all occurences
383 // of the "value" formal parameter with a dereferencing version of that
384 // parameter.
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)");
390 } else {
391 expr.ccodenode = get_variable_cexpression (p.name);