valaclass.vala 29.6 KB
Newer Older
1 2
/* valaclass.vala
 *
3
 * Copyright (C) 2006-2010  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 24

 * 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;

25 26 27
/**
 * Represents a class declaration in the source code.
 */
28
public class Vala.Class : ObjectTypeSymbol {
29 30 31 32 33 34 35 36 37 38
	/**
	 * 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; }
39

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
	/**
	 * 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;
		}
	}

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
	/**
	 * 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;
		}
	}

72 73 74 75
	/**
	 * Specifies wheather the ref function returns void instead of the
	 * object.
	 */
76 77 78 79 80 81 82 83 84 85 86
	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;
		}
	}
87

88 89 90 91 92 93 94
	/**
	 * 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; }

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

105 106 107 108 109 110
	/**
	 * 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; }

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

130
	private List<DataType> base_types = new ArrayList<DataType> ();
131

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

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

144 145 146 147 148
	/**
	 * Returns a copy of the list of classes.
	 *
	 * @return list of classes
	 */
149
	public List<Class> get_classes () {
150
		return classes;
151 152 153 154 155 156 157
	}

	/**
	 * Returns a copy of the list of structs.
	 *
	 * @return list of structs
	 */
158
	public List<Struct> get_structs () {
159
		return structs;
160 161 162 163 164 165 166
	}

	/**
	 * Returns a copy of the list of enums.
	 *
	 * @return list of enums
	 */
167
	public List<Enum> get_enums () {
168
		return enums;
169 170 171 172 173 174 175
	}

	/**
	 * Returns a copy of the list of delegates.
	 *
	 * @return list of delegates
	 */
176
	public List<Delegate> get_delegates () {
177
		return delegates;
178 179
	}

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

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

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

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

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

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

236 237
	Destructor? _destructor;

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

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

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

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

	/**
	 * Returns a copy of the list of constants.
	 *
	 * @return list of constants
	 */
309
	public List<Constant> get_constants () {
310
		return constants;
311 312
	}

313 314 315 316 317
	/**
	 * Adds the specified method as a member to this class.
	 *
	 * @param m a method
	 */
318
	public void add_method (Method m) {
319
		if (m.binding == MemberBinding.INSTANCE || m is CreationMethod) {
320 321 322
			if (m.this_parameter != null) {
				m.scope.remove (m.this_parameter.name);
			}
323
			m.this_parameter = new FormalParameter ("this", get_this_type ());
324 325
			m.scope.add (m.this_parameter.name, m.this_parameter);
		}
326
		if (!(m.return_type is VoidType) && (CodeContext.get ().profile == Profile.DOVA || m.get_postconditions ().size > 0)) {
327 328 329
			if (m.result_var != null) {
				m.scope.remove (m.result_var.name);
			}
330
			m.result_var = new LocalVariable (m.return_type.copy (), "result", null, source_reference);
331
			m.result_var.is_result = true;
332
		}
333 334 335
		if (m is CreationMethod) {
			if (m.name == null) {
				default_construction_method = m;
336
				m.name = ".new";
337
			}
338 339

			var cm = (CreationMethod) m;
340 341 342
			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));
343 344 345
				m.error = true;
				return;
			}
346 347
		}

348
		methods.add (m);
349
		scope.add (m.name, m);
350
	}
351

352 353 354 355 356
	/**
	 * Returns a copy of the list of methods.
	 *
	 * @return list of methods
	 */
357
	public override List<Method> get_methods () {
358
		return methods;
359 360 361 362 363 364 365
	}
	
	/**
	 * Adds the specified property as a member to this class.
	 *
	 * @param prop a property
	 */
366
	public void add_property (Property prop) {
367
		properties.add (prop);
368 369
		scope.add (prop.name, prop);

370
		prop.this_parameter = new FormalParameter ("this", get_this_type ());
371
		prop.scope.add (prop.this_parameter.name, prop.this_parameter);
372

373 374
		if (prop.field != null) {
			add_field (prop.field);
375
		}
376 377 378 379 380 381 382
	}
	
	/**
	 * Returns a copy of the list of properties.
	 *
	 * @return list of properties
	 */
