valaclass.vala 29.7 KB
Newer Older
1 2
/* valaclass.vala
 *
3
 * Copyright (C) 2006-2009  Jürg Billeter
4 5 6 7
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
 */

using GLib;
24
using Gee;
25

26 27 28
/**
 * Represents a class declaration in the source code.
 */
29
public class Vala.Class : ObjectTypeSymbol {
30 31 32 33 34 35 36 37 38 39
	/**
	 * Specifies the base class.
	 */
	public Class base_class { get; set; }
	
	/**
	 * Specifies whether this class is abstract. Abstract classes may not be
	 * instantiated.
	 */
	public bool is_abstract { get; set; }
40

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
	/**
	 * Instances of compact classes are fast to create and have a
	 * compact memory layout. Compact classes don't support runtime
	 * type information or virtual methods.
	 */
	public bool is_compact {
		get {
			if (base_class != null) {
				return base_class.is_compact;
			}
			return _is_compact;
		}
		set {
			_is_compact = value;
		}
	}

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	/**
	 * Instances of immutable classes are immutable after construction.
	 */
	public bool is_immutable {
		get {
			if (base_class != null) {
				return base_class.is_immutable;
			}
			return _is_immutable;
		}
		set {
			_is_immutable = value;
		}
	}

73 74 75 76
	/**
	 * Specifies wheather the ref function returns void instead of the
	 * object.
	 */
77 78 79 80 81 82 83 84 85 86 87
	public bool ref_function_void {
		get {
			if (base_class != null) {
				return base_class.ref_function_void;
			}
			return _ref_function_void;
		}
		set {
			_ref_function_void = value;
		}
	}
88

89 90 91 92 93 94 95
	/**
	 * The name of the function to use to check whether a value is an instance of
	 * this class. If this is null then the default type check function should be 
	 * used instead.
	 */
	public string? type_check_function { get; set; }

96 97 98
	/**
	 * Specifies whether this class has private fields.
	 */
99
	public bool has_private_fields { get; private set; }
100 101 102 103 104
	
	/**
	 * Specifies whether this class has class fields.
	 */
	public bool has_class_private_fields { get; private set; }
105

106 107 108 109 110 111
	/**
	 * Specifies whether the free function requires the address of a
	 * pointer instead of just the pointer.
	 */
	public bool free_function_address_of { get; private set; }

112
	private string cname;
113
	public string const_cname { get; set; }
114
	private string lower_case_cprefix;
115
	private string lower_case_csuffix;
116
	private string type_id;
117 118
	private string ref_function;
	private string unref_function;
119
	private bool _ref_function_void;
120
	private string ref_sink_function;
121
	private string param_spec_function;
122 123 124 125 126
	private string copy_function;
	private string free_function;
	private string marshaller_type_name;
	private string get_value_function;
	private string set_value_function;
127
	private string? type_signature;
128
	private bool _is_compact;
129
	private bool _is_immutable;
130

131
	private Gee.List<DataType> base_types = new ArrayList<DataType> ();
132

133 134 135 136 137
	private Gee.List<Constant> constants = new ArrayList<Constant> ();
	private Gee.List<Field> fields = new ArrayList<Field> ();
	private Gee.List<Method> methods = new ArrayList<Method> ();
	private Gee.List<Property> properties = new ArrayList<Property> ();
	private Gee.List<Signal> signals = new ArrayList<Signal> ();
138 139

	// inner types
140 141
	private Gee.List<Class> classes = new ArrayList<Class> ();
	private Gee.List<Struct> structs = new ArrayList<Struct> ();
142
	private Gee.List<Enum> enums = new ArrayList<Enum> ();
Jürg Billeter's avatar
Jürg Billeter committed
143 144
	private Gee.List<Delegate> delegates = new ArrayList<Delegate> ();

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
	/**
	 * Returns a copy of the list of classes.
	 *
	 * @return list of classes
	 */
	public Gee.List<Class> get_classes () {
		return new ReadOnlyList<Class> (classes);
	}

	/**
	 * Returns a copy of the list of structs.
	 *
	 * @return list of structs
	 */
	public Gee.List<Struct> get_structs () {
		return new ReadOnlyList<Struct> (structs);
	}

	/**
	 * Returns a copy of the list of enums.
	 *
	 * @return list of enums
	 */
	public Gee.List<Enum> get_enums () {
		return new ReadOnlyList<Enum> (enums);
	}

