valaclass.vala 32.2 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; 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 112
	public bool is_gboxed { get { return (free_function == "g_boxed_free"); } }

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

132
	private List<DataType> base_types = new ArrayList<DataType> ();
133

134 135 136 137 138
	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> ();
139 140

	// inner types
141 142 143 144
	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
145

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

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

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

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

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

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

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

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

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

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

238 239
	Destructor? _destructor;

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

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

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

	/**
	 * Adds the specified constant as a member to this class.
	 *
	 * @param c a constant
	 */
277
	public override void add_constant (Constant c) {
278
		constants.add (c);
279
		scope.add (c.name, c);
280 281 282 283 284 285 286
	}
	
	/**
	 * Adds the specified field as a member to this class.
	 *
	 * @param f a field
	 */
287
	public override void add_field (Field f) {
288 289 290
		if (CodeContext.get ().profile == Profile.DOVA &&
		    f.binding == MemberBinding.INSTANCE &&
		    (f.access == SymbolAccessibility.PUBLIC || f.access == SymbolAccessibility.PROTECTED) &&
291
		    name != "any" /* temporary workaround */) {
292 293
			// public/protected instance fields not supported, convert to automatic property

Jürg Billeter's avatar
Jürg Billeter committed
294
			var prop = new Property (f.name, f.variable_type.copy (), null, null, f.source_reference, comment);
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
			prop.access = access;

			var get_type = prop.property_type.copy ();
			get_type.value_owned = true;

			prop.get_accessor = new PropertyAccessor (true, false, false, get_type, null, f.source_reference);

			prop.set_accessor = new PropertyAccessor (false, true, false, prop.property_type.copy (), null, f.source_reference);

			f.name = "_%s".printf (f.name);
			f.access = SymbolAccessibility.PRIVATE;
			prop.field = f;

			add_property (prop);
			return;
		}

312
		fields.add (f);
313
		if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.INSTANCE) {
314
			has_private_fields = true;
315 316
		} else if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.CLASS) {
			has_class_private_fields = true;
317
		}
318
		scope.add (f.name, f);
319 320 321 322 323 324 325
	}
	
	/**
	 * Returns a copy of the list of fields.
	 *
	 * @return list of fields
	 */
326
	public List<Field> get_fields () {
327
		return fields;
328
	}
329 330 331 332 333 334

	/**
	 * Returns a copy of the list of constants.
	 *
	 * @return list of constants
	 */
335
	public List<Constant> get_constants () {
336
		return constants;
337 338
	}

339 340 341 342 343
	/**
	 * Adds the specified method as a member to this class.
	 *
	 * @param m a method
	 */
344
	public override void add_method (Method m) {
345
		if (m.binding == MemberBinding.INSTANCE || m is CreationMethod) {
346 347 348
			if (m.this_parameter != null) {
				m.scope.remove (m.this_parameter.name);
			}
349
			m.this_parameter = new Parameter ("this", get_this_type ());
350 351
			m.scope.add (m.this_parameter.name, m.this_parameter);
		}
352
		if (!(m.return_type is VoidType) && (CodeContext.get ().profile == Profile.DOVA || m.get_postconditions ().size > 0)) {
353 354 355
			if (m.result_var != null) {
				m.scope.remove (m.result_var.name);
			}
356
			m.result_var = new LocalVariable (m.return_type.copy (), "result", null, source_reference);
357
			m.result_var.is_result = true;
358
		}
359 360 361
		if (m is CreationMethod) {
			if (m.name == null) {
				default_construction_method = m;
362
				m.name = ".new";
363
			}
364 365

			var cm = (CreationMethod) m;
366 367 368
			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));
369 370 371
				m.error = true;
				return;
			}
372 373
		}

374
		methods.add (m);
375
		scope.add (m.name, m);
376
	}
377

378 379 380 381 382
	/**
	 * Returns a copy of the list of methods.
	 *
	 * @return list of methods
	 */
383
	public override List<Method> get_methods () {
384
		return methods;
385 386 387 388 389 390 391
	}
	
	/**
	 * Adds the specified property as a member to this class.
	 *
	 * @param prop a property
	 */