383
	public override List<Property> get_properties () {
384
		return properties;
385 386 387 388 389 390 391
	}
	
	/**
	 * Adds the specified signal as a member to this class.
	 *
	 * @param sig a signal
	 */
392
	public void add_signal (Signal sig) {
393
		signals.add (sig);
394
		scope.add (sig.name, sig);
395 396 397 398 399 400 401
	}
	
	/**
	 * Returns a copy of the list of signals.
	 *
	 * @return list of signals
	 */
402
	public override List<Signal> get_signals () {
403
		return signals;
404
	}
405

406 407 408 409 410
	/**
	 * Adds the specified class as an inner class.
	 *
	 * @param cl a class
	 */
411
	public void add_class (Class cl) {
412
		classes.add (cl);
413 414 415 416 417 418 419 420
		scope.add (cl.name, cl);
	}

	/**
	 * Adds the specified struct as an inner struct.
	 *
	 * @param st a struct
	 */
421
	public void add_struct (Struct st) {
422
		structs.add (st);
423 424 425
		scope.add (st.name, st);
	}

426 427 428 429 430 431 432 433 434 435
	/**
	 * 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
436 437 438 439 440
	/**
	 * Adds the specified delegate as an inner delegate.
	 *
	 * @param d a delegate
	 */
441
	public void add_delegate (Delegate d) {
Jürg Billeter's avatar
Jürg Billeter committed
442 443 444 445
		delegates.add (d);
		scope.add (d.name, d);
	}

446
	public override void accept (CodeVisitor visitor) {
447 448 449
		visitor.visit_class (this);
	}

450
	public override void accept_children (CodeVisitor visitor) {
451
		foreach (DataType type in base_types) {
452
			type.accept (visitor);
453
		}
454

455
		foreach (TypeParameter p in get_type_parameters ()) {
456
			p.accept (visitor);
457
		}
458 459 460 461 462 463

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

464 465
		foreach (Field f in fields) {
			f.accept (visitor);
466 467
		}
		
468 469
		foreach (Constant c in constants) {
			c.accept (visitor);
470 471
		}
		
472 473
		foreach (Method m in methods) {
			m.accept (visitor);
474 475
		}
		
476 477
		foreach (Property prop in properties) {
			prop.accept (visitor);
478 479
		}
		
480 481
		foreach (Signal sig in signals) {
			sig.accept (visitor);
482 483
		}
		
484 485
		if (constructor != null) {
			constructor.accept (visitor);
486 487
		}

488 489 490 491
		if (class_constructor != null) {
			class_constructor.accept (visitor);
		}

492 493 494 495
		if (static_constructor != null) {
			static_constructor.accept (visitor);
		}

496 497
		if (destructor != null) {
			destructor.accept (visitor);
498
		}
499

500 501 502 503
		if (static_destructor != null) {
			static_destructor.accept (visitor);
		}

504 505 506
		if (class_destructor != null) {
			class_destructor.accept (visitor);
		}
507 508 509 510 511 512 513 514
		
		foreach (Class cl in classes) {
			cl.accept (visitor);
		}
		
		foreach (Struct st in structs) {
			st.accept (visitor);
		}
515

Jürg Billeter's avatar
Jürg Billeter committed
516 517 518
		foreach (Delegate d in delegates) {
			d.accept (visitor);
		}
519 520
	}

521
	public override string get_cprefix () {
522
		return get_cname ();
523
	}
524

525
	public override string get_cname (bool const_type = false) {
526 527 528 529 530 531
		if (const_type) {
			if (const_cname != null) {
				return const_cname;
			} else if (is_immutable) {
				return "const " + get_cname (false);
			}
532 533
		}

534
		if (cname == null) {
535 536 537 538 539 540 541
			var attr = get_attribute ("CCode");
			if (attr != null) {
				cname = attr.get_string ("cname");
			}
			if (cname == null) {
				cname = get_default_cname ();
			}
542
		}
543 544
		return cname;
	}
545 546 547 548 549 550

	/**
	 * 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
	 */