	/**
	 * Returns a copy of the list of delegates.
	 *
	 * @return list of delegates
	 */
	public Gee.List<Delegate> get_delegates () {
		return new ReadOnlyList<Delegate> (delegates);
	}

181 182 183 184 185
	/**
	 * Specifies the default construction method.
	 */
	public Method default_construction_method { get; set; }
	
186 187 188 189
	/**
	 * Specifies the instance constructor.
	 */
	public Constructor constructor { get; set; }
190

191 192 193 194 195
	/**
	 * Specifies the class constructor.
	 */
	public Constructor class_constructor { get; set; }

196 197 198 199 200
	/**
	 * Specifies the static class constructor.
	 */
	public Constructor static_constructor { get; set; }

201 202 203
	/**
	 * Specifies the instance destructor.
	 */
204 205 206 207 208 209 210 211
	public Destructor? destructor {
		get { return _destructor; }
		set {
			_destructor = value;
			if (_destructor != null) {
				if (_destructor.this_parameter != null) {
					_destructor.scope.remove (_destructor.this_parameter.name);
				}
212
				_destructor.this_parameter = new FormalParameter ("this", get_this_type ());
213 214 215 216
				_destructor.scope.add (_destructor.this_parameter.name, _destructor.this_parameter);
			}
		}
	}
217 218 219 220 221

	/**
	 * Specifies the class destructor.
	 */
	public Destructor? static_destructor { get; set; }
222 223 224 225
	
	/**
	 * Specifies the class destructor.
	 */
226
	public Destructor? class_destructor { get; set; }
227 228 229 230

	/**
	 * Specifies whether this class denotes an error base.
	 */
231 232 233 234 235
	public bool is_error_base {
		get {
			return get_attribute ("ErrorBase") != null;
		}
	}
236

237 238
	Destructor? _destructor;

239 240 241 242 243
	/**
	 * Creates a new class.
	 *
	 * @param name   type name
	 * @param source reference to source code
244
	 * @param comment class documentation
245 246
	 * @return       newly created class
	 */
247 248
	public Class (string name, SourceReference? source_reference = null, Comment? comment = null) {
		base (name, source_reference, comment);
249
	}
250

251 252 253 254 255 256
	/**
	 * Adds the specified class or interface to the list of base types of
	 * this class.
	 *
	 * @param type a class or interface reference
	 */
257
	public void add_base_type (DataType type) {
258
		base_types.add (type);
259
		type.parent_node = this;
260 261 262 263 264 265 266
	}

	/**
	 * Returns a copy of the base type list.
	 *
	 * @return list of base types
	 */
267 268
	public Gee.List<DataType> get_base_types () {
		return new ReadOnlyList<DataType> (base_types);
269 270 271 272 273 274 275
	}

	/**
	 * Adds the specified constant as a member to this class.
	 *
	 * @param c a constant
	 */
276
	public void add_constant (Constant c) {
277
		constants.add (c);
278
		scope.add (c.name, c);
279 280 281 282 283 284 285
	}
	
	/**
	 * Adds the specified field as a member to this class.
	 *
	 * @param f a field
	 */
286
	public void add_field (Field f) {
287
		fields.add (f);
288
		if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.INSTANCE) {
289
			has_private_fields = true;
290 291
		} else if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.CLASS) {
			has_class_private_fields = true;
292
		}
293
		scope.add (f.name, f);
294 295 296 297 298 299 300
	}
	
	/**
	 * Returns a copy of the list of fields.
	 *
	 * @return list of fields
	 */
301 302
	public Gee.List<Field> get_fields () {
		return new ReadOnlyList<Field> (fields);
303
	}
304 305 306 307 308 309 310 311 312 313

	/**
	 * Returns a copy of the list of constants.
	 *
	 * @return list of constants
	 */
	public Gee.List<Constant> get_constants () {
		return new ReadOnlyList<Constant> (constants);
	}

314 315 316 317 318 319 320 321 322 323
	ObjectType get_this_type () {
		var result = new ObjectType (this);
		foreach (var type_parameter in get_type_parameters ()) {
			var type_arg = new GenericType (type_parameter);
			type_arg.value_owned = true;
			result.add_type_argument (type_arg);
		}
		return result;
	}

324 325 326 327 328
	/**
	 * Adds the specified method as a member to this class.
	 *
	 * @param m a method
	 */