392
	public override void add_property (Property prop) {
393
		properties.add (prop);
394 395
		scope.add (prop.name, prop);

396
		prop.this_parameter = new Parameter ("this", get_this_type ());
397
		prop.scope.add (prop.this_parameter.name, prop.this_parameter);
398

399 400
		if (prop.field != null) {
			add_field (prop.field);
401
		}
402 403 404 405 406 407 408
	}
	
	/**
	 * Returns a copy of the list of properties.
	 *
	 * @return list of properties
	 */
409
	public override List<Property> get_properties () {
410
		return properties;
411 412 413 414 415 416 417
	}
	
	/**
	 * Adds the specified signal as a member to this class.
	 *
	 * @param sig a signal
	 */
418
	public override void add_signal (Signal sig) {
419
		signals.add (sig);
420
		scope.add (sig.name, sig);
421 422 423 424 425 426 427
	}
	
	/**
	 * Returns a copy of the list of signals.
	 *
	 * @return list of signals
	 */
428
	public override List<Signal> get_signals () {
429
		return signals;
430
	}
431

432 433 434 435 436
	/**
	 * Adds the specified class as an inner class.
	 *
	 * @param cl a class
	 */
437
	public override void add_class (Class cl) {
438
		classes.add (cl);
439 440 441 442 443 444 445 446
		scope.add (cl.name, cl);
	}

	/**
	 * Adds the specified struct as an inner struct.
	 *
	 * @param st a struct
	 */
447
	public override void add_struct (Struct st) {
448
		structs.add (st);
449 450 451
		scope.add (st.name, st);
	}

452 453 454 455 456
	/**
	 * Adds the specified enum as an inner enum.
	 *
	 * @param en an enum
	 */
457
	public override void add_enum (Enum en) {
458 459 460 461
		enums.add (en);
		scope.add (en.name, en);
	}

Jürg Billeter's avatar
Jürg Billeter committed
462 463 464 465 466
	/**
	 * Adds the specified delegate as an inner delegate.
	 *
	 * @param d a delegate
	 */
467
	public override void add_delegate (Delegate d) {
Jürg Billeter's avatar
Jürg Billeter committed
468 469 470 471
		delegates.add (d);
		scope.add (d.name, d);
	}

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
	public override void add_constructor (Constructor c) {
		if (c.binding == MemberBinding.INSTANCE) {
			if (constructor != null) {
				Report.error (c.source_reference, "class already contains a constructor");
			}
			constructor = c;
		} else if (c.binding == MemberBinding.CLASS) {
			if (class_constructor != null) {
				Report.error (c.source_reference, "class already contains a class constructor");
			}
			class_constructor = c;
		} else {
			if (static_constructor != null) {
				Report.error (c.source_reference, "class already contains a static constructor");
			}
			static_constructor = c;
		}
	}

	public override void add_destructor (Destructor d) {
		if (d.binding == MemberBinding.INSTANCE) {
			if (destructor != null) {
				Report.error (d.source_reference, "class already contains a destructor");
			}
			destructor = d;
		} else if (d.binding == MemberBinding.CLASS) {
			if (class_destructor != null) {
				Report.error (d.source_reference, "class already contains a class destructor");
			}
			class_destructor = d;
		} else {
			if (static_destructor != null) {
				Report.error (d.source_reference, "class already contains a static destructor");
			}
			static_destructor = d;
		}
	}

510
	public override void accept (CodeVisitor visitor) {
511 512 513
		visitor.visit_class (this);
	}

