valaclass.vala 17.9 KB
Newer Older
1 2
/* valaclass.vala
 *
3
 * Copyright (C) 2006-2008  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.
 */
Jürg Billeter's avatar
Jürg Billeter committed
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

	/**
	 * Specifies whether this class is static. Static classes may not be
	 * instantiated and may only contain static members.
	 */
	public bool is_static { get; set; }

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
	/**
	 * 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;
		}
	}

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	/**
	 * 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;
		}
	}

79 80 81 82 83 84 85
	/**
	 * 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; }

86 87 88
	/**
	 * Specifies whether this class has private fields.
	 */
89
	public bool has_private_fields { get; private set; }
90

91
	private string cname;
92 93
	private string const_cname;
	private string lower_case_cprefix;
94
	private string lower_case_csuffix;
95
	private string type_id;
96 97 98 99 100 101 102
	private string ref_function;
	private string unref_function;
	private string copy_function;
	private string free_function;
	private string marshaller_type_name;
	private string get_value_function;
	private string set_value_function;
103
	private string? type_signature;
104
	private bool _is_compact;
105
	private bool _is_immutable;
106

107
	private Gee.List<TypeParameter> type_parameters = new ArrayList<TypeParameter> ();
108

109
	private Gee.List<DataType> base_types = new ArrayList<DataType> ();
110

111 112 113 114 115
	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> ();
116 117

	// inner types
118 119
	private Gee.List<Class> classes = new ArrayList<Class> ();
	private Gee.List<Struct> structs = new ArrayList<Struct> ();
120
	private Gee.List<Enum> enums = new ArrayList<Enum> ();
Jürg Billeter's avatar
Jürg Billeter committed
121 122
	private Gee.List<Delegate> delegates = new ArrayList<Delegate> ();

123 124 125 126 127
	/**
	 * Specifies the default construction method.
	 */
	public Method default_construction_method { get; set; }
	
128 129 130 131
	/**
	 * Specifies the instance constructor.
	 */
	public Constructor constructor { get; set; }
132

133 134 135 136 137
	/**
	 * Specifies the class constructor.
	 */
	public Constructor class_constructor { get; set; }

138 139 140 141 142
	/**
	 * Specifies the static class constructor.
	 */
	public Constructor static_constructor { get; set; }

143 144 145
	/**
	 * Specifies the instance destructor.
	 */
146 147 148 149 150 151 152 153
	public Destructor? destructor {
		get { return _destructor; }
		set {
			_destructor = value;
			if (_destructor != null) {
				if (_destructor.this_parameter != null) {
					_destructor.scope.remove (_destructor.this_parameter.name);
				}
154
				_destructor.this_parameter = new FormalParameter ("this", new ObjectType (this));
155 156 157 158
				_destructor.scope.add (_destructor.this_parameter.name, _destructor.this_parameter);
			}
		}
	}
159 160 161 162 163 164

	/**
	 * Specifies whether this class denotes an error base.
	 */
	public bool is_error_base { get; set ; }

165 166
	Destructor? _destructor;

167 168 169 170 171 172 173
	/**
	 * Creates a new class.
	 *
	 * @param name   type name
	 * @param source reference to source code
	 * @return       newly created class
	 */
174
	public Class (string name, SourceReference? source_reference = null) {
175 176
		this.source_reference = source_reference;
		this.name = name;
177
	}
178

179 180 181 182 183 184
	/**
	 * Adds the specified class or interface to the list of base types of
	 * this class.
	 *
	 * @param type a class or interface reference
	 */
185
	public void add_base_type (DataType type) {
186
		base_types.add (type);
187
		type.parent_node = this;
188 189 190 191 192 193 194
	}

	/**
	 * Returns a copy of the base type list.
	 *
	 * @return list of base types
	 */
195 196
	public Gee.List<DataType> get_base_types () {
		return new ReadOnlyList<DataType> (base_types);
197 198 199 200 201 202 203
	}

	/**
	 * Appends the specified parameter to the list of type parameters.
	 *
	 * @param p a type parameter
	 */
204
	public void add_type_parameter (TypeParameter p) {
205
		type_parameters.add (p);
206
		p.type = this;
207
		scope.add (p.name, p);
208
	}
209 210 211 212 213 214

	/**
	 * Returns a copy of the type parameter list.
	 *
	 * @return list of type parameters
	 */
215 216
	public Gee.List<TypeParameter> get_type_parameters () {
		return new ReadOnlyList<TypeParameter> (type_parameters);
217 218
	}

219 220 221 222 223
	/**
	 * Adds the specified constant as a member to this class.
	 *
	 * @param c a constant
	 */