329
	public void add_method (Method m) {
330
		if (m.binding == MemberBinding.INSTANCE || m is CreationMethod) {
331 332 333
			if (m.this_parameter != null) {
				m.scope.remove (m.this_parameter.name);
			}
334
			m.this_parameter = new FormalParameter ("this", get_this_type ());
335 336
			m.scope.add (m.this_parameter.name, m.this_parameter);
		}
337
		if (!(m.return_type is VoidType) && m.get_postconditions ().size > 0) {
338 339 340
			if (m.result_var != null) {
				m.scope.remove (m.result_var.name);
			}
341
			m.result_var = new LocalVariable (m.return_type.copy (), "result");
342
			m.result_var.is_result = true;
343
		}
344 345 346
		if (m is CreationMethod) {
			if (m.name == null) {
				default_construction_method = m;
347
				m.name = ".new";
348
			}
349 350

			var cm = (CreationMethod) m;
351 352 353
			if (cm.class_name != null && cm.class_name != name) {
				// class_name is null for constructors generated by GIdlParser
				Report.error (m.source_reference, "missing return type in method `%s.%s´".printf (get_full_name (), cm.class_name));
354 355 356
				m.error = true;
				return;
			}
357 358
		}

359
		methods.add (m);
360
		scope.add (m.name, m);
361 362
	}
	
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
	/**
	 * Adds the specified method as a hidden member to this class,
	 * primarily used for default signal handlers.
	 *
	 * The hidden methods are not part of the `methods` collection.
	 *
	 * There may also be other use cases, eg, convert array.resize() to
	 * this type of method?
	 *
	 * @param m a method
	 */
	public void add_hidden_method (Method m) {
		if (m.binding == MemberBinding.INSTANCE) {
			if (m.this_parameter != null) {
				m.scope.remove (m.this_parameter.name);
			}
			m.this_parameter = new FormalParameter ("this", get_this_type ());
			m.scope.add (m.this_parameter.name, m.this_parameter);
		}
		if (!(m.return_type is VoidType) && m.get_postconditions ().size > 0) {
			if (m.result_var != null) {
				m.scope.remove (m.result_var.name);
			}
			m.result_var = new LocalVariable (m.return_type.copy (), "result");
			m.result_var.is_result = true;
		}

		scope.add (null, m);
	}

393 394 395 396 397
	/**
	 * Returns a copy of the list of methods.
	 *
	 * @return list of methods
	 */
398 399
	public override Gee.List<Method> get_methods () {
		return new ReadOnlyList<Method> (methods);
400 401 402 403 404 405 406
	}
	
	/**
	 * Adds the specified property as a member to this class.
	 *
	 * @param prop a property
	 */
407
	public void add_property (Property prop) {
408
		properties.add (prop);
409 410
		scope.add (prop.name, prop);

411
		prop.this_parameter = new FormalParameter ("this", get_this_type ());
412
		prop.scope.add (prop.this_parameter.name, prop.this_parameter);
413

414 415
		if (prop.field != null) {
			add_field (prop.field);
416
		}
417 418 419 420 421 422 423
	}
	
	/**
	 * Returns a copy of the list of properties.
	 *
	 * @return list of properties
	 */
424 425
	public override Gee.List<Property> get_properties () {
		return new ReadOnlyList<Property> (properties);
426 427 428 429 430 431 432
	}
	
	/**
	 * Adds the specified signal as a member to this class.
	 *
	 * @param sig a signal
	 */
433
	public void add_signal (Signal sig) {
434
		signals.add (sig);
435
		scope.add (sig.name, sig);
436 437 438 439 440 441 442
	}
	
	/**
	 * Returns a copy of the list of signals.
	 *
	 * @return list of signals
	 */
443 444
	public override Gee.List<Signal> get_signals () {
		return new ReadOnlyList<Signal> (signals);
445
	}
446

447 448 449 450 451
	/**
	 * Adds the specified class as an inner class.
	 *
	 * @param cl a class
	 */
452
	public void add_class (Class cl) {
453
		classes.add (cl);
454 455 456 457 458 459 460 461
		scope.add (cl.name, cl);
	}

	/**
	 * Adds the specified struct as an inner struct.
	 *
	 * @param st a struct
	 */
462
	public void add_struct (Struct st) {
463
		structs.add (st);
464 465 466
		scope.add (st.name, st);
	}