551
	public string get_default_cname () {
552 553 554
		return "%s%s".printf (parent_symbol.get_cprefix (), name);
	}

555 556 557 558 559
	/**
	 * Sets the name of this class as it is used in C code.
	 *
	 * @param cname the name to be used in C code
	 */
560
	public void set_cname (string cname) {
561 562
		this.cname = cname;
	}
563

564
	private string get_lower_case_csuffix () {
565
		if (lower_case_csuffix == null) {
566
			lower_case_csuffix = camel_case_to_lower_case (name);
567 568 569 570 571 572 573 574 575 576

			// 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";
			}
577
		}
578 579
		return lower_case_csuffix;
	}
580

581
	public override string? get_lower_case_cname (string? infix) {
582 583
		if (infix == null) {
			infix = "";
584
		}
585
		return "%s%s%s".printf (parent_symbol.get_lower_case_cprefix (), infix, get_lower_case_csuffix ());
586 587
	}
	
588
	public override string get_lower_case_cprefix () {
589 590 591 592
		if (lower_case_cprefix == null) {
			lower_case_cprefix = "%s_".printf (get_lower_case_cname (null));
		}
		return lower_case_cprefix;
593 594
	}
	
595
	public override string? get_upper_case_cname (string? infix) {
596 597
		return get_lower_case_cname (infix).up ();
	}
598

599 600 601 602
	public override bool is_reference_type () {
		return true;
	}
	
603
	private void process_ccode_attribute (Attribute a) {
604 605 606
		if (a.has_argument ("ref_function")) {
			set_ref_function (a.get_string ("ref_function"));
		}
607 608 609
		if (a.has_argument ("ref_function_void")) {
			this.ref_function_void = a.get_bool ("ref_function_void");
		}
610 611 612
		if (a.has_argument ("unref_function")) {
			set_unref_function (a.get_string ("unref_function"));
		}
613 614 615
		if (a.has_argument ("ref_sink_function")) {
			set_ref_sink_function (a.get_string ("ref_sink_function"));
		}
616 617 618 619 620 621
		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"));
		}
622 623 624
		if (a.has_argument ("free_function_address_of")) {
			free_function_address_of = a.get_bool ("free_function_address_of");
		}
625 626 627 628 629 630 631 632 633 634 635 636
		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");
		}
637 638 639
		if (a.has_argument ("take_value_function")) {
			take_value_function = a.get_string ("take_value_function");
		}
640

641 642 643 644 645 646
		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");
		}
647 648 649
		if (a.has_argument ("lower_case_csuffix")) {
			lower_case_csuffix = a.get_string ("lower_case_csuffix");
		}
650 651 652 653
		if (a.has_argument ("cheader_filename")) {
			var val = a.get_string ("cheader_filename");
			foreach (string filename in val.split (",")) {
				add_cheader_filename (filename);
654 655
			}
		}
656 657 658
		if (a.has_argument ("type_check_function")) {
			type_check_function = a.get_string ("type_check_function");
		}
659 660 661 662

		if (a.has_argument ("param_spec_function")) {
			param_spec_function = a.get_string ("param_spec_function");
		}
663
	}
664

665 666 667 668 669 670 671
	/**
	 * Process all associated attributes.
	 */
	public void process_attributes () {
		foreach (Attribute a in attributes) {
			if (a.name == "CCode") {
				process_ccode_attribute (a);
672 673
			} else if (a.name == "Compact") {
				is_compact = true;
674 675
			} else if (a.name == "Immutable") {
				is_immutable = true;
676 677
			} else if (a.name == "Deprecated") {
				process_deprecated_attribute (a);
678 679
			}
		}
680
	}
681

682 683 684 685 686 687 688 689
	public string? get_default_type_id () {
		if (is_compact) {
			return "G_TYPE_POINTER";
		}

		return get_upper_case_cname ("TYPE_");
	}

690
	public override string? get_type_id () {
691
		if (type_id == null) {
692
			type_id = get_default_type_id ();
693
		}
694

695 696 697
		return type_id;
	}

698
	public void set_type_id (string type_id) {
699 700 701
		this.type_id = type_id;
	}