514
	public override void accept_children (CodeVisitor visitor) {
515
		foreach (DataType type in base_types) {
516
			type.accept (visitor);
517
		}
518

519
		foreach (TypeParameter p in get_type_parameters ()) {
520
			p.accept (visitor);
521
		}
522 523 524 525 526 527

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

528 529
		foreach (Field f in fields) {
			f.accept (visitor);
530 531
		}
		
532 533
		foreach (Constant c in constants) {
			c.accept (visitor);
534 535
		}
		
536 537
		foreach (Method m in methods) {
			m.accept (visitor);
538 539
		}
		
540 541
		foreach (Property prop in properties) {
			prop.accept (visitor);
542 543
		}
		
544 545
		foreach (Signal sig in signals) {
			sig.accept (visitor);
546 547
		}
		
548 549
		if (constructor != null) {
			constructor.accept (visitor);
550 551
		}

552 553 554 555
		if (class_constructor != null) {
			class_constructor.accept (visitor);
		}

556 557 558 559
		if (static_constructor != null) {
			static_constructor.accept (visitor);
		}

560 561
		if (destructor != null) {
			destructor.accept (visitor);
562
		}
563

564 565 566 567
		if (static_destructor != null) {
			static_destructor.accept (visitor);
		}

568 569 570
		if (class_destructor != null) {
			class_destructor.accept (visitor);
		}
571 572 573 574 575 576 577 578
		
		foreach (Class cl in classes) {
			cl.accept (visitor);
		}
		
		foreach (Struct st in structs) {
			st.accept (visitor);
		}
579

Jürg Billeter's avatar
Jürg Billeter committed
580 581 582
		foreach (Delegate d in delegates) {
			d.accept (visitor);
		}
583 584
	}

585
	public override string get_cprefix () {
586
		return get_cname ();
587
	}
588

589
	public override string get_cname (bool const_type = false) {
590 591 592 593 594 595
		if (const_type) {
			if (const_cname != null) {
				return const_cname;
			} else if (is_immutable) {
				return "const " + get_cname (false);
			}
596 597
		}

598
		if (cname == null) {
599 600 601 602 603 604 605
			var attr = get_attribute ("CCode");
			if (attr != null) {
				cname = attr.get_string ("cname");
			}
			if (cname == null) {
				cname = get_default_cname ();
			}
606
		}
607 608
		return cname;
	}
609 610 611 612 613 614

	/**
	 * 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
	 */
615
	public string get_default_cname () {
616 617 618
		return "%s%s".printf (parent_symbol.get_cprefix (), name);
	}

619 620 621 622 623
	/**
	 * Sets the name of this class as it is used in C code.
	 *
	 * @param cname the name to be used in C code
	 */
624
	public void set_cname (string cname) {
625 626
		this.cname = cname;
	}
627

628
	private string get_lower_case_csuffix () {
629
		if (lower_case_csuffix == null) {
630
			lower_case_csuffix = camel_case_to_lower_case (name);
631 632 633

			// remove underscores in some cases to avoid conflicts of type macros
			if (lower_case_csuffix.has_prefix ("type_")) {
634
				lower_case_csuffix = "type" + lower_case_csuffix.offset ("type_".length);
635
			} else if (lower_case_csuffix.has_prefix ("is_")) {
636
				lower_case_csuffix = "is" + lower_case_csuffix.offset ("is_".length);
637 638
			}
			if (lower_case_csuffix.has_suffix ("_class")) {
639
				lower_case_csuffix = lower_case_csuffix.substring (0, lower_case_csuffix.length - "_class".length) + "class";
640
			}
641
		}
642 643
		return lower_case_csuffix;
	}
644

645
	public override string? get_lower_case_cname (string? infix) {
646 647
		if (infix == null) {
			infix = "";
648
		}
649
		return "%s%s%s".printf (parent_symbol.get_lower_case_cprefix (), infix, get_lower_case_csuffix ());
650 651
	}
	
652
	public override string get_lower_case_cprefix () {
653 654 655 656
		if (lower_case_cprefix == null) {
			lower_case_cprefix = "%s_".printf (get_lower_case_cname (null));
		}
		return lower_case_cprefix;
657 658
	}
	
659
	public override string? get_upper_case_cname (string? infix) {
660 661
		return get_lower_case_cname (infix).up ();
	}
662

663 664 665
	public override bool is_reference_type () {
		return true;
	}
666 667 668 669 670 671

	private void process_gir_attribute (Attribute a) {
		if (a.has_argument ("name")) {
			gir_name = a.get_string ("name");
		}
	}