467 468 469 470 471 472 473 474 475 476
	/**
	 * Adds the specified enum as an inner enum.
	 *
	 * @param en an enum
	 */
	public void add_enum (Enum en) {
		enums.add (en);
		scope.add (en.name, en);
	}

Jürg Billeter's avatar
Jürg Billeter committed
477 478 479 480 481
	/**
	 * Adds the specified delegate as an inner delegate.
	 *
	 * @param d a delegate
	 */
482
	public void add_delegate (Delegate d) {
Jürg Billeter's avatar
Jürg Billeter committed
483 484 485 486
		delegates.add (d);
		scope.add (d.name, d);
	}

487
	public override void accept (CodeVisitor visitor) {
488 489 490
		visitor.visit_class (this);
	}

491
	public override void accept_children (CodeVisitor visitor) {
492
		foreach (DataType type in base_types) {
493
			type.accept (visitor);
494
		}
495

496
		foreach (TypeParameter p in get_type_parameters ()) {
497
			p.accept (visitor);
498
		}
499 500 501 502 503 504

		/* process enums first to avoid order problems in C code */
		foreach (Enum en in enums) {
			en.accept (visitor);
		}

505 506
		foreach (Field f in fields) {
			f.accept (visitor);
507 508
		}
		
509 510
		foreach (Constant c in constants) {
			c.accept (visitor);
511 512
		}
		
513 514
		foreach (Method m in methods) {
			m.accept (visitor);
515 516
		}
		
517 518
		foreach (Property prop in properties) {
			prop.accept (visitor);
519 520
		}
		
521 522
		foreach (Signal sig in signals) {
			sig.accept (visitor);
523 524
		}
		
525 526
		if (constructor != null) {
			constructor.accept (visitor);
527 528
		}

529 530 531 532
		if (class_constructor != null) {
			class_constructor.accept (visitor);
		}

533 534 535 536
		if (static_constructor != null) {
			static_constructor.accept (visitor);
		}

537 538
		if (destructor != null) {
			destructor.accept (visitor);
539
		}
540

541 542 543 544
		if (static_destructor != null) {
			static_destructor.accept (visitor);
		}

545 546 547
		if (class_destructor != null) {
			class_destructor.accept (visitor);
		}
548 549 550 551 552 553 554 555
		
		foreach (Class cl in classes) {
			cl.accept (visitor);
		}
		
		foreach (Struct st in structs) {
			st.accept (visitor);
		}
556

Jürg Billeter's avatar
Jürg Billeter committed
557 558 559
		foreach (Delegate d in delegates) {
			d.accept (visitor);
		}
560 561
	}

562
	public override string get_cprefix () {
563
		return get_cname ();
564
	}
565

566
	public override string get_cname (bool const_type = false) {
567 568 569 570 571 572
		if (const_type) {
			if (const_cname != null) {
				return const_cname;
			} else if (is_immutable) {
				return "const " + get_cname (false);
			}
573 574
		}

575
		if (cname == null) {
576 577 578 579 580 581 582
			var attr = get_attribute ("CCode");
			if (attr != null) {
				cname = attr.get_string ("cname");
			}
			if (cname == null) {
				cname = get_default_cname ();
			}
583
		}
584 585
		return cname;
	}
586 587 588 589 590 591

	/**
	 * Returns the default name of this class as it is used in C code.
	 *
	 * @return the name to be used in C code by default
	 */
592
	public string get_default_cname () {
593 594 595
		return "%s%s".printf (parent_symbol.get_cprefix (), name);
	}

596 597 598 599 600
	/**
	 * Sets the name of this class as it is used in C code.
	 *
	 * @param cname the name to be used in C code
	 */
601
	public void set_cname (string cname) {
602 603
		this.cname = cname;
	}
604

605
	private string get_lower_case_csuffix () {
606
		if (lower_case_csuffix == null) {
607
			lower_case_csuffix = camel_case_to_lower_case (name);
608 609 610 611 612 613 614 615 616 617

			// remove underscores in some cases to avoid conflicts of type macros
			if (lower_case_csuffix.has_prefix ("type_")) {
				lower_case_csuffix = "type" + lower_case_csuffix.offset ("type_".len ());
			} else if (lower_case_csuffix.has_prefix ("is_")) {
				lower_case_csuffix = "is" + lower_case_csuffix.offset ("is_".len ());
			}
			if (lower_case_csuffix.has_suffix ("_class")) {
				lower_case_csuffix = lower_case_csuffix.substring (0, lower_case_csuffix.len () - "_class".len ()) + "class";
			}
618
		}
619 620
		return lower_case_csuffix;
	}