224
	public void add_constant (Constant c) {
225
		constants.add (c);
226
		scope.add (c.name, c);
227 228 229 230 231 232 233
	}
	
	/**
	 * Adds the specified field as a member to this class.
	 *
	 * @param f a field
	 */
234
	public void add_field (Field f) {
235
		fields.add (f);
236
		if (f.access == SymbolAccessibility.PRIVATE && f.binding == MemberBinding.INSTANCE) {
237
			has_private_fields = true;
238
		}
239
		scope.add (f.name, f);
240 241 242 243 244 245 246
	}
	
	/**
	 * Returns a copy of the list of fields.
	 *
	 * @return list of fields
	 */
247 248
	public Gee.List<Field> get_fields () {
		return new ReadOnlyList<Field> (fields);
249 250 251 252 253 254 255
	}
	
	/**
	 * Adds the specified method as a member to this class.
	 *
	 * @param m a method
	 */
256
	public void add_method (Method m) {
257
		if (m.binding == MemberBinding.INSTANCE || m is CreationMethod) {
258 259 260
			if (m.this_parameter != null) {
				m.scope.remove (m.this_parameter.name);
			}
261
			m.this_parameter = new FormalParameter ("this", new ObjectType (this));
262 263
			m.scope.add (m.this_parameter.name, m.this_parameter);
		}
264
		if (!(m.return_type is VoidType) && m.get_postconditions ().size > 0) {
265 266 267
			if (m.result_var != null) {
				m.scope.remove (m.result_var.name);
			}
268
			m.result_var = new LocalVariable (m.return_type.copy (), "result");
269 270
			m.scope.add (m.result_var.name, m.result_var);
		}
271 272 273 274 275 276 277
		if (m is CreationMethod) {
			if (m.name == null) {
				default_construction_method = m;
				m.name = ".new";
			} else {
				m.name = ".new." + m.name;
			}
278 279 280 281 282 283 284 285

			var cm = (CreationMethod) m;
			if (cm.type_name != null && cm.type_name != name) {
				// type_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.type_name));
				m.error = true;
				return;
			}
286 287
		}

288
		methods.add (m);
289
		scope.add (m.name, m);
290 291 292 293 294 295 296
	}
	
	/**
	 * Returns a copy of the list of methods.
	 *
	 * @return list of methods
	 */
297 298
	public override Gee.List<Method> get_methods () {
		return new ReadOnlyList<Method> (methods);
299 300 301 302 303 304 305
	}
	
	/**
	 * Adds the specified property as a member to this class.
	 *
	 * @param prop a property
	 */
306
	public void add_property (Property prop) {
307
		properties.add (prop);
308 309
		scope.add (prop.name, prop);

310
		prop.this_parameter = new FormalParameter ("this", new ObjectType (this));
311
		prop.scope.add (prop.this_parameter.name, prop.this_parameter);
312

313 314
		if (prop.field != null) {
			add_field (prop.field);
315
		}
316 317 318 319 320 321 322
	}
	
	/**
	 * Returns a copy of the list of properties.
	 *
	 * @return list of properties
	 */
323 324
	public override Gee.List<Property> get_properties () {
		return new ReadOnlyList<Property> (properties);
325 326 327 328 329 330 331
	}
	
	/**
	 * Adds the specified signal as a member to this class.
	 *
	 * @param sig a signal
	 */
332
	public void add_signal (Signal sig) {
333
		signals.add (sig);
334
		scope.add (sig.name, sig);
335 336 337 338 339 340 341
	}
	
	/**
	 * Returns a copy of the list of signals.
	 *
	 * @return list of signals
	 */
342 343
	public override Gee.List<Signal> get_signals () {
		return new ReadOnlyList<Signal> (signals);
344
	}
345

346 347 348 349 350
	/**
	 * Adds the specified class as an inner class.
	 *
	 * @param cl a class
	 */
351
	public void add_class (Class cl) {
352
		classes.add (cl);
353 354 355 356 357 358 359 360
		scope.add (cl.name, cl);
	}

	/**
	 * Adds the specified struct as an inner struct.
	 *
	 * @param st a struct
	 */
361
	public void add_struct (Struct st) {
362
		structs.add (st);
363 364 365
		scope.add (st.name, st);
	}

366 367 368 369 370 371 372 373 374 375
	/**
	 * 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
376 377 378 379 380
	/**
	 * Adds the specified delegate as an inner delegate.
	 *
	 * @param d a delegate
	 */
381
	public void add_delegate (Delegate d) {
Jürg Billeter's avatar
Jürg Billeter committed
382 383 384 385
		delegates.add (d);
		scope.add (d.name, d);
	}

386
	public override void accept (CodeVisitor visitor) {
387 388 389
		visitor.visit_class (this);
	}