702
	public override string? get_marshaller_type_name () {
703
		if (marshaller_type_name == null) {
704 705
			if (base_class != null) {
				marshaller_type_name = base_class.get_marshaller_type_name ();
706 707
			} else if (!is_compact) {
				marshaller_type_name = get_upper_case_cname ();
708
			} else if (get_type_id () == "G_TYPE_POINTER") {
709
				marshaller_type_name = "POINTER";
710 711
			} else {
				marshaller_type_name = "BOXED";
712 713 714 715
			}
		}

		return marshaller_type_name;
716 717
	}

718
	public override string? get_param_spec_function () {
719
		if (param_spec_function == null) {
720
			param_spec_function = get_default_param_spec_function ();
721 722 723 724 725
		}

		return param_spec_function;
	}

726 727 728 729 730 731 732 733 734 735 736 737
	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";
		}
	}

738
	public override string? get_get_value_function () {
739
		if (get_value_function == null) {
740
			if (is_fundamental ()) {
741
				get_value_function = get_lower_case_cname ("value_get_");
742
			} else if (base_class != null) {
743
				get_value_function = base_class.get_get_value_function ();
744
			} else if (get_type_id () == "G_TYPE_POINTER") {
745
				get_value_function = "g_value_get_pointer";
746 747
			} else {
				get_value_function = "g_value_get_boxed";
748 749 750 751
			}
		}

		return get_value_function;
752 753
	}
	
754
	public override string? get_set_value_function () {
755
		if (set_value_function == null) {
756
			if (is_fundamental ()) {
757
				set_value_function = get_lower_case_cname ("value_set_");
758
			} else if (base_class != null) {
759
				set_value_function = base_class.get_set_value_function ();
760
			} else if (get_type_id () == "G_TYPE_POINTER") {
761
				set_value_function = "g_value_set_pointer";
762 763
			} else {
				set_value_function = "g_value_set_boxed";
764 765 766 767
			}
		}

		return set_value_function;
768 769
	}

770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
	public override string? get_take_value_function () {
		if (take_value_function == null) {
			if (is_fundamental ()) {
				take_value_function = get_lower_case_cname ("value_take_");
			} else if (base_class != null) {
				take_value_function = base_class.get_take_value_function ();
			} else if (get_type_id () == "G_TYPE_POINTER") {
				take_value_function = "g_value_set_pointer";
			} else {
				take_value_function = "g_value_take_boxed";
			}
		}

		return take_value_function;
	}

786
	public override bool is_reference_counting () {
787
		return get_ref_function () != null;
788
	}
789

790
	public bool is_fundamental () {
791
		if (!is_compact && base_class == null) {
792 793 794 795 796
			return true;
		}
		return false;
	}

797
	public override string? get_ref_function () {
798 799 800 801
		if (ref_function == null && is_fundamental ()) {
			ref_function = get_lower_case_cprefix () + "ref";
		}

802 803
		if (ref_function == null && base_class != null) {
			return base_class.get_ref_function ();
804 805 806
		} else {
			return ref_function;
		}
807
	}
808

809
	public void set_ref_function (string? name) {
810 811 812
		this.ref_function = name;
	}

813
	public override string? get_unref_function () {
814 815 816 817
		if (unref_function == null && is_fundamental ()) {
			unref_function = get_lower_case_cprefix () + "unref";
		}

818 819
		if (unref_function == null && base_class != null) {
			return base_class.get_unref_function ();
820 821 822 823 824
		} else {
			return unref_function;
		}
	}

825
	public void set_unref_function (string? name) {
826 827 828
		this.unref_function = name;
	}

829 830 831 832 833 834 835 836 837 838 839 840
	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;
	}

841
	public override string? get_dup_function () {
842 843 844
		return copy_function;
	}

845
	public void set_dup_function (string? name) {
846 847 848 849
		this.copy_function = name;
	}

	public string get_default_free_function () {
850
		return get_lower_case_cprefix () + "free";
851 852
	}