621

622
	public override string? get_lower_case_cname (string? infix) {
623 624
		if (infix == null) {
			infix = "";
625
		}
626
		return "%s%s%s".printf (parent_symbol.get_lower_case_cprefix (), infix, get_lower_case_csuffix ());
627 628
	}
	
629
	public override string get_lower_case_cprefix () {
630 631 632 633
		if (lower_case_cprefix == null) {
			lower_case_cprefix = "%s_".printf (get_lower_case_cname (null));
		}
		return lower_case_cprefix;
634 635
	}
	
636
	public override string? get_upper_case_cname (string? infix) {
637 638
		return get_lower_case_cname (infix).up ();
	}
639

640 641 642 643
	public override string? get_type_signature () {
		return type_signature;
	}

644 645 646 647
	public override bool is_reference_type () {
		return true;
	}
	
648
	private void process_ccode_attribute (Attribute a) {
649 650 651
		if (a.has_argument ("ref_function")) {
			set_ref_function (a.get_string ("ref_function"));
		}
652 653 654
		if (a.has_argument ("ref_function_void")) {
			this.ref_function_void = a.get_bool ("ref_function_void");
		}
655 656 657
		if (a.has_argument ("unref_function")) {
			set_unref_function (a.get_string ("unref_function"));
		}
658 659 660
		if (a.has_argument ("ref_sink_function")) {
			set_ref_sink_function (a.get_string ("ref_sink_function"));
		}
661 662 663 664 665 666
		if (a.has_argument ("copy_function")) {
			set_dup_function (a.get_string ("copy_function"));
		}
		if (a.has_argument ("free_function")) {
			set_free_function (a.get_string ("free_function"));
		}
667 668 669
		if (a.has_argument ("free_function_address_of")) {
			free_function_address_of = a.get_bool ("free_function_address_of");
		}
670 671 672 673 674 675 676 677 678 679 680 681 682
		if (a.has_argument ("type_id")) {
			type_id = a.get_string ("type_id");
		}
		if (a.has_argument ("marshaller_type_name")) {
			marshaller_type_name = a.get_string ("marshaller_type_name");
		}
		if (a.has_argument ("get_value_function")) {
			get_value_function = a.get_string ("get_value_function");
		}
		if (a.has_argument ("set_value_function")) {
			set_value_function = a.get_string ("set_value_function");
		}

683 684 685 686 687 688
		if (a.has_argument ("const_cname")) {
			const_cname = a.get_string ("const_cname");
		}
		if (a.has_argument ("cprefix")) {
			lower_case_cprefix = a.get_string ("cprefix");
		}
689 690 691
		if (a.has_argument ("lower_case_csuffix")) {
			lower_case_csuffix = a.get_string ("lower_case_csuffix");
		}
692 693 694 695
		if (a.has_argument ("cheader_filename")) {
			var val = a.get_string ("cheader_filename");
			foreach (string filename in val.split (",")) {
				add_cheader_filename (filename);
696 697
			}
		}
698 699 700
		if (a.has_argument ("type_signature")) {
			type_signature = a.get_string ("type_signature");
		}
701 702 703
		if (a.has_argument ("type_check_function")) {
			type_check_function = a.get_string ("type_check_function");
		}
704 705 706 707

		if (a.has_argument ("param_spec_function")) {
			param_spec_function = a.get_string ("param_spec_function");
		}
708
	}
709

710 711 712 713 714 715 716
	/**
	 * Process all associated attributes.
	 */
	public void process_attributes () {
		foreach (Attribute a in attributes) {
			if (a.name == "CCode") {
				process_ccode_attribute (a);
717 718
			} else if (a.name == "Compact") {
				is_compact = true;
719 720
			} else if (a.name == "Immutable") {
				is_immutable = true;
721 722
			}
		}
723
	}
724

725
	public override string? get_type_id () {
726
		if (type_id == null) {
727
			if (!is_compact) {
728 729 730 731
				type_id = get_upper_case_cname ("TYPE_");
			} else {
				type_id = "G_TYPE_POINTER";
			}
732 733 734 735 736
		}
		
		return type_id;
	}

737
	public void set_type_id (string type_id) {
738 739 740
		this.type_id = type_id;
	}