390
	public override void accept_children (CodeVisitor visitor) {
391
		foreach (DataType type in base_types) {
392
			type.accept (visitor);
393
		}
394 395 396

		foreach (TypeParameter p in type_parameters) {
			p.accept (visitor);
397
		}
398 399 400 401 402 403

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

404 405
		foreach (Field f in fields) {
			f.accept (visitor);
406 407
		}
		
408 409
		foreach (Constant c in constants) {
			c.accept (visitor);
410 411
		}
		
412 413
		foreach (Method m in methods) {
			m.accept (visitor);
414 415
		}
		
416 417
		foreach (Property prop in properties) {
			prop.accept (visitor);
418 419
		}
		
420 421
		foreach (Signal sig in signals) {
			sig.accept (visitor);
422 423
		}
		
424 425
		if (constructor != null) {
			constructor.accept (visitor);
426 427
		}

428 429 430 431
		if (class_constructor != null) {
			class_constructor.accept (visitor);
		}

432 433 434 435
		if (static_constructor != null) {
			static_constructor.accept (visitor);
		}

436 437
		if (destructor != null) {
			destructor.accept (visitor);
438
		}
439 440 441 442 443 444 445 446
		
		foreach (Class cl in classes) {
			cl.accept (visitor);
		}
		
		foreach (Struct st in structs) {
			st.accept (visitor);
		}
447

Jürg Billeter's avatar
Jürg Billeter committed
448 449 450
		foreach (Delegate d in delegates) {
			d.accept (visitor);
		}
451 452
	}

453
	public override string get_cprefix () {
454
		return get_cname ();
455
	}
456

457
	public override string get_cname (bool const_type = false) {
458 459 460 461 462 463
		if (const_type) {
			if (const_cname != null) {
				return const_cname;
			} else if (is_immutable) {
				return "const " + get_cname (false);
			}
464 465
		}

466
		if (cname == null) {
467
			cname = get_default_cname ();
468
		}
469 470
		return cname;
	}
471 472 473 474 475 476

	/**
	 * 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
	 */
477
	public string get_default_cname () {
478 479 480
		return "%s%s".printf (parent_symbol.get_cprefix (), name);
	}

481 482 483 484 485
	/**
	 * Sets the name of this class as it is used in C code.
	 *
	 * @param cname the name to be used in C code
	 */
486
	public void set_cname (string cname) {
487 488 489
		this.cname = cname;
	}
	
490
	private string get_lower_case_csuffix () {
491
		if (lower_case_csuffix == null) {
492
			lower_case_csuffix = camel_case_to_lower_case (name);
493 494 495 496 497 498 499 500 501 502

			// 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";
			}
503
		}
504 505
		return lower_case_csuffix;
	}
506

507
	public override string? get_lower_case_cname (string? infix) {
508 509
		if (infix == null) {
			infix = "";
510
		}
511
		return "%s%s%s".printf (parent_symbol.get_lower_case_cprefix (), infix, get_lower_case_csuffix ());
512 513
	}
	
514
	public override string get_lower_case_cprefix () {
515 516 517 518
		if (lower_case_cprefix == null) {
			lower_case_cprefix = "%s_".printf (get_lower_case_cname (null));
		}
		return lower_case_cprefix;
519 520
	}
	
521
	public override string? get_upper_case_cname (string? infix) {
522 523
		return get_lower_case_cname (infix).up ();
	}
524

525 526 527 528
	public override string? get_type_signature () {
		return type_signature;
	}

529 530 531 532
	public override bool is_reference_type () {
		return true;
	}
	
533
	private void process_ccode_attribute (Attribute a) {
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
		if (a.has_argument ("ref_function")) {
			set_ref_function (a.get_string ("ref_function"));
		}
		if (a.has_argument ("unref_function")) {
			set_unref_function (a.get_string ("unref_function"));
		}
		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"));
		}
		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");
		}

559 560 561
		if (a.has_argument ("cname")) {
			set_cname (a.get_string ("cname"));
		}
562 563 564 565 566 567
		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");
		}
568 569 570
		if (a.has_argument ("lower_case_csuffix")) {
			lower_case_csuffix = a.get_string ("lower_case_csuffix");
		}
571 572 573 574
		if (a.has_argument ("cheader_filename")) {
			var val = a.get_string ("cheader_filename");
			foreach (string filename in val.split (",")) {
				add_cheader_filename (filename);
575 576
			}
		}
577 578 579
		if (a.has_argument ("type_signature")) {
			type_signature = a.get_string ("type_signature");
		}
580 581 582
		if (a.has_argument ("type_check_function")) {
			type_check_function = a.get_string ("type_check_function");
		}
