3 * Copyright (C) 2006-2012 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 a class declaration in the source code.
28 public class Vala
.Class
: ObjectTypeSymbol
{
30 * Specifies the base class.
32 public Class base_class
{ get; set; }
35 * Specifies whether this class is abstract. Abstract classes may not be
38 public bool is_abstract
{ get; set; }
41 * Instances of compact classes are fast to create and have a
42 * compact memory layout. Compact classes don't support runtime
43 * type information or virtual methods.
45 public bool is_compact
{
47 if (_is_compact
== null) {
48 if (base_class
!= null) {
49 _is_compact
= base_class
.is_compact
;
51 _is_compact
= get_attribute ("Compact") != null;
54 if (_is_compact
== null) {
55 _is_compact
= get_attribute ("Compact") != null;
61 set_attribute ("Compact", value
);
66 * Instances of immutable classes are immutable after construction.
68 public bool is_immutable
{
70 if (_is_immutable
== null) {
71 if (base_class
!= null) {
72 _is_immutable
= base_class
.is_immutable
;
74 _is_immutable
= get_attribute ("Immutable") != null;
77 if (_is_immutable
== null) {
78 _is_immutable
= get_attribute ("Immutable") != null;
83 _is_immutable
= value
;
84 set_attribute ("Immutable", value
);
89 * Specifies whether this class has private fields.
91 public bool has_private_fields
{ get; set; }
94 * Specifies whether this class has class fields.
96 public bool has_class_private_fields
{ get; private set; }
98 private bool? _is_compact
;
99 private bool? _is_immutable
;
101 private List
<DataType
> base_types
= new ArrayList
<DataType
> ();
103 private List
<Constant
> constants
= new ArrayList
<Constant
> ();
104 private List
<Field
> fields
= new ArrayList
<Field
> ();
105 private List
<Method
> methods
= new ArrayList
<Method
> ();
106 private List
<Property
> properties
= new ArrayList
<Property
> ();
107 private List
<Signal
> signals
= new ArrayList
<Signal
> ();
110 private List
<Class
> classes
= new ArrayList
<Class
> ();
111 private List
<Struct
> structs
= new ArrayList
<Struct
> ();
112 private List
<Enum
> enums
= new ArrayList
<Enum
> ();
113 private List
<Delegate
> delegates
= new ArrayList
<Delegate
> ();
116 * Returns a copy of the list of classes.
118 * @return list of classes
120 public List
<Class
> get_classes () {
125 * Returns a copy of the list of structs.
127 * @return list of structs
129 public List
<Struct
> get_structs () {
134 * Returns a copy of the list of enums.
136 * @return list of enums
138 public List
<Enum
> get_enums () {
143 * Returns a copy of the list of delegates.
145 * @return list of delegates
147 public List
<Delegate
> get_delegates () {
152 * Specifies the default construction method.
154 public CreationMethod default_construction_method
{ get; set; }
157 * Specifies the instance constructor.
159 public Constructor constructor
{ get; set; }
162 * Specifies the class constructor.
164 public Constructor class_constructor
{ get; set; }
167 * Specifies the static class constructor.
169 public Constructor static_constructor
{ get; set; }
172 * Specifies the instance destructor.
174 public Destructor? destructor
{
175 get { return _destructor
; }
178 if (_destructor
!= null) {
179 if (_destructor
.this_parameter
!= null) {
180 _destructor
.scope
.remove (_destructor
.this_parameter
.name
);
182 _destructor
.this_parameter
= new
Parameter ("this", get_this_type ());
183 _destructor
.scope
.add (_destructor
.this_parameter
.name
, _destructor
.this_parameter
);
189 * Specifies the class destructor.
191 public Destructor? static_destructor
{ get; set; }
194 * Specifies the class destructor.
196 public Destructor? class_destructor
{ get; set; }
199 * Specifies whether this class denotes an error base.
201 public bool is_error_base
{
203 return get_attribute ("ErrorBase") != null;
207 Destructor? _destructor
;
210 * Creates a new class.
212 * @param name type name
213 * @param source_reference reference to source code
214 * @param comment class documentation
215 * @return newly created class
217 public Class (string name
, SourceReference? source_reference
= null, Comment? comment
= null) {
218 base (name
, source_reference
, comment
);
222 * Adds the specified class or interface to the list of base types of
225 * @param type a class or interface reference
227 public void add_base_type (DataType type
) {
228 base_types
.add (type
);
229 type
.parent_node
= this
;
233 * Returns a copy of the base type list.
235 * @return list of base types
237 public List
<DataType
> get_base_types () {
242 * Adds the specified constant as a member to this class.
244 * @param c a constant
246 public override void add_constant (Constant c
) {
248 scope
.add (c
.name
, c
);
252 * Adds the specified field as a member to this class.
256 public override void add_field (Field f
) {
258 if (f
.access
== SymbolAccessibility
.PRIVATE
&& f
.binding
== MemberBinding
.INSTANCE
) {
259 has_private_fields
= true;
260 } else if (f
.access
== SymbolAccessibility
.PRIVATE
&& f
.binding
== MemberBinding
.CLASS
) {
261 has_class_private_fields
= true;
263 scope
.add (f
.name
, f
);
267 * Returns a copy of the list of fields.
269 * @return list of fields
271 public List
<Field
> get_fields () {
276 * Returns a copy of the list of constants.
278 * @return list of constants
280 public List
<Constant
> get_constants () {
285 * Adds the specified method as a member to this class.
289 public override void add_method (Method m
) {
290 if (m
.binding
== MemberBinding
.INSTANCE
|| m is CreationMethod
) {
291 if (m
.this_parameter
!= null) {
292 m
.scope
.remove (m
.this_parameter
.name
);
294 m
.this_parameter
= new
Parameter ("this", get_this_type ());
295 m
.scope
.add (m
.this_parameter
.name
, m
.this_parameter
);
297 if (!(m
.return_type is VoidType
) && m
.get_postconditions ().size
> 0) {
298 if (m
.result_var
!= null) {
299 m
.scope
.remove (m
.result_var
.name
);
301 m
.result_var
= new
LocalVariable (m
.return_type
.copy (), "result", null, source_reference
);
302 m
.result_var
.is_result
= true;
304 if (m is CreationMethod
) {
305 if (m
.name
== null) {
306 default_construction_method
= (CreationMethod
) m
;
310 var cm
= (CreationMethod
) m
;
311 if (cm
.class_name
!= null && cm
.class_name
!= name
) {
312 // class_name is null for constructors generated by GIdlParser
313 Report
.error (m
.source_reference
, "missing return type in method `%s.%s´".printf (get_full_name (), cm
.class_name
));
320 if (m
.base_interface_type
== null) {
321 scope
.add (m
.name
, m
);
323 // explicit interface method implementation
329 * Returns a copy of the list of methods.
331 * @return list of methods
333 public override List
<Method
> get_methods () {
338 * Adds the specified property as a member to this class.
340 * @param prop a property
342 public override void add_property (Property prop
) {
343 properties
.add (prop
);
344 scope
.add (prop
.name
, prop
);
346 prop
.this_parameter
= new
Parameter ("this", get_this_type ());
347 prop
.scope
.add (prop
.this_parameter
.name
, prop
.this_parameter
);
349 if (prop
.field
!= null) {
350 add_field (prop
.field
);
355 * Returns a copy of the list of properties.
357 * @return list of properties
359 public override List
<Property
> get_properties () {
364 * Adds the specified signal as a member to this class.
366 * @param sig a signal
368 public override void add_signal (Signal sig
) {
370 scope
.add (sig
.name
, sig
);
374 * Returns a copy of the list of signals.
376 * @return list of signals
378 public override List
<Signal
> get_signals () {
383 * Adds the specified class as an inner class.
387 public override void add_class (Class cl
) {
389 scope
.add (cl
.name
, cl
);
393 * Adds the specified struct as an inner struct.
397 public override void add_struct (Struct st
) {
399 scope
.add (st
.name
, st
);
403 * Adds the specified enum as an inner enum.
407 public override void add_enum (Enum en
) {
409 scope
.add (en
.name
, en
);
413 * Adds the specified delegate as an inner delegate.
415 * @param d a delegate
417 public override void add_delegate (Delegate d
) {
419 scope
.add (d
.name
, d
);
422 public override void add_constructor (Constructor c
) {
423 if (c
.binding
== MemberBinding
.INSTANCE
) {
424 if (constructor
!= null) {
425 Report
.error (c
.source_reference
, "class already contains a constructor");
428 } else if (c
.binding
== MemberBinding
.CLASS
) {
429 if (class_constructor
!= null) {
430 Report
.error (c
.source_reference
, "class already contains a class constructor");
432 class_constructor
= c
;
434 if (static_constructor
!= null) {
435 Report
.error (c
.source_reference
, "class already contains a static constructor");
437 static_constructor
= c
;
441 public override void add_destructor (Destructor d
) {
442 if (d
.binding
== MemberBinding
.INSTANCE
) {
443 if (destructor
!= null) {
444 Report
.error (d
.source_reference
, "class already contains a destructor");
447 } else if (d
.binding
== MemberBinding
.CLASS
) {
448 if (class_destructor
!= null) {
449 Report
.error (d
.source_reference
, "class already contains a class destructor");
451 class_destructor
= d
;
453 if (static_destructor
!= null) {
454 Report
.error (d
.source_reference
, "class already contains a static destructor");
456 static_destructor
= d
;
460 public override void accept (CodeVisitor visitor
) {
461 visitor
.visit_class (this
);
464 public override void accept_children (CodeVisitor visitor
) {
465 foreach (DataType type
in base_types
) {
466 type
.accept (visitor
);
469 foreach (TypeParameter p
in get_type_parameters ()) {
473 /* process enums first to avoid order problems in C code */
474 foreach (Enum en
in enums
) {
478 foreach (Field f
in fields
) {
482 foreach (Constant c
in constants
) {
486 foreach (Method m
in methods
) {
490 foreach (Property prop
in properties
) {
491 prop
.accept (visitor
);
494 foreach (Signal sig
in signals
) {
495 sig
.accept (visitor
);
498 if (constructor
!= null) {
499 constructor
.accept (visitor
);
502 if (class_constructor
!= null) {
503 class_constructor
.accept (visitor
);
506 if (static_constructor
!= null) {
507 static_constructor
.accept (visitor
);
510 if (destructor
!= null) {
511 destructor
.accept (visitor
);
514 if (static_destructor
!= null) {
515 static_destructor
.accept (visitor
);
518 if (class_destructor
!= null) {
519 class_destructor
.accept (visitor
);
522 foreach (Class cl
in classes
) {
526 foreach (Struct st
in structs
) {
530 foreach (Delegate d
in delegates
) {
535 public override bool is_reference_type () {
539 public bool is_fundamental () {
540 if (!is_compact
&& base_class
== null) {
546 public override bool is_subtype_of (TypeSymbol t
) {
551 foreach (DataType base_type
in base_types
) {
552 if (base_type
.data_type
!= null && base_type
.data_type
.is_subtype_of (t
)) {
560 public override void replace_type (DataType old_type
, DataType new_type
) {
561 for (int i
= 0; i
< base_types
.size
; i
++) {
562 if (base_types
[i
] == old_type
) {
563 base_types
[i
] = new_type
;
564 new_type
.parent_node
= this
;
570 private void get_all_prerequisites (Interface iface
, List
<TypeSymbol
> list
) {
571 foreach (DataType prereq
in iface
.get_prerequisites ()) {
572 TypeSymbol type
= prereq
.data_type
;
573 /* skip on previous errors */
579 if (type is Interface
) {
580 get_all_prerequisites ((Interface
) type
, list
);
586 private bool class_is_a (Class cl
, TypeSymbol t
) {
591 foreach (DataType base_type
in cl
.get_base_types ()) {
592 if (base_type
.data_type is Class
) {
593 if (class_is_a ((Class
) base_type
.data_type
, t
)) {
596 } else if (base_type
.data_type
== t
) {
604 public override bool check (CodeContext context
) {
611 var old_source_file
= context
.analyzer
.current_source_file
;
612 var old_symbol
= context
.analyzer
.current_symbol
;
614 if (source_reference
!= null) {
615 context
.analyzer
.current_source_file
= source_reference
.file
;
617 context
.analyzer
.current_symbol
= this
;
619 foreach (DataType base_type_reference
in get_base_types ()) {
620 if (!base_type_reference
.check (context
)) {
625 if (!(base_type_reference is ObjectType
)) {
627 Report
.error (source_reference
, "base type `%s` of class `%s` is not an object type".printf (base_type_reference
.to_string (), get_full_name ()));
631 // check whether base type is at least as accessible as the class
632 if (!context
.analyzer
.is_type_accessible (this
, base_type_reference
)) {
634 Report
.error (source_reference
, "base type `%s` is less accessible than class `%s`".printf (base_type_reference
.to_string (), get_full_name ()));
638 int n_type_args
= base_type_reference
.get_type_arguments ().size
;
639 int n_type_params
= ((ObjectTypeSymbol
) base_type_reference
.data_type
).get_type_parameters ().size
;
640 if (n_type_args
< n_type_params
) {
642 Report
.error (base_type_reference
.source_reference
, "too few type arguments");
644 } else if (n_type_args
> n_type_params
) {
646 Report
.error (base_type_reference
.source_reference
, "too many type arguments");
651 foreach (DataType type
in base_types
) {
652 type
.check (context
);
655 foreach (TypeParameter p
in get_type_parameters ()) {
659 /* process enums first to avoid order problems in C code */
660 foreach (Enum en
in enums
) {
664 foreach (Field f
in fields
) {
668 foreach (Constant c
in constants
) {
672 foreach (Method m
in methods
) {
676 foreach (Property prop
in properties
) {
677 if (prop
.get_attribute ("NoAccessorMethod") != null && !is_subtype_of (context
.analyzer
.object_type
)) {
679 Report
.error (prop
.source_reference
, "NoAccessorMethod is only allowed for properties in classes derived from GLib.Object");
682 prop
.check (context
);
685 foreach (Signal sig
in signals
) {
689 if (constructor
!= null) {
690 constructor
.check (context
);
693 if (class_constructor
!= null) {
694 class_constructor
.check (context
);
697 if (static_constructor
!= null) {
698 static_constructor
.check (context
);
701 if (destructor
!= null) {
702 destructor
.check (context
);
705 if (static_destructor
!= null) {
706 static_destructor
.check (context
);
709 if (class_destructor
!= null) {
710 class_destructor
.check (context
);
713 foreach (Class cl
in classes
) {
717 foreach (Struct st
in structs
) {
721 foreach (Delegate d
in delegates
) {
725 /* compact classes cannot implement interfaces */
727 foreach (DataType base_type
in get_base_types ()) {
728 if (base_type
.data_type is Interface
) {
730 Report
.error (source_reference
, "compact classes `%s` may not implement interfaces".printf (get_full_name ()));
734 if (!external
&& !external_package
&& base_class
!= null && base_class
!= context
.analyzer
.gsource_type
) {
735 foreach (Field f
in fields
) {
736 if (f
.binding
== MemberBinding
.INSTANCE
) {
738 Report
.error (source_reference
, "derived compact classes may not have instance fields");
745 /* gather all prerequisites */
746 List
<TypeSymbol
> prerequisites
= new ArrayList
<TypeSymbol
> ();
747 foreach (DataType base_type
in get_base_types ()) {
748 if (base_type
.data_type is Interface
) {
749 get_all_prerequisites ((Interface
) base_type
.data_type
, prerequisites
);
752 /* check whether all prerequisites are met */
753 List
<string> missing_prereqs
= new ArrayList
<string> ();
754 foreach (TypeSymbol prereq
in prerequisites
) {
755 if (!class_is_a (this
, prereq
)) {
756 missing_prereqs
.insert (0, prereq
.get_full_name ());
759 /* report any missing prerequisites */
760 if (missing_prereqs
.size
> 0) {
763 string error_string
= "%s: some prerequisites (".printf (get_full_name ());
765 foreach (string s
in missing_prereqs
) {
767 error_string
= "%s`%s'".printf (error_string
, s
);
770 error_string
= "%s, `%s'".printf (error_string
, s
);
773 error_string
+= ") are not met";
774 Report
.error (source_reference
, error_string
);
777 /* VAPI classes don't have to specify overridden methods */
778 if (source_type
== SourceFileType
.SOURCE
) {
779 /* all abstract symbols defined in base types have to be at least defined (or implemented) also in this type */
780 foreach (DataType base_type
in get_base_types ()) {
781 if (base_type
.data_type is Interface
) {
782 Interface iface
= (Interface
) base_type
.data_type
;
784 if (base_class
!= null && base_class
.is_subtype_of (iface
)) {
785 // reimplementation of interface, class is not required to reimplement all methods
789 /* We do not need to do expensive equality checking here since this is done
790 * already. We only need to guarantee the symbols are present.
794 foreach (Method m
in iface
.get_methods ()) {
796 var implemented
= false;
797 var base_class
= this
;
798 while (base_class
!= null) {
799 foreach (var impl
in base_class
.get_methods ()) {
800 if (impl
.name
== m
.name
&& (impl
.base_interface_type
== null || impl
.base_interface_type
.data_type
== iface
)) {
801 // method is used as interface implementation, so it is not unused
802 impl
.version
.check (source_reference
);
808 base_class
= base_class
.base_class
;
812 Report
.error (source_reference
, "`%s' does not implement interface method `%s'".printf (get_full_name (), m
.get_full_name ()));
817 /* check properties */
818 foreach (Property prop
in iface
.get_properties ()) {
819 if (prop
.is_abstract
) {
821 var base_class
= this
;
822 while (base_class
!= null && !(sym is Property
)) {
823 sym
= base_class
.scope
.lookup (prop
.name
);
824 base_class
= base_class
.base_class
;
826 if (sym is Property
) {
827 var base_prop
= (Property
) sym
;
828 string? invalid_match
= null;
829 // No check at all for "new" classified properties, really?
830 if (!base_prop
.hides
&& !base_prop
.compatible (prop
, out invalid_match
)) {
832 Report
.error (source_reference
, "Type and/or accessors of inherited properties `%s' and `%s' do not match: %s.".printf (prop
.get_full_name (), base_prop
.get_full_name (), invalid_match
));
834 // property is used as interface implementation, so it is not unused
835 sym
.version
.check (source_reference
);
839 Report
.error (source_reference
, "`%s' does not implement interface property `%s'".printf (get_full_name (), prop
.get_full_name ()));
846 /* all abstract symbols defined in base classes have to be implemented in non-abstract classes */
848 var base_class
= base_class
;
849 while (base_class
!= null && base_class
.is_abstract
) {
850 foreach (Method base_method
in base_class
.get_methods ()) {
851 if (base_method
.is_abstract
) {
852 var override_method
= SemanticAnalyzer
.symbol_lookup_inherited (this
, base_method
.name
) as Method
;
853 if (override_method
== null || !override_method
.overrides
) {
855 Report
.error (source_reference
, "`%s' does not implement abstract method `%s'".printf (get_full_name (), base_method
.get_full_name ()));
859 foreach (Property base_property
in base_class
.get_properties ()) {
860 if (base_property
.is_abstract
) {
861 var override_property
= SemanticAnalyzer
.symbol_lookup_inherited (this
, base_property
.name
) as Property
;
862 if (override_property
== null || !override_property
.overrides
) {
864 Report
.error (source_reference
, "`%s' does not implement abstract property `%s'".printf (get_full_name (), base_property
.get_full_name ()));
868 base_class
= base_class
.base_class
;
873 context
.analyzer
.current_source_file
= old_source_file
;
874 context
.analyzer
.current_symbol
= old_symbol
;