741
	public override string? get_marshaller_type_name () {
742
		if (marshaller_type_name == null) {
743 744
			if (base_class != null) {
				marshaller_type_name = base_class.get_marshaller_type_name ();
745 746 747 748 749 750
			} else {
				marshaller_type_name = "POINTER";
			}
		}

		return marshaller_type_name;
751 752
	}

753
	public override string? get_param_spec_function () {
754
		if (param_spec_function == null) {
755
			param_spec_function = get_default_param_spec_function ();
756 757 758 759 760
		}

		return param_spec_function;
	}

761 762 763 764 765 766 767 768 769 770 771 772
	public string? get_default_param_spec_function () {
		if (is_fundamental ()) {
			return get_lower_case_cname ("param_spec_");
		} else if (base_class != null) {
			return base_class.get_param_spec_function ();
		} else if (get_type_id () == "G_TYPE_POINTER") {
			return "g_param_spec_pointer";
		} else {
			return "g_param_spec_boxed";
		}
	}

773
	public override string? get_get_value_function () {
774
		if (get_value_function == null) {
775
			if (is_fundamental ()) {
776
				get_value_function = get_lower_case_cname ("value_get_");
777
			} else if (base_class != null) {
778
				get_value_function = base_class.get_get_value_function ();
779
			} else if (get_type_id () == "G_TYPE_POINTER") {
780
				get_value_function = "g_value_get_pointer";
781 782
			} else {
				get_value_function = "g_value_get_boxed";
783 784 785 786
			}
		}

		return get_value_function;
787 788
	}
	
789
	public override string? get_set_value_function () {
790
		if (set_value_function == null) {
791
			if (is_fundamental ()) {
792
				set_value_function = get_lower_case_cname ("value_set_");
793
			} else if (base_class != null) {
794
				set_value_function = base_class.get_set_value_function ();
795
			} else if (get_type_id () == "G_TYPE_POINTER") {
796
				set_value_function = "g_value_set_pointer";
797 798
			} else {
				set_value_function = "g_value_set_boxed";
799 800 801 802
			}
		}

		return set_value_function;
803 804
	}

805
	public override bool is_reference_counting () {
806
		return get_ref_function () != null;
807
	}
808

809
	public bool is_fundamental () {
810
		if (!is_compact && base_class == null) {
811 812 813 814 815
			return true;
		}
		return false;
	}

816
	public override string? get_ref_function () {
817 818 819 820
		if (ref_function == null && is_fundamental ()) {
			ref_function = get_lower_case_cprefix () + "ref";
		}

821 822
		if (ref_function == null && base_class != null) {
			return base_class.get_ref_function ();
823 824 825
		} else {
			return ref_function;
		}
826
	}
827

828
	public void set_ref_function (string? name) {
829 830 831
		this.ref_function = name;
	}

832
	public override string? get_unref_function () {
833 834 835 836
		if (unref_function == null && is_fundamental ()) {
			unref_function = get_lower_case_cprefix () + "unref";
		}

837 838
		if (unref_function == null && base_class != null) {
			return base_class.get_unref_function ();
839 840 841 842 843
		} else {
			return unref_function;
		}
	}

844
	public void set_unref_function (string? name) {
845 846 847
		this.unref_function = name;
	}

848 849 850 851 852 853 854 855 856 857 858 859
	public override string? get_ref_sink_function () {
		if (ref_sink_function == null && base_class != null) {
			return base_class.get_ref_sink_function ();
		} else {
			return ref_sink_function;
		}
	}

	public void set_ref_sink_function (string? name) {
		this.ref_sink_function = name;
	}

860
	public override string? get_dup_function () {
861 862 863
		return copy_function;
	}

864
	public void set_dup_function (string? name) {
865 866 867 868
		this.copy_function = name;
	}

	public string get_default_free_function () {
869
		return get_lower_case_cprefix () + "free";
870 871
	}

872
	public override string? get_free_function () {
873
		if (free_function == null) {
874 875 876
			if (base_class != null) {
				return base_class.get_free_function ();
			}
877 878 879 880 881
			free_function = get_default_free_function ();
		}
		return free_function;
	}
	
882
	public void set_free_function (string name) {
883
		this.free_function = name;
884
	}
885
	
886
	public override bool is_subtype_of (TypeSymbol t) {
887 888 889 890
		if (this == t) {
			return true;
		}

891
		foreach (DataType base_type in base_types) {
Jürg Billeter's avatar
Jürg Billeter committed
892
			if (base_type.data_type != null && base_type.data_type.is_subtype_of (t)) {
893 894 895 896 897 898
				return true;
			}
		}
		
		return false;
	}