583 584 585 586 587 588 589 590 591
	}
	
	/**
	 * Process all associated attributes.
	 */
	public void process_attributes () {
		foreach (Attribute a in attributes) {
			if (a.name == "CCode") {
				process_ccode_attribute (a);
592 593
			} else if (a.name == "ErrorBase") {
				is_error_base = true;
594 595
			} else if (a.name == "Compact") {
				is_compact = true;
596 597
			} else if (a.name == "Immutable") {
				is_immutable = true;
598 599
			}
		}
600
	}
601

602
	public override string? get_type_id () {
603
		if (type_id == null) {
604
			if (!is_compact) {
605 606 607 608
				type_id = get_upper_case_cname ("TYPE_");
			} else {
				type_id = "G_TYPE_POINTER";
			}
609 610 611 612 613
		}
		
		return type_id;
	}

614
	public void set_type_id (string type_id) {
615 616 617
		this.type_id = type_id;
	}

618
	public override string? get_marshaller_type_name () {
619
		if (marshaller_type_name == null) {
620 621
			if (base_class != null) {
				marshaller_type_name = base_class.get_marshaller_type_name ();
622 623 624 625 626 627
			} else {
				marshaller_type_name = "POINTER";
			}
		}

		return marshaller_type_name;
628 629
	}

630
	public override string? get_get_value_function () {
631
		if (get_value_function == null) {
632 633
			if (base_class != null) {
				get_value_function = base_class.get_get_value_function ();
634 635 636 637 638 639
			} else {
				get_value_function = "g_value_get_pointer";
			}
		}

		return get_value_function;
640 641
	}
	
642
	public override string? get_set_value_function () {
643
		if (set_value_function == null) {
644 645
			if (base_class != null) {
				set_value_function = base_class.get_set_value_function ();
646 647 648 649 650 651
			} else {
				set_value_function = "g_value_set_pointer";
			}
		}

		return set_value_function;
652 653
	}

654
	public override bool is_reference_counting () {
655
		return get_ref_function () != null;
656
	}
657 658

	bool is_fundamental () {
659
		if (!is_compact && base_class == null) {
660 661 662 663 664
			return true;
		}
		return false;
	}

665
	public override string? get_ref_function () {
666 667 668 669
		if (ref_function == null && is_fundamental ()) {
			ref_function = get_lower_case_cprefix () + "ref";
		}

670 671
		if (ref_function == null && base_class != null) {
			return base_class.get_ref_function ();
672 673 674
		} else {
			return ref_function;
		}
675
	}
676

677
	public void set_ref_function (string? name) {
678 679 680
		this.ref_function = name;
	}

681
	public override string? get_unref_function () {
682 683 684 685
		if (unref_function == null && is_fundamental ()) {
			unref_function = get_lower_case_cprefix () + "unref";
		}

686 687
		if (unref_function == null && base_class != null) {
			return base_class.get_unref_function ();
688 689 690 691 692
		} else {
			return unref_function;
		}
	}

693
	public void set_unref_function (string? name) {
694 695 696
		this.unref_function = name;
	}

697
	public override string? get_dup_function () {
698 699 700
		return copy_function;
	}

701
	public void set_dup_function (string? name) {
702 703 704 705
		this.copy_function = name;
	}

	public string get_default_free_function () {
706
		return get_lower_case_cprefix () + "free";
707 708
	}

709
	public override string? get_free_function () {
710 711 712 713 714 715
		if (free_function == null) {
			free_function = get_default_free_function ();
		}
		return free_function;
	}
	
716
	public void set_free_function (string name) {
717
		this.free_function = name;
718
	}
719
	
Jürg Billeter's avatar
Jürg Billeter committed
720
	public override bool is_subtype_of (TypeSymbol t) {
721 722 723 724
		if (this == t) {
			return true;
		}

725
		foreach (DataType base_type in base_types) {
Jürg Billeter's avatar
Jürg Billeter committed
726
			if (base_type.data_type != null && base_type.data_type.is_subtype_of (t)) {
727 728 729 730 731 732
				return true;
			}
		}
		
		return false;
	}
733

734
	public override int get_type_parameter_index (string name) {
735 736 737 738 739 740 741 742 743
		int i = 0;
		foreach (TypeParameter parameter in type_parameters) {
			if (parameter.name == name) {
				return i;
			}
			i++;
		}
		return -1;
	}
744

745
	public override void replace_type (DataType old_type, DataType new_type) {
746 747 748 749 750 751 752
		for (int i = 0; i < base_types.size; i++) {
			if (base_types[i] == old_type) {
				base_types[i] = new_type;
				return;
			}
		}
	}
753 754 755 756

	public override CodeBinding? create_code_binding (CodeGenerator codegen) {
		return codegen.create_class_binding (this);
	}
757
}
758