672
	
673
	private void process_ccode_attribute (Attribute a) {
674 675 676
		if (a.has_argument ("ref_function")) {
			set_ref_function (a.get_string ("ref_function"));
		}
677 678 679
		if (a.has_argument ("ref_function_void")) {
			this.ref_function_void = a.get_bool ("ref_function_void");
		}
680 681 682
		if (a.has_argument ("unref_function")) {
			set_unref_function (a.get_string ("unref_function"));
		}
683 684 685
		if (a.has_argument ("ref_sink_function")) {
			set_ref_sink_function (a.get_string ("ref_sink_function"));
		}
686 687 688 689 690 691
		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"));
		}
692 693 694
		if (a.has_argument ("free_function_address_of")) {
			free_function_address_of = a.get_bool ("free_function_address_of");
		}
695 696 697 698 699 700 701 702 703 704 705 706
		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");
		}
707 708 709
		if (a.has_argument ("take_value_function")) {
			take_value_function = a.get_string ("take_value_function");
		}
710

711 712 713 714 715 716
		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");
		}
717 718 719
		if (a.has_argument ("lower_case_csuffix")) {
			lower_case_csuffix = a.get_string ("lower_case_csuffix");
		}
720 721 722 723
		if (a.has_argument ("cheader_filename")) {
			var val = a.get_string ("cheader_filename");
			foreach (string filename in val.split (",")) {
				add_cheader_filename (filename);
724 725
			}
		}
726 727 728
		if (a.has_argument ("type_check_function")) {
			type_check_function = a.get_string ("type_check_function");
		}
729 730 731 732

		if (a.has_argument ("param_spec_function")) {
			param_spec_function = a.get_string ("param_spec_function");
		}
733
	}
734

735 736 737 738 739 740 741
	/**
	 * Process all associated attributes.
	 */
	public void process_attributes () {
		foreach (Attribute a in attributes) {
			if (a.name == "CCode") {
				process_ccode_attribute (a);
742 743
			} else if (a.name == "Compact") {
				is_compact = true;
744 745
			} else if (a.name == "Immutable") {
				is_immutable = true;
746 747
			} else if (a.name == "Deprecated") {
				process_deprecated_attribute (a);
748 749
			} else if (a.name == "GIR") {
				process_gir_attribute (a);
750 751
			}
		}
752
	}
753

754 755 756 757 758 759 760 761
	public string? get_default_type_id () {
		if (is_compact) {
			return "G_TYPE_POINTER";
		}

		return get_upper_case_cname ("TYPE_");
	}

762
	public override string? get_type_id () {
763
		if (type_id == null) {
764
			type_id = get_default_type_id ();
765
		}
766

767 768 769
		return type_id;
	}

770
	public void set_type_id (string type_id) {
771 772 773
		this.type_id = type_id;
	}

774
	public override string? get_marshaller_type_name () {
775
		if (marshaller_type_name == null) {
776 777
			if (base_class != null) {
				marshaller_type_name = base_class.get_marshaller_type_name ();
778 779
			} else if (!is_compact) {
				marshaller_type_name = get_upper_case_cname ();
780
			} else if (get_type_id () == "G_TYPE_POINTER") {
781
				marshaller_type_name = "POINTER";
782 783
			} else {
				marshaller_type_name = "BOXED";
784 785 786 787
			}
		}

		return marshaller_type_name;
788 789
	}

790
	public override string? get_param_spec_function () {
791
		if (param_spec_function == null) {
792
			param_spec_function = get_default_param_spec_function ();
793 794 795 796 797
		}

		return param_spec_function;
	}

798 799 800 801 802 803 804 805 806 807 808 809
	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";
		}
	}

810
	public override string? get_get_value_function () {
811
		if (get_value_function == null) {
812
			if (is_fundamental ()) {
813
				get_value_function = get_lower_case_cname ("value_get_");
814
			} else if (base_class != null) {
815
				get_value_function = base_class.get_get_value_function ();
816
			} else if (get_type_id () == "G_TYPE_POINTER") {
817
				get_value_function = "g_value_get_pointer";
818 819
			} else {
				get_value_function = "g_value_get_boxed";
820 821 822 823
			}
		}

		return get_value_function;
824 825
	}
	