899

900
	public override void replace_type (DataType old_type, DataType new_type) {
901 902 903 904 905 906 907
		for (int i = 0; i < base_types.size; i++) {
			if (base_types[i] == old_type) {
				base_types[i] = new_type;
				return;
			}
		}
	}
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951

	private void get_all_prerequisites (Interface iface, Gee.List<TypeSymbol> list) {
		foreach (DataType prereq in iface.get_prerequisites ()) {
			TypeSymbol type = prereq.data_type;
			/* skip on previous errors */
			if (type == null) {
				continue;
			}

			list.add (type);
			if (type is Interface) {
				get_all_prerequisites ((Interface) type, list);

			}
		}
	}

	private bool class_is_a (Class cl, TypeSymbol t) {
		if (cl == t) {
			return true;
		}

		foreach (DataType base_type in cl.get_base_types ()) {
			if (base_type.data_type is Class) {
				if (class_is_a ((Class) base_type.data_type, t)) {
					return true;
				}
			} else if (base_type.data_type == t) {
				return true;
			}
		}

		return false;
	}

	public override bool check (SemanticAnalyzer analyzer) {
		if (checked) {
			return !error;
		}

		checked = true;

		process_attributes ();

952 953 954 955 956 957
		var old_source_file = analyzer.current_source_file;
		var old_symbol = analyzer.current_symbol;

		if (source_reference != null) {
			analyzer.current_source_file = source_reference.file;
		}
958 959 960
		analyzer.current_symbol = this;

		foreach (DataType base_type_reference in get_base_types ()) {
961 962 963 964 965
			if (!base_type_reference.check (analyzer)) {
				error = true;
				return false;
			}

966 967 968 969 970 971
			if (!(base_type_reference is ObjectType)) {
				error = true;
				Report.error (source_reference, "base type `%s` of class `%s` is not an object type".printf (base_type_reference.to_string (), get_full_name ()));
				return false;
			}

972 973 974 975 976 977 978 979
			// check whether base type is at least as accessible as the class
			if (!analyzer.is_type_accessible (this, base_type_reference)) {
				error = true;
				Report.error (source_reference, "base type `%s` is less accessible than class `%s`".printf (base_type_reference.to_string (), get_full_name ()));
				return false;
			}
		}

980 981 982 983
		foreach (DataType type in base_types) {
			type.check (analyzer);
		}

984
		foreach (TypeParameter p in get_type_parameters ()) {
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
			p.check (analyzer);
		}

		/* process enums first to avoid order problems in C code */
		foreach (Enum en in enums) {
			en.check (analyzer);
		}

		foreach (Field f in fields) {
			f.check (analyzer);
		}
		
		foreach (Constant c in constants) {
			c.check (analyzer);
		}
		
		foreach (Method m in methods) {
			m.check (analyzer);
		}
		
		foreach (Property prop in properties) {
			prop.check (analyzer);
		}
		
		foreach (Signal sig in signals) {
			sig.check (analyzer);
		}
		
		if (constructor != null) {
			constructor.check (analyzer);
		}

		if (class_constructor != null) {
			class_constructor.check (analyzer);
		}

		if (static_constructor != null) {
			static_constructor.check (analyzer);
		}

		if (destructor != null) {
			destructor.check (analyzer);
		}
1028 1029 1030 1031

		if (static_destructor != null) {
			static_destructor.check (analyzer);
		}
1032
		
1033 1034 1035 1036
		if (class_destructor != null) {
			class_destructor.check (analyzer);
		}
		
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
		foreach (Class cl in classes) {
			cl.check (analyzer);
		}
		
		foreach (Struct st in structs) {
			st.check (analyzer);
		}

		foreach (Delegate d in delegates) {
			d.check (analyzer);
		}
1048 1049 1050 1051 1052 1053 1054 1055 1056

		/* compact classes cannot implement interfaces */
		if (is_compact) {
			foreach (DataType base_type in get_base_types ()) {
				if (base_type.data_type is Interface) {
					error = true;
					Report.error (source_reference, "compact classes `%s` may not implement interfaces".printf (get_full_name ()));
				}
			}
1057

1058
			if (!external && !external_package && base_class != null) {
1059 1060 1061 1062 1063 1064 1065
				foreach (Field f in fields) {
					if (f.binding == MemberBinding.INSTANCE) {
						error = true;
						Report.error (source_reference, "derived compact classes may not have instance fields");
					}
				}
			}
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
		}

		/* gather all prerequisites */
		Gee.List<TypeSymbol> prerequisites = new ArrayList<TypeSymbol> ();
		foreach (DataType base_type in get_base_types ()) {
			if (base_type.data_type is Interface) {
				get_all_prerequisites ((Interface) base_type.data_type, prerequisites);
			}
		}
		/* check whether all prerequisites are met */
		Gee.List<string> missing_prereqs = new ArrayList<string> ();
		foreach (TypeSymbol prereq in prerequisites) {
			if (!class_is_a (this, prereq)) {
				missing_prereqs.insert (0, prereq.get_full_name ());
			}
		}
		/* report any missing prerequisites */
		if (missing_prereqs.size > 0) {
			error = true;

			string error_string = "%s: some prerequisites (".printf (get_full_name ());
			bool first = true;
			foreach (string s in missing_prereqs) {
				if (first) {
					error_string = "%s`%s'".printf (error_string, s);
					first = false;
				} else {
					error_string = "%s, `%s'".printf (error_string, s);
				}
			}
			error_string += ") are not met";
			Report.error (source_reference, error_string);
		}

		/* VAPI classes don't have to specify overridden methods */
		if (!external_package) {
			/* all abstract symbols defined in base types have to be at least defined (or implemented) also in this type */
			foreach (DataType base_type in get_base_types ()) {
				if (base_type.data_type is Interface) {
					Interface iface = (Interface) base_type.data_type;

					if (base_class != null && base_class.is_subtype_of (iface)) {
						// reimplementation of interface, class is not required to reimplement all methods
						break;
					}

					/* We do not need to do expensive equality checking here since this is done
					 * already. We only need to guarantee the symbols are present.
					 */

					/* check methods */
					foreach (Method m in iface.get_methods ()) {
						if (m.is_abstract) {
							Symbol sym = null;
							var base_class = this;
							while (base_class != null && !(sym is Method)) {
								sym = base_class.scope.lookup (m.name);
								base_class = base_class.base_class;
							}
1125 1126 1127 1128
							if (sym is Method) {
								// method is used as interface implementation, so it is not unused
								sym.used = true;
							} else {
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
								error = true;
								Report.error (source_reference, "`%s' does not implement interface method `%s'".printf (get_full_name (), m.get_full_name ()));
							}
						}
					}

					/* check properties */
					foreach (Property prop in iface.get_properties ()) {
						if (prop.is_abstract) {
							Symbol sym = null;
							var base_class = this;
							while (base_class != null && !(sym is Property)) {
								sym = base_class.scope.lookup (prop.name);
								base_class = base_class.base_class;
							}
1144 1145 1146 1147
							if (sym is Property) {
								// property is used as interface implementation, so it is not unused
								sym.used = true;
							} else {
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
								error = true;
								Report.error (source_reference, "`%s' does not implement interface property `%s'".printf (get_full_name (), prop.get_full_name ()));
							}
						}
					}
				}
			}

			/* all abstract symbols defined in base classes have to be implemented in non-abstract classes */
			if (!is_abstract) {
				var base_class = base_class;
				while (base_class != null && base_class.is_abstract) {
					foreach (Method base_method in base_class.get_methods ()) {
						if (base_method.is_abstract) {
							var override_method = analyzer.symbol_lookup_inherited (this, base_method.name) as Method;
							if (override_method == null || !override_method.overrides) {
								error = true;
								Report.error (source_reference, "`%s' does not implement abstract method `%s'".printf (get_full_name (), base_method.get_full_name ()));
							}
						}
					}
					foreach (Property base_property in base_class.get_properties ()) {
						if (base_property.is_abstract) {
							var override_property = analyzer.symbol_lookup_inherited (this, base_property.name) as Property;
							if (override_property == null || !override_property.overrides) {
								error = true;
								Report.error (source_reference, "`%s' does not implement abstract property `%s'".printf (get_full_name (), base_property.get_full_name ()));
							}
						}
					}
					base_class = base_class.base_class;
				}
			}
		}

1183 1184
		analyzer.current_source_file = old_source_file;
		analyzer.current_symbol = old_symbol;
1185 1186 1187

		return !error;
	}
1188
}
1189

1190
// vim:sw=8 noet