Add support for async signal handlers
[vala-lang.git] / vala / valaproperty.vala
blob467cdf84efbb1476718fd076f934debbdebaad2e
1 /* valaproperty.vala
3 * Copyright (C) 2006-2010 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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
21 * Raffaele Sandrini <raffaele@sandrini.ch>
24 using GLib;
26 /**
27 * Represents a property declaration in the source code.
29 public class Vala.Property : Symbol, Lockable {
30 /**
31 * The property type.
33 public DataType? property_type {
34 get { return _data_type; }
35 set {
36 _data_type = value;
37 if (value != null) {
38 _data_type.parent_node = this;
43 /**
44 * The get accessor of this property if available.
46 public PropertyAccessor? get_accessor {
47 get { return _get_accessor; }
48 set {
49 _get_accessor = value;
50 if (value != null) {
51 value.owner = scope;
56 /**
57 * The set/construct accessor of this property if available.
59 public PropertyAccessor? set_accessor {
60 get { return _set_accessor; }
61 set {
62 _set_accessor = value;
63 if (value != null) {
64 value.owner = scope;
69 /**
70 * Represents the generated `this` parameter in this property.
72 public Parameter this_parameter { get; set; }
74 /**
75 * Specifies whether a `notify` signal should be emitted on property
76 * changes.
78 public bool notify { get; set; default = true; }
80 /**
81 * Specifies whether the implementation of this property does not
82 * provide getter/setter methods.
84 public bool no_accessor_method { get; set; }
86 /**
87 * Specifies whether automatic accessor code generation should be
88 * disabled.
90 public bool interface_only { get; set; }
92 /**
93 * Specifies whether this property is abstract. Abstract properties have
94 * no accessor bodies, may only be specified within abstract classes and
95 * interfaces, and must be overriden by derived non-abstract classes.
97 public bool is_abstract { get; set; }
99 /**
100 * Specifies whether this property is virtual. Virtual properties may be
101 * overridden by derived classes.
103 public bool is_virtual { get; set; }
106 * Specifies whether this property overrides a virtual or abstract
107 * property of a base type.
109 public bool overrides { get; set; }
112 * Reference the the Field that holds this property
114 public Field field { get; set; }
117 * Specifies whether this field may only be accessed with an instance of
118 * the contained type.
120 public MemberBinding binding { get; set; default = MemberBinding.INSTANCE; }
123 * Specifies the virtual or abstract property this property overrides.
124 * Reference must be weak as virtual properties set base_property to
125 * themselves.
127 public Property base_property {
128 get {
129 find_base_properties ();
130 return _base_property;
135 * Specifies the abstract interface property this property implements.
137 public Property base_interface_property {
138 get {
139 find_base_properties ();
140 return _base_interface_property;
145 * Specifies the default value of this property.
147 public Expression initializer { get; set; }
149 public bool no_array_length { get; set; }
151 public bool array_null_terminated { get; set; }
154 * Nickname of this property.
156 public string nick {
157 get {
158 if (_nick == null) {
159 _nick = get_canonical_name ();
161 return _nick;
163 set { _nick = value; }
167 * The long description of this property.
169 public string blurb {
170 get {
171 if (_blurb == null) {
172 _blurb = get_canonical_name ();
174 return _blurb;
176 set { _blurb = value; }
179 private bool lock_used = false;
181 private DataType _data_type;
183 private string? _nick;
184 private string? _blurb;
186 private weak Property _base_property;
187 private Property _base_interface_property;
188 private bool base_properties_valid;
189 PropertyAccessor? _get_accessor;
190 PropertyAccessor? _set_accessor;
193 * Creates a new property.
195 * @param name property name
196 * @param type property type
197 * @param get_accessor get accessor
198 * @param set_accessor set/construct accessor
199 * @param source reference to source code
200 * @return newly created property
202 public Property (string name, DataType? property_type, PropertyAccessor? get_accessor, PropertyAccessor? set_accessor, SourceReference? source_reference = null, Comment? comment = null) {
203 base (name, source_reference, comment);
204 this.property_type = property_type;
205 this.get_accessor = get_accessor;
206 this.set_accessor = set_accessor;
209 public override void accept (CodeVisitor visitor) {
210 visitor.visit_property (this);
213 public override void accept_children (CodeVisitor visitor) {
214 property_type.accept (visitor);
216 if (get_accessor != null) {
217 get_accessor.accept (visitor);
219 if (set_accessor != null) {
220 set_accessor.accept (visitor);
223 if (initializer != null) {
224 initializer.accept (visitor);
229 * Returns the C name of this property in upper case. Words are
230 * separated by underscores. The upper case C name of the class is
231 * prefix of the result.
233 * @return the upper case name to be used in C code
235 public string get_upper_case_cname () {
236 return "%s_%s".printf (parent_symbol.get_lower_case_cname (null), camel_case_to_lower_case (name)).up ();
240 * Returns the string literal of this property to be used in C code.
242 * @return string literal to be used in C code
244 public CCodeConstant get_canonical_cconstant () {
245 return new CCodeConstant ("\"%s\"".printf (get_canonical_name ()));
248 public string get_canonical_name () {
249 var str = new StringBuilder ();
251 string i = name;
253 while (i.length > 0) {
254 unichar c = i.get_char ();
255 if (c == '_') {
256 str.append_c ('-');
257 } else {
258 str.append_unichar (c);
261 i = i.next_char ();
264 return str.str;
267 void process_ccode_attribute (Attribute a) {
268 if (a.has_argument ("notify")) {
269 notify = a.get_bool ("notify");
271 if (a.has_argument ("array_length")) {
272 no_array_length = !a.get_bool ("array_length");
274 if (a.has_argument ("array_null_terminated")) {
275 array_null_terminated = a.get_bool ("array_null_terminated");
280 * Process all associated attributes.
282 public void process_attributes () {
283 foreach (Attribute a in attributes) {
284 if (a.name == "CCode") {
285 process_ccode_attribute (a);
286 } else if (a.name == "NoAccessorMethod") {
287 no_accessor_method = true;
288 } else if (a.name == "Description") {
289 if (a.has_argument ("nick")) {
290 nick = a.get_string ("nick");
292 if (a.has_argument ("blurb")) {
293 blurb = a.get_string ("blurb");
295 } else if (a.name == "Deprecated") {
296 process_deprecated_attribute (a);
301 public bool get_lock_used () {
302 return lock_used;
305 public void set_lock_used (bool used) {
306 lock_used = used;
310 * Checks whether the accessors and type of the specified property
311 * matches this property.
313 * @param prop a property
314 * @return true if the specified property is compatible to this
315 * property
317 public bool equals (Property prop2) {
318 if ((get_accessor == null && prop2.get_accessor != null) ||
319 (get_accessor != null && prop2.get_accessor == null)) {
320 return false;
323 if ((set_accessor == null && prop2.set_accessor != null) ||
324 (set_accessor != null && prop2.set_accessor == null)) {
325 return false;
328 if (get_accessor != null) {
329 // check accessor value_type instead of property_type
330 // due to possible ownership differences
331 if (!prop2.get_accessor.value_type.equals (get_accessor.value_type)) {
332 return false;
336 if (set_accessor != null) {
337 // check accessor value_type instead of property_type
338 // due to possible ownership differences
339 if (!prop2.set_accessor.value_type.equals (set_accessor.value_type)) {
340 return false;
343 if (set_accessor.writable != prop2.set_accessor.writable) {
344 return false;
346 if (set_accessor.construction != prop2.set_accessor.construction) {
347 return false;
351 return true;
354 public override void replace_type (DataType old_type, DataType new_type) {
355 if (property_type == old_type) {
356 property_type = new_type;
360 private void find_base_properties () {
361 if (base_properties_valid) {
362 return;
365 if (parent_symbol is Class) {
366 find_base_interface_property ((Class) parent_symbol);
367 if (is_virtual || overrides) {
368 find_base_class_property ((Class) parent_symbol);
370 } else if (parent_symbol is Interface) {
371 if (is_virtual || is_abstract) {
372 _base_interface_property = this;
376 base_properties_valid = true;
379 private void find_base_class_property (Class cl) {
380 var sym = cl.scope.lookup (name);
381 if (sym is Property) {
382 var base_property = (Property) sym;
383 if (base_property.is_abstract || base_property.is_virtual) {
384 if (!equals (base_property)) {
385 error = true;
386 Report.error (source_reference, "Type and/or accessors of overriding property `%s' do not match overridden property `%s'.".printf (get_full_name (), base_property.get_full_name ()));
387 return;
390 _base_property = base_property;
391 return;
395 if (cl.base_class != null) {
396 find_base_class_property (cl.base_class);
400 private void find_base_interface_property (Class cl) {
401 // FIXME report error if multiple possible base properties are found
402 foreach (DataType type in cl.get_base_types ()) {
403 if (type.data_type is Interface) {
404 var sym = type.data_type.scope.lookup (name);
405 if (sym is Property) {
406 var base_property = (Property) sym;
407 if (base_property.is_abstract) {
408 if (!equals (base_property)) {
409 error = true;
410 Report.error (source_reference, "Type and/or accessors of overriding property `%s' do not match overridden property `%s'.".printf (get_full_name (), base_property.get_full_name ()));
411 return;
414 _base_interface_property = base_property;
415 return;
422 public override bool check (CodeContext context) {
423 if (checked) {
424 return !error;
427 checked = true;
429 process_attributes ();
431 if (is_abstract) {
432 if (parent_symbol is Class) {
433 var cl = (Class) parent_symbol;
434 if (!cl.is_abstract) {
435 error = true;
436 Report.error (source_reference, "Abstract properties may not be declared in non-abstract classes");
437 return false;
439 } else if (!(parent_symbol is Interface)) {
440 error = true;
441 Report.error (source_reference, "Abstract properties may not be declared outside of classes and interfaces");
442 return false;
444 } else if (is_virtual) {
445 if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
446 error = true;
447 Report.error (source_reference, "Virtual properties may not be declared outside of classes and interfaces");
448 return false;
451 if (parent_symbol is Class) {
452 var cl = (Class) parent_symbol;
453 if (cl.is_compact) {
454 error = true;
455 Report.error (source_reference, "Virtual properties may not be declared in compact classes");
456 return false;
459 } else if (overrides) {
460 if (!(parent_symbol is Class)) {
461 error = true;
462 Report.error (source_reference, "Properties may not be overridden outside of classes");
463 return false;
465 } else if (access == SymbolAccessibility.PROTECTED) {
466 if (!(parent_symbol is Class) && !(parent_symbol is Interface)) {
467 error = true;
468 Report.error (source_reference, "Protected properties may not be declared outside of classes and interfaces");
469 return false;
473 var old_source_file = context.analyzer.current_source_file;
474 var old_symbol = context.analyzer.current_symbol;
476 if (source_reference != null) {
477 context.analyzer.current_source_file = source_reference.file;
479 context.analyzer.current_symbol = this;
481 if (property_type is VoidType) {
482 error = true;
483 Report.error (source_reference, "'void' not supported as property type");
484 return false;
487 property_type.check (context);
489 if (get_accessor != null) {
490 get_accessor.check (context);
492 if (set_accessor != null) {
493 set_accessor.check (context);
496 if (initializer != null) {
497 initializer.check (context);
500 // check whether property type is at least as accessible as the property
501 if (!context.analyzer.is_type_accessible (this, property_type)) {
502 error = true;
503 Report.error (source_reference, "property type `%s` is less accessible than property `%s`".printf (property_type.to_string (), get_full_name ()));
506 if (overrides && base_property == null) {
507 Report.error (source_reference, "%s: no suitable property found to override".printf (get_full_name ()));
510 if (!external_package && !overrides && !hides && get_hidden_member () != null) {
511 Report.warning (source_reference, "%s hides inherited property `%s'. Use the `new' keyword if hiding was intentional".printf (get_full_name (), get_hidden_member ().get_full_name ()));
514 /* construct properties must be public */
515 if (set_accessor != null && set_accessor.construction) {
516 if (access != SymbolAccessibility.PUBLIC) {
517 error = true;
518 Report.error (source_reference, "%s: construct properties must be public".printf (get_full_name ()));
522 if (initializer != null && !initializer.error && initializer.value_type != null && !(initializer.value_type.compatible (property_type))) {
523 error = true;
524 Report.error (initializer.source_reference, "Expected initializer of type `%s' but got `%s'".printf (property_type.to_string (), initializer.value_type.to_string ()));
527 context.analyzer.current_source_file = old_source_file;
528 context.analyzer.current_symbol = old_symbol;
530 return !error;