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>
26 * Represents an access to a type member in the source code.
28 public class Vala
.MemberAccess
: Expression
{
30 * The parent of the member.
32 public Expression? inner
{
39 _inner
.parent_node
= this
;
45 * The name of the member.
47 public string member_name
{ get; set; }
50 * Pointer member access.
52 public bool pointer_member_access
{ get; set; }
55 * Represents access to an instance member without an actual instance,
56 * e.g. `MyClass.an_instance_method`.
58 public bool prototype_access
{ get; set; }
61 * Specifies whether the member is used for object creation.
63 public bool creation_member
{ get; set; }
66 * Qualified access to global symbol.
68 public bool qualified
{ get; set; }
70 private Expression? _inner
;
71 private List
<DataType
> type_argument_list
= new ArrayList
<DataType
> ();
74 * Creates a new member access expression.
76 * @param inner parent of the member
77 * @param member_name member name
78 * @param source_reference reference to source code
79 * @return newly created member access expression
81 public MemberAccess (Expression? inner
, string member_name
, SourceReference? source_reference
= null) {
83 this
.member_name
= member_name
;
84 this
.source_reference
= source_reference
;
87 public MemberAccess
.simple (string member_name
, SourceReference? source_reference
= null) {
88 this
.member_name
= member_name
;
89 this
.source_reference
= source_reference
;
92 public MemberAccess
.pointer (Expression inner
, string member_name
, SourceReference? source_reference
= null) {
94 this
.member_name
= member_name
;
95 this
.source_reference
= source_reference
;
96 pointer_member_access
= true;
100 * Appends the specified type as generic type argument.
102 * @param arg a type reference
104 public void add_type_argument (DataType arg
) {
105 type_argument_list
.add (arg
);
106 arg
.parent_node
= this
;
110 * Returns a copy of the list of generic type arguments.
112 * @return type argument list
114 public List
<DataType
> get_type_arguments () {
115 return new ReadOnlyList
<DataType
> (type_argument_list
);
118 public override void accept (CodeVisitor visitor
) {
119 visitor
.visit_member_access (this
);
121 visitor
.visit_expression (this
);
124 public override void accept_children (CodeVisitor visitor
) {
126 inner
.accept (visitor
);
129 foreach (DataType type_arg
in type_argument_list
) {
130 type_arg
.accept (visitor
);
134 public override string to_string () {
135 if (symbol_reference
.is_instance_member ()) {
139 return "%s.%s".printf (inner
.to_string (), member_name
);
142 // ensure to always use fully-qualified name
143 // to refer to static members
144 return symbol_reference
.get_full_name ();
148 public override void replace_expression (Expression old_node
, Expression new_node
) {
149 if (inner
== old_node
) {
154 public override bool is_pure () {
155 // accessing property could have side-effects
156 return (inner
== null || inner
.is_pure ()) && !(symbol_reference is Property
);
159 public override void replace_type (DataType old_type
, DataType new_type
) {
160 for (int i
= 0; i
< type_argument_list
.size
; i
++) {
161 if (type_argument_list
[i
] == old_type
) {
162 type_argument_list
[i
] = new_type
;
168 public override bool is_constant () {
169 if (symbol_reference is Constant
|| symbol_reference is EnumValue
) {
176 public override bool is_non_null () {
177 var c
= symbol_reference as Constant
;
179 return !c
.type_reference
.nullable
;
185 public override bool check (SemanticAnalyzer analyzer
) {
193 inner
.check (analyzer
);
196 foreach (DataType type_arg
in type_argument_list
) {
197 type_arg
.check (analyzer
);
200 Symbol base_symbol
= null;
201 FormalParameter this_parameter
= null;
202 bool may_access_instance_members
= false;
203 bool may_access_klass_members
= false;
205 symbol_reference
= null;
208 base_symbol
= analyzer
.root_symbol
;
209 symbol_reference
= analyzer
.root_symbol
.scope
.lookup (member_name
);
210 } else if (inner
== null) {
211 if (member_name
== "this") {
212 if (!analyzer
.is_in_instance_method ()) {
214 Report
.error (source_reference
, "This access invalid outside of instance methods");
219 base_symbol
= analyzer
.current_symbol
;
221 var sym
= analyzer
.current_symbol
;
222 while (sym
!= null && symbol_reference
== null) {
223 if (this_parameter
== null) {
224 if (sym is CreationMethod
) {
225 var cm
= (CreationMethod
) sym
;
226 this_parameter
= cm
.this_parameter
;
227 may_access_instance_members
= true;
228 may_access_klass_members
= true;
229 } else if (sym is Property
) {
230 var prop
= (Property
) sym
;
231 this_parameter
= prop
.this_parameter
;
232 may_access_instance_members
= (prop
.binding
== MemberBinding
.INSTANCE
);
233 may_access_klass_members
= (prop
.binding
!= MemberBinding
.STATIC
);
234 } else if (sym is Constructor
) {
235 var c
= (Constructor
) sym
;
236 this_parameter
= c
.this_parameter
;
237 may_access_instance_members
= (c
.binding
== MemberBinding
.INSTANCE
);
238 may_access_klass_members
= true;
239 } else if (sym is Destructor
) {
240 var d
= (Destructor
) sym
;
241 this_parameter
= d
.this_parameter
;
242 may_access_instance_members
= (d
.binding
== MemberBinding
.INSTANCE
);
243 may_access_klass_members
= true;
244 } else if (sym is Method
) {
245 var m
= (Method
) sym
;
246 this_parameter
= m
.this_parameter
;
247 may_access_instance_members
= (m
.binding
== MemberBinding
.INSTANCE
);
248 may_access_klass_members
= (m
.binding
!= MemberBinding
.STATIC
);
252 symbol_reference
= analyzer
.symbol_lookup_inherited (sym
, member_name
);
253 sym
= sym
.parent_symbol
;
256 if (symbol_reference
== null && source_reference
!= null) {
257 foreach (UsingDirective ns
in source_reference
.using_directives
) {
258 var local_sym
= ns
.namespace_symbol
.scope
.lookup (member_name
);
259 if (local_sym
!= null) {
260 if (symbol_reference
!= null && symbol_reference
!= local_sym
) {
262 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 ()));
265 symbol_reference
= local_sym
;
271 /* if there was an error in the inner expression, skip this check */
276 if (pointer_member_access
) {
277 var pointer_type
= inner
.value_type as PointerType
;
278 if (pointer_type
!= null && pointer_type
.base_type is ValueType
) {
279 // transform foo->bar to (*foo).bar
280 inner
= new
PointerIndirection (inner
, source_reference
);
281 inner
.check (analyzer
);
282 pointer_member_access
= false;
286 if (inner is MemberAccess
) {
287 var ma
= (MemberAccess
) inner
;
288 if (ma
.prototype_access
) {
290 Report
.error (source_reference
, "Access to instance member `%s' denied".printf (inner
.symbol_reference
.get_full_name ()));
295 if (inner is MemberAccess
|| inner is BaseAccess
) {
296 base_symbol
= inner
.symbol_reference
;
298 if (symbol_reference
== null && (base_symbol is Namespace
|| base_symbol is TypeSymbol
)) {
299 symbol_reference
= base_symbol
.scope
.lookup (member_name
);
300 if (inner is BaseAccess
) {
301 // inner expression is base access
302 // access to instance members of the base type possible
303 may_access_instance_members
= true;
304 may_access_klass_members
= true;
309 if (symbol_reference
== null && inner
.value_type
!= null) {
310 if (pointer_member_access
) {
311 symbol_reference
= inner
.value_type
.get_pointer_member (member_name
);
313 if (inner
.value_type
.data_type
!= null) {
314 base_symbol
= inner
.value_type
.data_type
;
316 symbol_reference
= inner
.value_type
.get_member (member_name
);
318 if (symbol_reference
!= null) {
319 // inner expression is variable, field, or parameter
320 // access to instance members of the corresponding type possible
321 may_access_instance_members
= true;
322 may_access_klass_members
= true;
326 if (symbol_reference
== null && inner
.value_type
!= null && inner
.value_type
.is_dynamic
) {
327 // allow late bound members for dynamic types
328 var dynamic_object_type
= (ObjectType
) inner
.value_type
;
329 if (parent_node is MethodCall
) {
330 var invoc
= (MethodCall
) parent_node
;
331 if (invoc
.call
== this
) {
334 if (invoc
.target_type
!= null) {
335 ret_type
= invoc
.target_type
.copy ();
336 ret_type
.value_owned
= true;
337 } else if (invoc
.parent_node is ExpressionStatement
) {
338 ret_type
= new
VoidType ();
340 // expect dynamic object of the same type
341 ret_type
= inner
.value_type
.copy ();
343 var m
= new
DynamicMethod (inner
.value_type
, member_name
, ret_type
, source_reference
);
344 m
.invocation
= invoc
;
345 var err
= new
ErrorType (null, null);
346 err
.dynamic_error
= true;
347 m
.add_error_type (err
);
348 m
.access
= SymbolAccessibility
.PUBLIC
;
349 m
.add_parameter (new FormalParameter
.with_ellipsis ());
350 dynamic_object_type
.type_symbol
.scope
.add (null, m
);
351 symbol_reference
= m
;
353 } else if (parent_node is Assignment
) {
354 var a
= (Assignment
) parent_node
;
356 && (a
.operator
== AssignmentOperator
.ADD
357 || a
.operator
== AssignmentOperator
.SUB
)) {
359 var s
= new
DynamicSignal (inner
.value_type
, member_name
, new
VoidType (), source_reference
);
361 s
.access
= SymbolAccessibility
.PUBLIC
;
362 dynamic_object_type
.type_symbol
.scope
.add (null, s
);
363 symbol_reference
= s
;
364 } else if (a
.left
== this
) {
365 // dynamic property assignment
366 var prop
= new
DynamicProperty (inner
.value_type
, member_name
, source_reference
);
367 prop
.access
= SymbolAccessibility
.PUBLIC
;
368 prop
.set_accessor
= new
PropertyAccessor (false, true, false, null, null, prop
.source_reference
);
369 prop
.set_accessor
.access
= SymbolAccessibility
.PUBLIC
;
370 prop
.owner
= inner
.value_type
.data_type
.scope
;
371 dynamic_object_type
.type_symbol
.scope
.add (null, prop
);
372 symbol_reference
= prop
;
375 if (symbol_reference
== null) {
376 // dynamic property read access
377 var prop
= new
DynamicProperty (inner
.value_type
, member_name
, source_reference
);
378 if (target_type
!= null) {
379 prop
.property_type
= target_type
;
381 // expect dynamic object of the same type
382 prop
.property_type
= inner
.value_type
.copy ();
384 prop
.access
= SymbolAccessibility
.PUBLIC
;
385 prop
.get_accessor
= new
PropertyAccessor (true, false, false, prop
.property_type
.copy (), null, prop
.source_reference
);
386 prop
.get_accessor
.access
= SymbolAccessibility
.PUBLIC
;
387 prop
.owner
= inner
.value_type
.data_type
.scope
;
388 dynamic_object_type
.type_symbol
.scope
.add (null, prop
);
389 symbol_reference
= prop
;
391 if (symbol_reference
!= null) {
392 may_access_instance_members
= true;
393 may_access_klass_members
= true;
398 if (symbol_reference
== null) {
401 string base_type_name
= "(null)";
402 if (inner
!= null && inner
.value_type
!= null) {
403 base_type_name
= inner
.value_type
.to_string ();
404 } else if (base_symbol
!= null) {
405 base_type_name
= base_symbol
.get_full_name ();
408 Report
.error (source_reference
, "The name `%s' does not exist in the context of `%s'".printf (member_name
, base_type_name
));
412 var member
= symbol_reference
;
413 var access
= SymbolAccessibility
.PUBLIC
;
414 bool instance
= false;
417 if (!member
.check (analyzer
)) {
421 if (member is LocalVariable
) {
422 var local
= (LocalVariable
) member
;
423 var block
= (Block
) local
.parent_symbol
;
424 if (analyzer
.find_parent_method (block
) != analyzer
.current_method
) {
425 // mark all methods between current method and the captured
426 // block as closures (to support nested closures)
427 Symbol sym
= analyzer
.current_method
;
428 while (sym
!= block
) {
429 var method
= sym as Method
;
430 if (method
!= null) {
431 method
.closure
= true;
432 // consider captured variables as used
433 // as we require captured variables to be initialized
434 method
.add_captured_variable (local
);
436 sym
= sym
.parent_symbol
;
439 local
.captured
= true;
440 block
.captured
= true;
442 } else if (member is FormalParameter
) {
443 var param
= (FormalParameter
) member
;
444 var m
= param
.parent_symbol as Method
;
445 if (m
!= null && m
!= analyzer
.current_method
&& param
!= m
.this_parameter
) {
446 // mark all methods between current method and the captured
447 // parameter as closures (to support nested closures)
448 Symbol sym
= analyzer
.current_method
;
450 var method
= sym as Method
;
451 if (method
!= null) {
452 method
.closure
= true;
454 sym
= sym
.parent_symbol
;
457 param
.captured
= true;
458 m
.body
.captured
= true;
460 if (param
.direction
!= ParameterDirection
.IN
) {
462 Report
.error (source_reference
, "Cannot capture reference or output parameter `%s'".printf (param
.get_full_name ()));
465 } else if (member is Field
) {
466 var f
= (Field
) member
;
468 instance
= (f
.binding
== MemberBinding
.INSTANCE
);
469 klass
= (f
.binding
== MemberBinding
.CLASS
);
470 } else if (member is Method
) {
471 var m
= (Method
) member
;
472 if (m
.is_async_callback
) {
473 // ensure to use right callback method for virtual/abstract async methods
474 m
= analyzer
.current_method
.get_callback_method ();
475 symbol_reference
= m
;
476 member
= symbol_reference
;
477 } else if (m
.base_method
!= null) {
478 // refer to base method to inherit default arguments
481 if (m
.signal_reference
!= null) {
482 // method is class/default handler for a signal
483 // let signal deal with member access
484 symbol_reference
= m
.signal_reference
;
486 symbol_reference
= m
;
489 member
= symbol_reference
;
490 } else if (m
.base_interface_method
!= null) {
491 // refer to base method to inherit default arguments
492 m
= m
.base_interface_method
;
493 symbol_reference
= m
;
494 member
= symbol_reference
;
497 if (!(m is CreationMethod
)) {
498 instance
= (m
.binding
== MemberBinding
.INSTANCE
);
500 klass
= (m
.binding
== MemberBinding
.CLASS
);
501 } else if (member is Property
) {
502 var prop
= (Property
) member
;
503 if (!prop
.check (analyzer
)) {
507 if (prop
.base_property
!= null) {
508 // refer to base property
509 prop
= prop
.base_property
;
510 symbol_reference
= prop
;
511 member
= symbol_reference
;
512 } else if (prop
.base_interface_property
!= null) {
513 // refer to base property
514 prop
= prop
.base_interface_property
;
515 symbol_reference
= prop
;
516 member
= symbol_reference
;
518 access
= prop
.access
;
520 if (prop
.set_accessor
== null) {
522 Report
.error (source_reference
, "Property `%s' is read-only".printf (prop
.get_full_name ()));
525 if (prop
.access
== SymbolAccessibility
.PUBLIC
) {
526 access
= prop
.set_accessor
.access
;
527 } else if (prop
.access
== SymbolAccessibility
.PROTECTED
528 && prop
.set_accessor
.access
!= SymbolAccessibility
.PUBLIC
) {
529 access
= prop
.set_accessor
.access
;
532 if (prop
.get_accessor
== null) {
534 Report
.error (source_reference
, "Property `%s' is write-only".printf (prop
.get_full_name ()));
537 if (prop
.access
== SymbolAccessibility
.PUBLIC
) {
538 access
= prop
.get_accessor
.access
;
539 } else if (prop
.access
== SymbolAccessibility
.PROTECTED
540 && prop
.get_accessor
.access
!= SymbolAccessibility
.PUBLIC
) {
541 access
= prop
.get_accessor
.access
;
544 instance
= (prop
.binding
== MemberBinding
.INSTANCE
);
545 } else if (member is Signal
) {
551 if (access
== SymbolAccessibility
.PRIVATE
) {
552 var target_type
= member
.parent_symbol
;
554 bool in_target_type
= false;
555 for (Symbol this_symbol
= analyzer
.current_symbol
; this_symbol
!= null; this_symbol
= this_symbol
.parent_symbol
) {
556 if (target_type
== this_symbol
) {
557 in_target_type
= true;
562 if (!in_target_type
) {
564 Report
.error (source_reference
, "Access to private member `%s' denied".printf (member
.get_full_name ()));
568 if ((instance
&& !may_access_instance_members
) ||
569 (klass
&& !may_access_klass_members
)) {
570 prototype_access
= true;
572 if (symbol_reference is Method
) {
573 // also set static type for prototype access
574 // required when using instance methods as delegates in constants
575 // TODO replace by MethodPrototype
576 value_type
= analyzer
.get_value_type_for_symbol (symbol_reference
, lvalue
);
577 } else if (symbol_reference is Field
) {
578 value_type
= new
FieldPrototype ((Field
) symbol_reference
);
580 value_type
= new
InvalidType ();
583 if (target_type
!= null) {
584 value_type
.value_owned
= target_type
.value_owned
;
587 // implicit this access
588 if (instance
&& inner
== null) {
589 inner
= new
MemberAccess (null, "this", source_reference
);
590 inner
.value_type
= this_parameter
.parameter_type
.copy ();
591 inner
.symbol_reference
= this_parameter
;
594 formal_value_type
= analyzer
.get_value_type_for_symbol (symbol_reference
, lvalue
);
595 if (inner
!= null && formal_value_type
!= null) {
596 value_type
= formal_value_type
.get_actual_type (inner
.value_type
, null, this
);
598 value_type
= formal_value_type
;
601 if (symbol_reference is Method
) {
602 var m
= (Method
) symbol_reference
;
604 if (target_type
!= null) {
605 value_type
.value_owned
= target_type
.value_owned
;
609 if (m
.base_method
!= null) {
610 base_method
= m
.base_method
;
611 } else if (m
.base_interface_method
!= null) {
612 base_method
= m
.base_interface_method
;
617 if (instance
&& base_method
.parent_symbol is TypeSymbol
) {
618 inner
.target_type
= analyzer
.get_data_type_for_symbol ((TypeSymbol
) base_method
.parent_symbol
);
620 } else if (symbol_reference is Property
) {
621 var prop
= (Property
) symbol_reference
;
623 Property base_property
;
624 if (prop
.base_property
!= null) {
625 base_property
= prop
.base_property
;
626 } else if (prop
.base_interface_property
!= null) {
627 base_property
= prop
.base_interface_property
;
629 base_property
= prop
;
632 if (instance
&& base_property
.parent_symbol
!= null) {
633 inner
.target_type
= analyzer
.get_data_type_for_symbol ((TypeSymbol
) base_property
.parent_symbol
);
635 } else if ((symbol_reference is Field
636 || symbol_reference is Signal
)
637 && instance
&& symbol_reference
.parent_symbol
!= null) {
638 inner
.target_type
= analyzer
.get_data_type_for_symbol ((TypeSymbol
) symbol_reference
.parent_symbol
);
645 public override void get_defined_variables (Collection
<LocalVariable
> collection
) {
647 inner
.get_defined_variables (collection
);
651 public override void get_used_variables (Collection
<LocalVariable
> collection
) {
653 inner
.get_used_variables (collection
);
655 var local
= symbol_reference as LocalVariable
;
657 collection
.add (local
);