1 /* valamemberaccess.vala
3 * Copyright (C) 2006-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>
27 * Represents an access to a type member in the source code.
29 public class Vala
.MemberAccess
: Expression
{
31 * The parent of the member.
33 public Expression? inner
{
40 _inner
.parent_node
= this
;
46 * The name of the member.
48 public string member_name
{ get; set; }
51 * Pointer member access.
53 public bool pointer_member_access
{ get; set; }
56 * Represents access to an instance member without an actual instance,
57 * e.g. `MyClass.an_instance_method`.
59 public bool prototype_access
{ get; set; }
62 * Specifies whether the member is used for object creation.
64 public bool creation_member
{ get; set; }
67 * Qualified access to global symbol.
69 public bool qualified
{ get; set; }
71 private Expression? _inner
;
72 private Gee
.List
<DataType
> type_argument_list
= new ArrayList
<DataType
> ();
75 * Creates a new member access expression.
77 * @param inner parent of the member
78 * @param member_name member name
79 * @param source_reference reference to source code
80 * @return newly created member access expression
82 public MemberAccess (Expression? inner
, string member_name
, SourceReference? source_reference
= null) {
84 this
.member_name
= member_name
;
85 this
.source_reference
= source_reference
;
88 public MemberAccess
.simple (string member_name
, SourceReference? source_reference
= null) {
89 this
.member_name
= member_name
;
90 this
.source_reference
= source_reference
;
93 public MemberAccess
.pointer (Expression inner
, string member_name
, SourceReference? source_reference
= null) {
95 this
.member_name
= member_name
;
96 this
.source_reference
= source_reference
;
97 pointer_member_access
= true;
101 * Appends the specified type as generic type argument.
103 * @param arg a type reference
105 public void add_type_argument (DataType arg
) {
106 type_argument_list
.add (arg
);
107 arg
.parent_node
= this
;
111 * Returns a copy of the list of generic type arguments.
113 * @return type argument list
115 public Gee
.List
<DataType
> get_type_arguments () {
116 return new ReadOnlyList
<DataType
> (type_argument_list
);
119 public override void accept (CodeVisitor visitor
) {
120 visitor
.visit_member_access (this
);
122 visitor
.visit_expression (this
);
125 public override void accept_children (CodeVisitor visitor
) {
127 inner
.accept (visitor
);
130 foreach (DataType type_arg
in type_argument_list
) {
131 type_arg
.accept (visitor
);
135 public override string to_string () {
139 return "%s.%s".printf (inner
.to_string (), member_name
);
143 public override void replace_expression (Expression old_node
, Expression new_node
) {
144 if (inner
== old_node
) {
149 public override bool is_pure () {
150 // accessing property could have side-effects
151 return (inner
== null || inner
.is_pure ()) && !(symbol_reference is Property
);
154 public override void replace_type (DataType old_type
, DataType new_type
) {
155 for (int i
= 0; i
< type_argument_list
.size
; i
++) {
156 if (type_argument_list
[i
] == old_type
) {
157 type_argument_list
[i
] = new_type
;
163 public override bool is_constant () {
164 if (symbol_reference is Constant
) {
171 public override bool is_non_null () {
172 var c
= symbol_reference as Constant
;
174 return !c
.type_reference
.nullable
;
180 public override bool check (SemanticAnalyzer analyzer
) {
188 inner
.check (analyzer
);
191 foreach (DataType type_arg
in type_argument_list
) {
192 type_arg
.check (analyzer
);
195 Symbol base_symbol
= null;
196 FormalParameter this_parameter
= null;
197 bool may_access_instance_members
= false;
199 symbol_reference
= null;
202 base_symbol
= analyzer
.root_symbol
;
203 symbol_reference
= analyzer
.root_symbol
.scope
.lookup (member_name
);
204 } else if (inner
== null) {
205 if (member_name
== "this") {
206 if (!analyzer
.is_in_instance_method ()) {
208 Report
.error (source_reference
, "This access invalid outside of instance methods");
213 base_symbol
= analyzer
.current_symbol
;
215 var sym
= analyzer
.current_symbol
;
216 while (sym
!= null && symbol_reference
== null) {
217 if (this_parameter
== null) {
218 if (sym is CreationMethod
) {
219 var cm
= (CreationMethod
) sym
;
220 this_parameter
= cm
.this_parameter
;
221 may_access_instance_members
= true;
222 } else if (sym is Property
) {
223 var prop
= (Property
) sym
;
224 this_parameter
= prop
.this_parameter
;
225 may_access_instance_members
= true;
226 } else if (sym is Constructor
) {
227 var c
= (Constructor
) sym
;
228 this_parameter
= c
.this_parameter
;
229 may_access_instance_members
= true;
230 } else if (sym is Destructor
) {
231 var d
= (Destructor
) sym
;
232 this_parameter
= d
.this_parameter
;
233 may_access_instance_members
= true;
234 } else if (sym is Method
) {
235 var m
= (Method
) sym
;
236 this_parameter
= m
.this_parameter
;
237 may_access_instance_members
= (m
.binding
== MemberBinding
.INSTANCE
);
241 symbol_reference
= analyzer
.symbol_lookup_inherited (sym
, member_name
);
242 sym
= sym
.parent_symbol
;
245 if (symbol_reference
== null) {
246 foreach (UsingDirective ns
in analyzer
.current_source_file
.get_using_directives ()) {
247 var local_sym
= ns
.namespace_symbol
.scope
.lookup (member_name
);
248 if (local_sym
!= null) {
249 if (symbol_reference
!= null) {
251 Report
.error (source_reference
, "`%s' is an ambiguous reference between `%s' and `%s'".printf (member_name
, symbol_reference
.get_full_name (), local_sym
.get_full_name ()));
254 symbol_reference
= local_sym
;
260 /* if there was an error in the inner expression, skip this check */
265 if (pointer_member_access
) {
266 var pointer_type
= inner
.value_type as PointerType
;
267 if (pointer_type
!= null && pointer_type
.base_type is ValueType
) {
268 // transform foo->bar to (*foo).bar
269 inner
= new
PointerIndirection (inner
, source_reference
);
270 inner
.check (analyzer
);
271 pointer_member_access
= false;
275 if (inner is MemberAccess
) {
276 var ma
= (MemberAccess
) inner
;
277 if (ma
.prototype_access
) {
279 Report
.error (source_reference
, "Access to instance member `%s' denied".printf (inner
.symbol_reference
.get_full_name ()));
284 if (inner is MemberAccess
|| inner is BaseAccess
) {
285 base_symbol
= inner
.symbol_reference
;
287 if (symbol_reference
== null && (base_symbol is Namespace
|| base_symbol is TypeSymbol
)) {
288 symbol_reference
= base_symbol
.scope
.lookup (member_name
);
289 if (inner is BaseAccess
) {
290 // inner expression is base access
291 // access to instance members of the base type possible
292 may_access_instance_members
= true;
297 if (symbol_reference
== null && inner
.value_type
!= null) {
298 if (pointer_member_access
) {
299 symbol_reference
= inner
.value_type
.get_pointer_member (member_name
);
301 if (inner
.value_type
.data_type
!= null) {
302 base_symbol
= inner
.value_type
.data_type
;
304 symbol_reference
= inner
.value_type
.get_member (member_name
);
306 if (symbol_reference
!= null) {
307 // inner expression is variable, field, or parameter
308 // access to instance members of the corresponding type possible
309 may_access_instance_members
= true;
313 if (symbol_reference
== null && inner
.value_type
!= null && inner
.value_type
.is_dynamic
) {
314 // allow late bound members for dynamic types
315 var dynamic_object_type
= (ObjectType
) inner
.value_type
;
316 if (parent_node is MethodCall
) {
317 var invoc
= (MethodCall
) parent_node
;
318 if (invoc
.call
== this
) {
321 if (invoc
.target_type
!= null) {
322 ret_type
= invoc
.target_type
.copy ();
323 ret_type
.value_owned
= true;
324 } else if (invoc
.parent_node is ExpressionStatement
) {
325 ret_type
= new
VoidType ();
327 // expect dynamic object of the same type
328 ret_type
= inner
.value_type
.copy ();
330 var m
= new
DynamicMethod (inner
.value_type
, member_name
, ret_type
, source_reference
);
331 m
.invocation
= invoc
;
332 m
.add_error_type (new
ErrorType (null, null));
333 m
.access
= SymbolAccessibility
.PUBLIC
;
334 m
.add_parameter (new FormalParameter
.with_ellipsis ());
335 dynamic_object_type
.type_symbol
.scope
.add (null, m
);
336 symbol_reference
= m
;
338 } else if (parent_node is Assignment
) {
339 var a
= (Assignment
) parent_node
;
341 && (a
.operator
== AssignmentOperator
.ADD
342 || a
.operator
== AssignmentOperator
.SUB
)) {
344 var s
= new
DynamicSignal (inner
.value_type
, member_name
, new
VoidType (), source_reference
);
346 s
.access
= SymbolAccessibility
.PUBLIC
;
347 dynamic_object_type
.type_symbol
.scope
.add (null, s
);
348 symbol_reference
= s
;
349 } else if (a
.left
== this
) {
350 // dynamic property assignment
351 var prop
= new
DynamicProperty (inner
.value_type
, member_name
, source_reference
);
352 prop
.access
= SymbolAccessibility
.PUBLIC
;
353 prop
.set_accessor
= new
PropertyAccessor (false, true, false, null, null, prop
.source_reference
);
354 prop
.set_accessor
.access
= SymbolAccessibility
.PUBLIC
;
355 prop
.owner
= inner
.value_type
.data_type
.scope
;
356 dynamic_object_type
.type_symbol
.scope
.add (null, prop
);
357 symbol_reference
= prop
;
360 if (symbol_reference
== null) {
361 // dynamic property read access
362 var prop
= new
DynamicProperty (inner
.value_type
, member_name
, source_reference
);
363 if (target_type
!= null) {
364 prop
.property_type
= target_type
;
366 // expect dynamic object of the same type
367 prop
.property_type
= inner
.value_type
.copy ();
369 prop
.access
= SymbolAccessibility
.PUBLIC
;
370 prop
.get_accessor
= new
PropertyAccessor (true, false, false, prop
.property_type
.copy (), null, prop
.source_reference
);
371 prop
.get_accessor
.access
= SymbolAccessibility
.PUBLIC
;
372 prop
.owner
= inner
.value_type
.data_type
.scope
;
373 dynamic_object_type
.type_symbol
.scope
.add (null, prop
);
374 symbol_reference
= prop
;
376 if (symbol_reference
!= null) {
377 may_access_instance_members
= true;
382 if (symbol_reference
== null) {
385 string base_type_name
= "(null)";
386 if (inner
!= null && inner
.value_type
!= null) {
387 base_type_name
= inner
.value_type
.to_string ();
388 } else if (base_symbol
!= null) {
389 base_type_name
= base_symbol
.get_full_name ();
392 Report
.error (source_reference
, "The name `%s' does not exist in the context of `%s'".printf (member_name
, base_type_name
));
396 var member
= symbol_reference
;
397 var access
= SymbolAccessibility
.PUBLIC
;
398 bool instance
= false;
401 if (!member
.check (analyzer
)) {
405 if (member is Field
) {
406 var f
= (Field
) member
;
408 instance
= (f
.binding
== MemberBinding
.INSTANCE
);
409 klass
= (f
.binding
== MemberBinding
.CLASS
);
410 } else if (member is Method
) {
411 var m
= (Method
) member
;
413 if (!(m is CreationMethod
)) {
414 instance
= (m
.binding
== MemberBinding
.INSTANCE
);
416 klass
= (m
.binding
== MemberBinding
.CLASS
);
417 } else if (member is Property
) {
418 var prop
= (Property
) member
;
419 if (!prop
.check (analyzer
)) {
423 access
= prop
.access
;
425 if (prop
.set_accessor
== null) {
427 Report
.error (source_reference
, "Property `%s' is read-only".printf (prop
.get_full_name ()));
430 if (prop
.access
== SymbolAccessibility
.PUBLIC
) {
431 access
= prop
.set_accessor
.access
;
432 } else if (prop
.access
== SymbolAccessibility
.PROTECTED
433 && prop
.set_accessor
.access
!= SymbolAccessibility
.PUBLIC
) {
434 access
= prop
.set_accessor
.access
;
437 if (prop
.get_accessor
== null) {
439 Report
.error (source_reference
, "Property `%s' is write-only".printf (prop
.get_full_name ()));
442 if (prop
.access
== SymbolAccessibility
.PUBLIC
) {
443 access
= prop
.get_accessor
.access
;
444 } else if (prop
.access
== SymbolAccessibility
.PROTECTED
445 && prop
.get_accessor
.access
!= SymbolAccessibility
.PUBLIC
) {
446 access
= prop
.get_accessor
.access
;
449 instance
= (prop
.binding
== MemberBinding
.INSTANCE
);
450 } else if (member is Signal
) {
456 if (access
== SymbolAccessibility
.PRIVATE
) {
457 var target_type
= member
.parent_symbol
;
459 bool in_target_type
= false;
460 for (Symbol this_symbol
= analyzer
.current_symbol
; this_symbol
!= null; this_symbol
= this_symbol
.parent_symbol
) {
461 if (target_type
== this_symbol
) {
462 in_target_type
= true;
467 if (!in_target_type
) {
469 Report
.error (source_reference
, "Access to private member `%s' denied".printf (member
.get_full_name ()));
473 if ((instance
|| klass
) && !may_access_instance_members
) {
474 prototype_access
= true;
476 if (symbol_reference is Method
) {
477 // also set static type for prototype access
478 // required when using instance methods as delegates in constants
479 // TODO replace by MethodPrototype
480 value_type
= analyzer
.get_value_type_for_symbol (symbol_reference
, lvalue
);
481 } else if (symbol_reference is Field
) {
482 value_type
= new
FieldPrototype ((Field
) symbol_reference
);
484 value_type
= new
InvalidType ();
487 // implicit this access
488 if (instance
&& inner
== null) {
489 inner
= new
MemberAccess (null, "this", source_reference
);
490 inner
.value_type
= this_parameter
.parameter_type
.copy ();
491 inner
.symbol_reference
= this_parameter
;
494 formal_value_type
= analyzer
.get_value_type_for_symbol (symbol_reference
, lvalue
);
495 if (inner
!= null && formal_value_type
!= null) {
496 value_type
= formal_value_type
.get_actual_type (inner
.value_type
, this
);
498 value_type
= formal_value_type
;
501 if (symbol_reference is Method
) {
502 var m
= (Method
) symbol_reference
;
505 if (m
.base_method
!= null) {
506 base_method
= m
.base_method
;
507 } else if (m
.base_interface_method
!= null) {
508 base_method
= m
.base_interface_method
;
513 if (instance
&& base_method
.parent_symbol
!= null) {
514 inner
.target_type
= analyzer
.get_data_type_for_symbol ((TypeSymbol
) base_method
.parent_symbol
);
516 } else if (symbol_reference is Property
) {
517 var prop
= (Property
) symbol_reference
;
519 Property base_property
;
520 if (prop
.base_property
!= null) {
521 base_property
= prop
.base_property
;
522 } else if (prop
.base_interface_property
!= null) {
523 base_property
= prop
.base_interface_property
;
525 base_property
= prop
;
528 if (instance
&& base_property
.parent_symbol
!= null) {
529 inner
.target_type
= analyzer
.get_data_type_for_symbol ((TypeSymbol
) base_property
.parent_symbol
);
531 } else if ((symbol_reference is Field
532 || symbol_reference is Signal
)
533 && instance
&& symbol_reference
.parent_symbol
!= null) {
534 inner
.target_type
= analyzer
.get_data_type_for_symbol ((TypeSymbol
) symbol_reference
.parent_symbol
);
538 analyzer
.current_source_file
.add_symbol_dependency (symbol_reference
, SourceFileDependencyType
.SOURCE
);
543 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
545 inner
.get_defined_variables (collection
);
549 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
551 inner
.get_used_variables (collection
);
553 var local
= symbol_reference as LocalVariable
;
555 collection
.add (local
);