826
	public override string? get_set_value_function () {
827
		if (set_value_function == null) {
828
			if (is_fundamental ()) {
829
				set_value_function = get_lower_case_cname ("value_set_");
830
			} else if (base_class != null) {
831
				set_value_function = base_class.get_set_value_function ();
832
			} else if (get_type_id () == "G_TYPE_POINTER") {
833
				set_value_function = "g_value_set_pointer";
834 835
			} else {
				set_value_function = "g_value_set_boxed";
836 837 838 839
			}
		}

		return set_value_function;
840 841
	}

842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
	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;
	}

858
	public override bool is_reference_counting () {
859
		return get_ref_function () != null;
860
	}
861

862
	public bool is_fundamental () {
863
		if (!is_compact && base_class == null) {
864
			return true;
865 866
		} else if (CodeContext.get ().profile == Profile.DOVA && base_class.base_class == null) {
			return true;
867 868 869 870
		}
		return false;
	}

871
	public override string? get_ref_function () {
872 873 874 875
		if (ref_function == null && is_fundamental ()) {
			ref_function = get_lower_case_cprefix () + "ref";
		}

876 877
		if (ref_function == null && base_class != null) {
			return base_class.get_ref_function ();
878 879 880
		} else {
			return ref_function;
		}
881
	}
882

883
	public void set_ref_function (string? name) {
884 885 886
		this.ref_function = name;
	}

887
	public override string? get_unref_function () {
888 889 890 891
		if (unref_function == null && is_fundamental ()) {
			unref_function = get_lower_case_cprefix () + "unref";
		}

892 893
		if (unref_function == null && base_class != null) {
			return base_class.get_unref_function ();
894 895 896 897 898
		} else {
			return unref_function;
		}
	}

899
	public void set_unref_function (string? name) {
900 901 902
		this.unref_function = name;
	}

903 904 905 906 907 908 909 910 911 912 913 914
	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;
	}

915
	public override string? get_dup_function () {
916 917 918
		return copy_function;
	}

919
	public void set_dup_function (string? name) {
920 921 922 923
		this.copy_function = name;
	}

	public string get_default_free_function () {
924
		return get_lower_case_cprefix () + "free";
925 926
	}

927
	public override string? get_free_function () {
928
		if (free_function == null) {
929 930 931
			if (base_class != null) {
				return base_class.get_free_function ();
			}
932 933 934 935 936
			free_function = get_default_free_function ();
		}
		return free_function;
	}
	
937
	public void set_free_function (string name) {
938
		this.free_function = name;
939
	}
940
	
941
	public override bool is_subtype_of (TypeSymbol t) {
942 943 944 945
		if (this == t) {
			return true;
		}

946
		foreach (DataType base_type in base_types) {
Jürg Billeter's avatar
Jürg Billeter committed
947
			if (base_type.data_type != null && base_type.data_type.is_subtype_of (t)) {
948 949 950 951 952 953
				return true;
			}
		}
		
		return false;
	}
954

955
	public override void replace_type (DataType old_type, DataType new_type) {
956 957 958 959 960 961 962
		for (int i = 0; i < base_types.size; i++) {
			if (base_types[i] == old_type) {
				base_types[i] = new_type;
				return;
			}
		}
	}
963

964
	private void get_all_prerequisites (Interface iface, List<TypeSymbol> list) {
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
		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;
	}