853
	public override string? get_free_function () {
854
		if (free_function == null) {
855 856 857
			if (base_class != null) {
				return base_class.get_free_function ();
			}
858 859 860 861 862
			free_function = get_default_free_function ();
		}
		return free_function;
	}
	
863
	public void set_free_function (string name) {
864
		this.free_function = name;
865
	}
866
	
867
	public override bool is_subtype_of (TypeSymbol t) {
868 869 870 871
		if (this == t) {
			return true;
		}

872
		foreach (DataType base_type in base_types) {
Jürg Billeter's avatar
Jürg Billeter committed
873
			if (base_type.data_type != null && base_type.data_type.is_subtype_of (t)) {
874 875 876 877 878 879
				return true;
			}
		}
		
		return false;
	}
880

881
	public override void replace_type (DataType old_type, DataType new_type) {
882 883 884 885 886 887 888
		for (int i = 0; i < base_types.size; i++) {
			if (base_types[i] == old_type) {
				base_types[i] = new_type;
				return;
			}
		}
	}
889

890
	private void get_all_prerequisites (Interface iface, List<TypeSymbol> list) {
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 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
		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 ();

933 934 935 936 937 938
		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;
		}
939 940 941
		analyzer.current_symbol = this;

		foreach (DataType base_type_reference in get_base_types ()) {
942 943 944 945 946
			if (!base_type_reference.check (analyzer)) {
				error = true;
				return false;
			}

947 948 949 950 951 952
			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;
			}

953 954 955 956 957 958
			// 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;
			}
959 960 961 962 963 964 965 966 967 968 969 970

			int n_type_args = base_type_reference.get_type_arguments ().size;
			int n_type_params = ((ObjectTypeSymbol) base_type_reference.data_type).get_type_parameters ().size;
			if (n_type_args < n_type_params) {
				error = true;
				Report.error (base_type_reference.source_reference, "too few type arguments");
				return false;
			} else if (n_type_args > n_type_params) {
				error = true;
				Report.error (base_type_reference.source_reference, "too many type arguments");
				return false;
			}
971 972
		}

973 974 975 976
		foreach (DataType type in base_types) {
			type.check (analyzer);
		}

977
		foreach (TypeParameter p in get_type_parameters ()) {
978 979 980 981 982 983 984 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
			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);
		}
1021 1022 1023 1024

		if (static_destructor != null) {
			static_destructor.check (analyzer);
		}
1025
		
1026 1027 1028 1029
		if (class_destructor != null) {
			class_destructor.check (analyzer);
		}
		
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
		foreach (Class cl in classes) {
			cl.check (analyzer);
		}
		
		foreach (Struct st in structs) {
			st.check (analyzer);
		}

		foreach (Delegate d in delegates) {
			d.check (analyzer);
		}
1041 1042 1043 1044 1045 1046 1047 1048 1049

		/* 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 ()));
				}
			}
1050

1051
			if (!external && !external_package && base_class != null) {
1052 1053 1054 1055 1056 1057 1058
				foreach (Field f in fields) {
					if (f.binding == MemberBinding.INSTANCE) {
						error = true;
						Report.error (source_reference, "derived compact classes may not have instance fields");
					}
				}
			}
1059 1060 1061
		}

		/* gather all prerequisites */
1062
		List<TypeSymbol> prerequisites = new ArrayList<TypeSymbol> ();
1063 1064 1065 1066 1067 1068
		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 */
1069
		List<string> missing_prereqs = new ArrayList<string> ();
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
		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;
							}
1118 1119
							if (sym is Method) {
								// method is used as interface implementation, so it is not unused
1120
								sym.check_deprecated (source_reference);
1121 1122
								sym.used = true;
							} else {
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
								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;
							}
1138 1139
							if (sym is Property) {
								// property is used as interface implementation, so it is not unused
1140
								sym.check_deprecated (source_reference);
1141 1142
								sym.used = true;
							} else {
1143 1144 1145 1146 1147 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
								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;
				}
			}
		}

1178 1179
		analyzer.current_source_file = old_source_file;
		analyzer.current_symbol = old_symbol;
1180 1181 1182

		return !error;
	}
1183
}
1184

1185
// vim:sw=8 noet