998
	public override bool check (CodeContext context) {
999 1000 1001 1002 1003 1004 1005 1006
		if (checked) {
			return !error;
		}

		checked = true;

		process_attributes ();

1007 1008
		var old_source_file = context.analyzer.current_source_file;
		var old_symbol = context.analyzer.current_symbol;
1009 1010

		if (source_reference != null) {
1011
			context.analyzer.current_source_file = source_reference.file;
1012
		}
1013
		context.analyzer.current_symbol = this;
1014 1015

		foreach (DataType base_type_reference in get_base_types ()) {
1016
			if (!base_type_reference.check (context)) {
1017 1018 1019 1020
				error = true;
				return false;
			}

1021 1022 1023 1024 1025 1026
			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;
			}

1027
			// check whether base type is at least as accessible as the class
1028
			if (!context.analyzer.is_type_accessible (this, base_type_reference)) {
1029 1030 1031 1032
				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;
			}
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044

			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;
			}
1045 1046
		}

1047
		foreach (DataType type in base_types) {
1048
			type.check (context);
1049 1050
		}

1051
		foreach (TypeParameter p in get_type_parameters ()) {
1052
			p.check (context);
1053 1054 1055 1056
		}

		/* process enums first to avoid order problems in C code */
		foreach (Enum en in enums) {
1057
			en.check (context);
1058 1059 1060
		}

		foreach (Field f in fields) {
1061
			f.check (context);
1062 1063 1064
		}
		
		foreach (Constant c in constants) {
1065
			c.check (context);
1066 1067 1068
		}
		
		foreach (Method m in methods) {
1069
			m.check (context);
1070 1071 1072
		}
		
		foreach (Property prop in properties) {
1073
			prop.check (context);
1074 1075 1076
		}
		
		foreach (Signal sig in signals) {
1077
			sig.check (context);
1078 1079 1080
		}
		
		if (constructor != null) {
1081
			constructor.check (context);
1082 1083 1084
		}

		if (class_constructor != null) {
1085
			class_constructor.check (context);
1086 1087 1088
		}

		if (static_constructor != null) {
1089
			static_constructor.check (context);
1090 1091 1092
		}

		if (destructor != null) {
1093
			destructor.check (context);
1094
		}
1095 1096

		if (static_destructor != null) {
1097
			static_destructor.check (context);
1098
		}
1099
		
1100
		if (class_destructor != null) {
1101
			class_destructor.check (context);
1102 1103
		}
		
1104
		foreach (Class cl in classes) {
1105
			cl.check (context);
1106 1107 1108
		}
		
		foreach (Struct st in structs) {
1109
			st.check (context);
1110 1111 1112
		}

		foreach (Delegate d in delegates) {
1113
			d.check (context);
1114
		}
1115 1116 1117 1118 1119 1120 1121 1122 1123

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

1125
			if (!external && !external_package && base_class != null) {
1126 1127 1128 1129
				foreach (Field f in fields) {
					if (f.binding == MemberBinding.INSTANCE) {
						error = true;
						Report.error (source_reference, "derived compact classes may not have instance fields");
1130
						break;
1131 1132 1133
					}
				}
			}
1134 1135 1136
		}

		/* gather all prerequisites */
1137
		List<TypeSymbol> prerequisites = new ArrayList<TypeSymbol> ();
1138 1139 1140 1141 1142 1143
		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 */
1144
		List<string> missing_prereqs = new ArrayList<string> ();
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168
		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 */
1169
		if (source_type == SourceFileType.SOURCE) {
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192
			/* 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;
							}
1193 1194
							if (sym is Method) {
								// method is used as interface implementation, so it is not unused
1195
								sym.check_deprecated (source_reference);
1196 1197
								sym.used = true;
							} else {
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
								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;
							}
1213 1214
							if (sym is Property) {
								// property is used as interface implementation, so it is not unused
1215
								sym.check_deprecated (source_reference);
1216 1217
								sym.used = true;
							} else {
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
								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) {
1232
							var override_method = context.analyzer.symbol_lookup_inherited (this, base_method.name) as Method;
1233 1234 1235 1236 1237 1238 1239 1240
							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) {
1241
							var override_property = context.analyzer.symbol_lookup_inherited (this, base_property.name) as Property;
1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
							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;
				}
			}
		}

1253 1254
		context.analyzer.current_source_file = old_source_file;
		context.analyzer.current_symbol = old_symbol;
1255 1256 1257

		return !error;
	}
1258
}
1259

1260
// vim:sw=8 noet