valagirwriter.vala 42.3 KB
Newer Older
1
/* valagirwriter.vala
2
 *
3
 * Copyright (C) 2008-2012  Jürg Billeter
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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
 * version 2.1 of the License, or (at your option) any later version.

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

/**
26
 * Code visitor generating .gir file for the public interface.
27
 */
28
public class Vala.GIRWriter : CodeVisitor {
29
	private CodeContext context;
30 31 32
	private string directory;
	private string gir_namespace;
	private string gir_version;
33
	private string gir_shared_library;
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	protected virtual string? get_interface_comment (Interface iface) {
		return null;
	}

	protected virtual string? get_struct_comment (Struct st) {
		return null;
	}

	protected virtual string? get_enum_comment (Enum en) {
		return null;
	}

	protected virtual string? get_class_comment (Class c) {
		return null;
	}

	protected virtual string? get_error_code_comment (ErrorCode ecode) {
		return null;
	}

	protected virtual string? get_enum_value_comment (EnumValue ev) {
		return null;
	}

	protected virtual string? get_constant_comment (Constant c) {
		return null;
	}

	protected virtual string? get_error_domain_comment (ErrorDomain edomain) {
		return null;
	}

	protected virtual string? get_field_comment (Field f) {
		return null;
	}

	protected virtual string? get_delegate_comment (Delegate cb) {
		return null;
	}

	protected virtual string? get_method_comment (Method m) {
		return null;
	}

	protected virtual string? get_property_comment (Property prop) {
		return null;
	}

	protected virtual string? get_delegate_return_comment (Delegate cb) {
		return null;
	}

	protected virtual string? get_signal_return_comment (Signal sig) {
		return null;
	}

	protected virtual string? get_method_return_comment (Method m) {
		return null;
	}

	protected virtual string? get_signal_comment (Signal sig) {
		return null;
	}

	protected virtual string? get_parameter_comment (Parameter param) {
		return null;
	}

103
	StringBuilder buffer = new StringBuilder();
104
	FileStream stream;
105 106
	Vala.HashSet<Namespace> unannotated_namespaces = new Vala.HashSet<Namespace>();
	Vala.HashSet<Namespace> our_namespaces = new Vala.HashSet<Namespace>();
107 108
	Vala.ArrayList<Vala.Symbol> hierarchy = new Vala.ArrayList<Vala.Symbol>();
	Vala.ArrayList<Vala.CodeNode> deferred = new Vala.ArrayList<Vala.CodeNode>();
109

110 111
	int indent;

112
	private TypeSymbol gobject_type;
113
	private TypeSymbol ginitiallyunowned_type;
114

115 116 117
	private struct GIRNamespace {
		public GIRNamespace (string ns, string version) {
			this.ns = ns; this.version = version;
Rob Taylor's avatar
Rob Taylor committed
118
		}
119 120 121 122
		public string ns;
		public string version;
		public bool equal (GIRNamespace g) {
			return ((ns == g.ns) && (version == g.version));
Rob Taylor's avatar
Rob Taylor committed
123
		}
124
	}
Rob Taylor's avatar
Rob Taylor committed
125

126
	private ArrayList<GIRNamespace?> externals = new ArrayList<GIRNamespace?> ((EqualFunc<GIRNamespace>) GIRNamespace.equal);
Rob Taylor's avatar
Rob Taylor committed
127

128 129
	public void write_includes() {
		foreach (var i in externals) {
130 131 132 133
			if (i.ns != this.gir_namespace) {
				write_indent_stream ();
				stream.printf ("<include name=\"%s\" version=\"%s\"/>\n", i.ns, i.version);
			}
Rob Taylor's avatar
Rob Taylor committed
134 135 136
		}
	}

137

138 139 140 141
	/**
	 * Writes the public interface of the specified code context into the
	 * specified file.
	 *
142 143
	 * @param context      a code context
	 * @param gir_filename a relative or absolute filename
144
	 */
145
	public void write_file (CodeContext context, string directory, string gir_filename, string gir_namespace, string gir_version, string package, string? gir_shared_library = null) {
146
		this.context = context;
147 148 149
		this.directory = directory;
		this.gir_namespace = gir_namespace;
		this.gir_version = gir_version;
150
		this.gir_shared_library = gir_shared_library;
151 152 153

		var root_symbol = context.root;
		var glib_ns = root_symbol.scope.lookup ("GLib");
154
		gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
155
		ginitiallyunowned_type = (TypeSymbol) glib_ns.scope.lookup ("InitiallyUnowned");
156

157 158 159 160 161 162 163
		write_package (package);

		context.accept (this);

		indent--;
		buffer.append_printf ("</repository>\n");

164
		string filename = "%s%c%s".printf (directory, Path.DIR_SEPARATOR, gir_filename);
165
		stream = FileStream.open (filename, "w");
166 167
		if (stream == null) {
			Report.error (null, "unable to open `%s' for writing".printf (filename));
168
			this.context = null;
169 170
			return;
		}
171 172 173

		stream.printf ("<?xml version=\"1.0\"?>\n");

174
		stream.printf ("<repository version=\"1.2\"");
175 176 177 178
		stream.printf (" xmlns=\"http://www.gtk.org/introspection/core/1.0\"");
		stream.printf (" xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"");
		stream.printf (" xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
		stream.printf (">\n");
179
		indent++;
180

181
		write_includes();
182
		indent--;
183

184
		stream.puts (buffer.str);
185 186
		stream = null;

187 188 189 190 191 192 193 194 195
		foreach (var ns in unannotated_namespaces) {
			if (!our_namespaces.contains(ns)) {
				Report.warning (ns.source_reference, "Namespace %s does not have a GIR namespace and version annotation".printf (ns.name));
			}
		}
		foreach (var ns in our_namespaces) {
			ns.source_reference.file.gir_namespace = gir_namespace;
			ns.source_reference.file.gir_version = gir_version;
		}
196 197 198 199

		if (our_namespaces.size == 0) {
			Report.error (null, "No suitable namespace found to export for GIR");
		}
200 201

		this.context = null;
202 203
	}

204 205 206 207 208 209 210 211 212
	private void write_doc (string? comment) {
		if (comment != null) {
			write_indent ();
			buffer.append ("<doc xml:whitespace=\"preserve\">");
			buffer.append (comment);
			buffer.append ("</doc>\n");
		}
	}

213 214
	private void write_package (string package) {
		write_indent ();
215
		buffer.append_printf ("<package name=\"%s\"/>\n", package);
216 217
	}

218 219
	private void write_c_includes (Namespace ns) {
		// Collect C header filenames
220
		Set<string> header_filenames = new HashSet<string> (str_hash, str_equal);
221
		foreach (unowned string c_header_filename in get_ccode_header_filenames (ns).split (",")) {
222 223 224
			header_filenames.add (c_header_filename);
		}
		foreach (Symbol symbol in ns.scope.get_symbol_table ().get_values ()) {
225
			foreach (unowned string c_header_filename in get_ccode_header_filenames (symbol).split (",")) {
226 227 228 229 230 231 232 233 234 235 236 237
				header_filenames.add (c_header_filename);
			}
		}

		// Generate c:include tags
		foreach (string c_header_filename in header_filenames) {
			write_c_include (c_header_filename);
		}
	}

	private void write_c_include (string name) {
		write_indent ();
238
		buffer.append_printf ("<c:include name=\"%s\"/>\n", name);
239 240
	}

241
	public override void visit_namespace (Namespace ns) {
242
		if (ns.external_package) {
243 244 245
			return;
		}

246 247 248 249
		if (!is_visibility (ns)) {
			return;
		}

250 251
		if (ns.name == null)  {
			// global namespace
252
			hierarchy.insert (0, ns);
253
			ns.accept_children (this);
254
			hierarchy.remove_at (0);
255 256 257
			return;
		}

258
		if (ns.parent_symbol.name != null) {
259
			ns.accept_children (this);
260 261 262
			return;
		}

263 264
		write_c_includes (ns);

265
		write_indent ();
266
		buffer.append_printf ("<namespace name=\"%s\" version=\"%s\"", gir_namespace, gir_version);
267
		string? cprefix = get_ccode_prefix (ns);
268 269 270
		if (gir_shared_library != null) {
			buffer.append_printf(" shared-library=\"%s\"", gir_shared_library);
		}
271
		if (cprefix != null) {
272
			buffer.append_printf (" c:prefix=\"%s\"", cprefix);
273
		}
274
		buffer.append_printf (">\n");
275 276
		indent++;

277
		hierarchy.insert (0, ns);
278
		ns.accept_children (this);
279
		hierarchy.remove_at (0);
280 281 282

		indent--;
		write_indent ();
283 284
		buffer.append_printf ("</namespace>\n");
		our_namespaces.add(ns);
285 286

		visit_deferred ();
287 288
	}

289
	private void write_symbol_attributes (Symbol symbol) {
290 291 292
		if (!is_introspectable (symbol)) {
			buffer.append_printf (" introspectable=\"0\"");
		}
293
		if (symbol.version.deprecated) {
294
			buffer.append_printf (" deprecated=\"1\"");
295 296
			if (symbol.version.deprecated_since != null) {
				buffer.append_printf (" deprecated-version=\"%s\"", symbol.version.deprecated_since);
297 298
			}
		}
299 300 301
		if (symbol.version.since != null) {
			buffer.append_printf (" version=\"%s\"", symbol.version.since);
		}
302 303
	}

304
	public override void visit_class (Class cl) {
305
		if (cl.external_package) {
306 307 308 309 310 311 312
			return;
		}

		if (!check_accessibility (cl)) {
			return;
		}

313 314 315 316 317
		if (!(hierarchy[0] is Namespace)) {
			deferred.add (cl);
			return;
		}

318
		if (cl.is_subtype_of (gobject_type)) {
319
			string gtype_struct_name = get_gir_name (cl) + "Class";
320

321
			write_indent ();
322
			buffer.append_printf ("<class name=\"%s\"", get_gir_name (cl));
323
			write_gtype_attributes (cl);
324 325
			buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
			buffer.append_printf (" parent=\"%s\"", gi_type_name (cl.base_class));
326
			if (cl.is_abstract) {
327
				buffer.append_printf (" abstract=\"1\"");
328
			}
329
			write_symbol_attributes (cl);
330
			buffer.append_printf (">\n");
331 332
			indent++;

333 334
			write_doc (get_class_comment (cl));

335 336
			// write implemented interfaces
			foreach (DataType base_type in cl.get_base_types ()) {
337 338
				var object_type = (ObjectType) base_type;
				if (object_type.type_symbol is Interface) {
339
					write_indent ();
340
					buffer.append_printf ("<implements name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
341 342 343
				}
			}

344
			write_indent ();
345
			buffer.append_printf ("<field name=\"parent_instance\">\n");
346 347
			indent++;
			write_indent ();
348
			buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
349 350
			indent--;
			write_indent ();
351
			buffer.append_printf("</field>\n");
352 353

			write_indent ();
354
			buffer.append_printf ("<field name=\"priv\">\n");
355 356
			indent++;
			write_indent ();
357
			buffer.append_printf ("<type name=\"%sPrivate\" c:type=\"%sPrivate*\"/>\n", get_gir_name (cl), get_ccode_name (cl));
358 359
			indent--;
			write_indent ();
360
			buffer.append_printf("</field>\n");
361

362
			hierarchy.insert (0, cl);
363
			cl.accept_children (this);
364
			hierarchy.remove_at (0);
365 366 367

			indent--;
			write_indent ();
368
			buffer.append_printf ("</class>\n");
369 370

			write_indent ();
371
			buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
372
			write_ctype_attributes (cl, "Class");
373 374
			buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", cl.name);
			buffer.append_printf (">\n");
375 376
			indent++;

377
			write_indent ();
378
			buffer.append_printf ("<field name=\"parent_class\">\n");
379 380
			indent++;
			write_indent ();
381
			buffer.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
382 383
			indent--;
			write_indent ();
384
			buffer.append_printf ("</field>\n");
385

386 387
			foreach (Method m in cl.get_methods ()) {
				if (m.is_abstract || m.is_virtual) {
388 389 390 391 392 393 394 395 396 397
					if (m.coroutine) {
						string finish_name = m.name;
						if (finish_name.has_suffix ("_async")) {
							finish_name = finish_name.substring (0, finish_name.length - "_async".length);
						}
						finish_name += "_finish";

						write_indent ();
						buffer.append_printf("<field name=\"%s\">\n", m.name);
						indent++;
398
						do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false);
399 400 401 402 403 404 405
						indent--;
						write_indent ();
						buffer.append_printf ("</field>\n");

						write_indent ();
						buffer.append_printf("<field name=\"%s\">\n", finish_name);
						indent++;
406
						do_write_signature (m, "callback", true, finish_name, get_ccode_finish_name (m), m.get_async_end_parameters (), m.return_type, m.tree_can_fail, false);
407 408 409 410 411 412 413
						indent--;
						write_indent ();
						buffer.append_printf ("</field>\n");
					} else {
						write_indent ();
						buffer.append_printf("<field name=\"%s\">\n", m.name);
						indent++;
414
						do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
415 416 417 418
						indent--;
						write_indent ();
						buffer.append_printf ("</field>\n");
					}
419 420 421
				}
			}

422 423 424
			foreach (Signal sig in cl.get_signals ()) {
				if (sig.default_handler != null) {
					write_indent ();
425
					buffer.append_printf ("<field name=\"%s\">\n", get_ccode_lower_case_name (sig));
426
					indent++;
427
					write_signature (sig.default_handler, "callback", false, true);
428 429
					indent--;
					write_indent ();
430
					buffer.append_printf ("</field>\n");
431 432 433
				}
			}

434 435
			indent--;
			write_indent ();
436
			buffer.append_printf ("</record>\n");
437 438

			write_indent ();
439
			buffer.append_printf ("<record name=\"%sPrivate\" c:type=\"%sPrivate\" disguised=\"1\"/>\n", get_gir_name (cl), get_ccode_name (cl));
440 441
		} else {
			write_indent ();
442
			buffer.append_printf ("<record name=\"%s\"", get_gir_name (cl));
443
			write_symbol_attributes (cl);
444
			buffer.append_printf (">\n");
445 446
			indent++;

447 448
			write_doc (get_class_comment (cl));

449
			hierarchy.insert (0, cl);
450
			cl.accept_children (this);
451
			hierarchy.remove_at (0);
452 453 454

			indent--;
			write_indent ();
455
			buffer.append_printf ("</record>\n");
456
		}
457 458

		visit_deferred ();
459 460
	}

461
	public override void visit_struct (Struct st) {
462
		if (st.external_package) {
463 464 465 466 467 468 469
			return;
		}

		if (!check_accessibility (st)) {
			return;
		}

470 471 472 473 474
		if (!(hierarchy[0] is Namespace)) {
			deferred.add (st);
			return;
		}

475
		write_indent ();
476
		buffer.append_printf ("<record name=\"%s\"", get_gir_name (st));
477 478 479 480 481
		if (get_ccode_has_type_id (st)) {
			write_gtype_attributes (st);
		} else {
			write_ctype_attributes (st);
		}
482
		write_symbol_attributes (st);
483
		buffer.append_printf (">\n");
484 485
		indent++;

486 487
		write_doc (get_struct_comment (st));

488
		hierarchy.insert (0, st);
489
		st.accept_children (this);
490
		hierarchy.remove_at (0);
491 492 493

		indent--;
		write_indent ();
494
		buffer.append_printf ("</record>\n");
495 496

		visit_deferred ();
497 498
	}

499
	public override void visit_interface (Interface iface) {
500
		if (iface.external_package) {
501 502 503 504 505 506 507
			return;
		}

		if (!check_accessibility (iface)) {
			return;
		}

508 509 510 511 512
		if (!(hierarchy[0] is Namespace)) {
			deferred.add (iface);
			return;
		}

513 514
		string gtype_struct_name = iface.name + "Iface";

515
		write_indent ();
516
		buffer.append_printf ("<interface name=\"%s\"", get_gir_name (iface));
517
		write_gtype_attributes (iface);
518
		buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
519
		write_symbol_attributes (iface);
520
		buffer.append_printf (">\n");
521 522
		indent++;

523 524
		write_doc (get_interface_comment (iface));

525 526 527
		// write prerequisites
		if (iface.get_prerequisites ().size > 0) {
			foreach (DataType base_type in iface.get_prerequisites ()) {
528 529
				write_indent ();
				buffer.append_printf ("<prerequisite name=\"%s\"/>\n", gi_type_name (((ObjectType) base_type).type_symbol));
530 531 532
			}
		}

533
		hierarchy.insert (0, iface);
534
		iface.accept_children (this);
535
		hierarchy.remove_at (0);
536 537 538

		indent--;
		write_indent ();
539
		buffer.append_printf ("</interface>\n");
540 541

		write_indent ();
542
		buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
543
		write_ctype_attributes (iface, "Iface");
544 545
		buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", iface.name);
		buffer.append_printf (">\n");
546 547
		indent++;

548 549 550 551 552 553 554 555 556
		write_indent ();
		buffer.append_printf ("<field name=\"parent_iface\">\n");
		indent++;
		write_indent ();
		buffer.append_printf ("<type name=\"GObject.TypeInterface\" c:type=\"GTypeInterface\"/>\n");
		indent--;
		write_indent ();
		buffer.append_printf ("</field>\n");

557 558
		foreach (Method m in iface.get_methods ()) {
			if (m.is_abstract || m.is_virtual) {
559 560 561 562 563 564 565 566 567 568
				if (m.coroutine) {
					string finish_name = m.name;
					if (finish_name.has_suffix ("_async")) {
						finish_name = finish_name.substring (0, finish_name.length - "_async".length);
					}
					finish_name += "_finish";

					write_indent ();
					buffer.append_printf("<field name=\"%s\">\n", m.name);
					indent++;
569
					do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false);
570 571 572 573 574 575 576
					indent--;
					write_indent ();
					buffer.append_printf ("</field>\n");

					write_indent ();
					buffer.append_printf("<field name=\"%s\">\n", finish_name);
					indent++;
577
					do_write_signature (m, "callback", true, finish_name, get_ccode_finish_name (m), m.get_async_end_parameters (), m.return_type, m.tree_can_fail, false);
578 579 580 581 582 583 584
					indent--;
					write_indent ();
					buffer.append_printf ("</field>\n");
				} else {
					write_indent ();
					buffer.append_printf("<field name=\"%s\">\n", m.name);
					indent++;
585
					do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
586 587 588 589
					indent--;
					write_indent ();
					buffer.append_printf ("</field>\n");
				}
590 591 592
			}
		}

593 594 595 596 597 598 599
		foreach (var prop in iface.get_properties ()) {
			if (prop.is_abstract || prop.is_virtual) {
				if (prop.get_accessor != null) {
					var m = prop.get_accessor.get_method ();
					write_indent ();
					buffer.append_printf("<field name=\"%s\">\n", m.name);
					indent++;
600
					do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
601 602 603 604 605
					indent--;
					write_indent ();
					buffer.append_printf ("</field>\n");
				}

606
				if (prop.set_accessor != null && prop.set_accessor.writable) {
607 608 609 610
					var m = prop.set_accessor.get_method ();
					write_indent ();
					buffer.append_printf("<field name=\"%s\">\n", m.name);
					indent++;
611
					do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
612 613 614 615 616 617 618
					indent--;
					write_indent ();
					buffer.append_printf ("</field>\n");
				}
			}
		}

619 620
		indent--;
		write_indent ();
621
		buffer.append_printf ("</record>\n");
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

		visit_deferred ();
	}

	private void visit_deferred () {
		var nodes = this.deferred;
		this.deferred = new Vala.ArrayList<Vala.CodeNode>();

		foreach (var node in nodes) {
			node.accept (this);
		}
	}

	private string? get_gir_name (Symbol symbol) {
		string? gir_name = null;
		var h0 = hierarchy[0];

		for (Symbol? cur_sym = symbol ; cur_sym != null ; cur_sym = cur_sym.parent_symbol) {
			if (cur_sym == h0) {
				break;
			}

644 645 646 647 648
			var cur_name = cur_sym.get_attribute_string ("GIR", "name");
			if (cur_name == null) {
				cur_name = cur_sym.name;
			}
			gir_name = cur_name.concat (gir_name);
649 650 651
		}

		return gir_name;
652 653
	}

654
	public override void visit_enum (Enum en) {
655
		if (en.external_package) {
656 657 658 659 660 661 662
			return;
		}

		if (!check_accessibility (en)) {
			return;
		}

663 664 665 666 667
		if (!(hierarchy[0] is Namespace)) {
			deferred.add (en);
			return;
		}

668 669
		string element_name = (en.is_flags) ? "bitfield" : "enumeration";

670
		write_indent ();
671
		buffer.append_printf ("<%s name=\"%s\"", element_name, get_gir_name (en));
672 673 674 675 676
		if (get_ccode_has_type_id (en)) {
			write_gtype_attributes (en);
		} else {
			write_ctype_attributes (en);
		}
677
		write_symbol_attributes (en);
678
		buffer.append_printf (">\n");
679 680
		indent++;

681 682
		write_doc (get_enum_comment (en));

683
		enum_value = 0;
684
		hierarchy.insert (0, en);
685
		en.accept_children (this);
686
		hierarchy.remove_at (0);
687 688 689

		indent--;
		write_indent ();
690
		buffer.append_printf ("</%s>\n", element_name);
691 692

		visit_deferred ();
693 694
	}

695 696
	private int enum_value;

697
	public override void visit_enum_value (EnumValue ev) {
698
		write_indent ();
699
		var en = (Enum) hierarchy[0];
700
		buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ev.name.down (), get_ccode_name (ev));
701 702
		if (ev.value != null) {
			string value = literal_expression_to_value_string (ev.value);
703
			buffer.append_printf (" value=\"%s\"", value);
704
		} else {
705 706 707 708 709
			if (en.is_flags) {
				buffer.append_printf (" value=\"%d\"", 1 << enum_value++);
			} else {
				buffer.append_printf (" value=\"%d\"", enum_value++);
			}
710
		}
711
		write_symbol_attributes (ev);
712 713 714 715 716 717 718 719 720 721 722 723 724 725

		string? comment = get_enum_value_comment (ev);
		if (comment == null) {
			buffer.append_printf ("/>\n");
		} else {
			buffer.append_printf (">\n");
			indent++;

			write_doc (comment);

			indent--;
			write_indent ();
			buffer.append_printf ("</member>\n");
		}
726 727 728
	}

	public override void visit_error_domain (ErrorDomain edomain) {
729
		if (edomain.external_package) {
730 731 732 733 734 735 736
			return;
		}

		if (!check_accessibility (edomain)) {
			return;
		}

737
		write_indent ();
738
		buffer.append_printf ("<enumeration name=\"%s\"", edomain.name);
739
		write_ctype_attributes (edomain);
740
		buffer.append_printf (" glib:error-domain=\"%s\"", get_ccode_quark_name (edomain));
741
		write_symbol_attributes (edomain);
742
		buffer.append_printf (">\n");
743 744
		indent++;

745 746
		write_doc (get_error_domain_comment (edomain));

747
		enum_value = 0;
748
		hierarchy.insert (0, edomain);
749
		edomain.accept_children (this);
750
		hierarchy.remove_at (0);
751 752 753

		indent--;
		write_indent ();
754
		buffer.append_printf ("</enumeration>\n");
755 756

		visit_deferred ();
757 758 759 760
	}

	public override void visit_error_code (ErrorCode ecode) {
		write_indent ();
761
		buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ecode.name.down (), get_ccode_name (ecode));
762 763
		if (ecode.value != null) {
			string value = literal_expression_to_value_string (ecode.value);
764
			buffer.append_printf (" value=\"%s\"", value);
765
		} else {
766
			buffer.append_printf (" value=\"%d\"", enum_value++);
767
		}
768
		write_symbol_attributes (ecode);
769 770 771 772 773 774 775 776 777 778 779 780 781 782

		string? comment = get_error_code_comment (ecode);
		if (comment == null) {
			buffer.append_printf ("/>\n");
		} else {
			buffer.append_printf (">\n");
			indent++;

			write_doc (comment);

			indent--;
			write_indent ();
			buffer.append_printf ("</member>\n");
		}
783 784
	}

785
	public override void visit_constant (Constant c) {
786
		if (c.external_package) {
787 788 789 790 791 792 793
			return;
		}

		if (!check_accessibility (c)) {
			return;
		}

794
		//TODO Add better constant evaluation
795
		var initializer = c.value;
796 797 798
		string value = literal_expression_to_value_string (initializer);

		write_indent ();
799
		buffer.append_printf ("<constant name=\"%s\" c:identifier=\"%s\"", c.name, get_ccode_name (c));
800
		buffer.append_printf (" value=\"%s\"", value);
801
		write_symbol_attributes (c);
802
		buffer.append_printf (">\n");
803 804
		indent++;

805 806
		write_doc (get_constant_comment (c));

807 808 809
		write_type (initializer.value_type);

		indent--;
810
		write_indent ();
811
		buffer.append_printf ("</constant>\n");
812 813
	}

814
	public override void visit_field (Field f) {
815
		if (f.external_package) {
816 817 818 819 820 821 822 823
			return;
		}

		if (!check_accessibility (f)) {
			return;
		}

		write_indent ();
824
		buffer.append_printf ("<field name=\"%s\"", get_ccode_name (f));
Jürg Billeter's avatar
Jürg Billeter committed
825
		if (f.variable_type.nullable) {
826
			buffer.append_printf (" allow-none=\"1\"");
827
		}
828
		write_symbol_attributes (f);
829
		buffer.append_printf (">\n");
830
		indent++;
831

832 833
		write_doc (get_field_comment (f));

Jürg Billeter's avatar
Jürg Billeter committed
834
		write_type (f.variable_type);
835 836 837

		indent--;
		write_indent ();
838
		buffer.append_printf ("</field>\n");
839 840
	}

841
	private void write_implicit_params (DataType? type, ref int index, bool has_array_length, string? name, ParameterDirection direction) {
842
		if (type is ArrayType && has_array_length) {
843
			for (var i = 0; i < ((ArrayType) type).rank; i++) {
844
				write_param_or_return (((ArrayType) type).length_type, true, ref index, has_array_length, "%s_length%i".printf (name, i + 1), null, direction);
845
			}
846
		} else if (type is DelegateType) {
847
			var deleg_type = (DelegateType) type;
848 849 850 851
			if (deleg_type.delegate_symbol.has_target) {
				var data_type = new PointerType (new VoidType ());
				write_param_or_return (data_type, true, ref index, false, "%s_target".printf (name), null, direction);
				if (deleg_type.is_disposable ()) {
852
					var notify_type = new DelegateType (context.root.scope.lookup ("GLib").scope.lookup ("DestroyNotify") as Delegate);
853 854
					write_param_or_return (notify_type, true, ref index, false, "%s_target_destroy_notify".printf (name), null, direction);
				}
855
			}
856
		}
857
	}
858

859
	void skip_implicit_params (DataType? type, ref int index, bool has_array_length) {
860
		if (type is ArrayType && has_array_length) {
861
			index += ((ArrayType) type).rank;
862 863
		} else if (type is DelegateType) {
			index++;
864 865
			var deleg_type = (DelegateType) type;
			if (deleg_type.is_disposable ()) {
866 867 868 869 870
				index++;
			}
		}
	}

871
	private void write_params_and_return (List<Parameter> params, DataType? return_type, bool return_array_length, string? return_comment = null, bool constructor = false, DataType? instance_type = null, bool user_data = false) {
872
		int last_index = 0;
873
		bool ret_is_struct = return_type != null && return_type.is_real_non_null_struct_type ();
874

875 876 877 878 879 880 881 882 883 884
		if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
			int index = 0;

			if (instance_type != null) {
				index++;
			}

			foreach (Parameter param in params) {
				index++;

885
				skip_implicit_params (param.variable_type, ref index, get_ccode_array_length (param));
886 887 888 889 890 891
			}

			if (ret_is_struct) {
				index++;
			} else {
				skip_implicit_params (return_type, ref index, return_array_length);
892 893 894
				if (return_type is ArrayType && return_array_length) {
					index -= ((ArrayType) return_type).rank - 1;
				}
895 896 897 898 899
			}

			last_index = index - 1;
		}

900 901 902 903 904 905
		if (return_type != null && !ret_is_struct) {
			write_param_or_return (return_type, false, ref last_index, return_array_length, null, return_comment, ParameterDirection.IN, constructor);
		} else if (ret_is_struct) {
			write_param_or_return (new VoidType (), false, ref last_index, false, null, return_comment, ParameterDirection.IN);
		}

906
		if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
907
			write_indent ();
908
			buffer.append_printf ("<parameters>\n");
909
			indent++;
910
			int index = 0;
911

912
			if (instance_type != null) {
913
				write_param_or_return (instance_type, true, ref index, false, "self");
914
			}
915

916
			foreach (Parameter param in params) {
917
				write_param_or_return (param.variable_type, true, ref index, get_ccode_array_length (param), param.name, get_parameter_comment (param), param.direction, false, false, param.ellipsis);
918

919
				write_implicit_params (param.variable_type, ref index, get_ccode_array_length (param), param.name, param.direction);
920
			}
921

922 923
			if (ret_is_struct) {
				// struct returns are converted to parameters
924
				write_param_or_return (return_type, true, ref index, false, "result", return_comment, ParameterDirection.OUT, constructor, true);
925 926
			} else {
				write_implicit_params (return_type, ref index, return_array_length, "result", ParameterDirection.OUT);
927 928
			}

929 930
			if (user_data) {
				write_indent ();
931
				buffer.append_printf ("<parameter name=\"user_data\" transfer-ownership=\"none\" closure=\"%d\">\n", index);
932 933
				indent++;
				write_indent ();
934
				buffer.append_printf ("<type name=\"gpointer\" c:type=\"void*\"/>\n");
935 936
				indent--;
				write_indent ();
937
				buffer.append_printf ("</parameter>\n");
938 939
			}

940
			indent--;
941
			write_indent ();
942
			buffer.append_printf ("</parameters>\n");
943 944 945
		}
	}

946
	public override void visit_delegate (Delegate cb) {
947
		if (cb.external_package) {
948 949 950 951 952 953 954 955
			return;
		}

		if (!check_accessibility (cb)) {
			return;
		}

		write_indent ();
956
		buffer.append_printf ("<callback name=\"%s\"", cb.name);
957
		buffer.append_printf (" c:type=\"%s\"", get_ccode_name (cb));
958
		if (cb.tree_can_fail) {
959
			buffer.append_printf (" throws=\"1\"");
960
		}
961
		write_symbol_attributes (cb);
962
		buffer.append_printf (">\n");
963 964
		indent++;

965 966
		write_doc (get_delegate_comment (cb));

967
		write_params_and_return (cb.get_parameters (), cb.return_type, get_ccode_array_length (cb), get_delegate_return_comment (cb), false, null, cb.has_target);
968 969 970

		indent--;
		write_indent ();
971
		buffer.append_printf ("</callback>\n");
972 973
	}

974
	public override void visit_method (Method m) {
975
		if (m.external_package) {
976 977 978 979 980 981 982 983
			return;
		}

		// don't write interface implementation unless it's an abstract or virtual method
		if (!check_accessibility (m) || m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
			return;
		}

984
		string tag_name = "method";
985 986 987 988 989 990 991
		var parent = this.hierarchy.get (0);
		if (parent is Enum) {
			deferred.add (m);
			return;
		}

		if (parent is Namespace || m.binding == MemberBinding.STATIC || parent != m.parent_symbol) {
992 993 994
			tag_name = "function";
		}

995
		write_signature (m, tag_name, true);
996 997

		if (m.is_abstract || m.is_virtual) {
998
			write_signature (m, "virtual-method", true, false);
999
		}
1000 1001
	}

1002
	bool is_type_introspectable (DataType type) {
1003
		// gobject-introspection does not currently support va_list parameters
1004
		if (get_ccode_name (type) == "va_list") {
1005 1006 1007 1008 1009 1010
			return false;
		}

		return true;
	}

1011
	bool is_method_introspectable (Method m) {
1012
		if (!is_type_introspectable (m.return_type)) {
1013 1014 1015
			return false;
		}
		foreach (var param in m.get_parameters ()) {
1016
			if (param.ellipsis || !is_type_introspectable (param.variable_type)) {
1017 1018 1019 1020 1021 1022
				return false;
			}
		}
		return true;
	}

1023 1024 1025 1026 1027 1028 1029 1030
	bool is_introspectable (Symbol sym) {
		if (sym is Method && !is_method_introspectable ((Method) sym)) {
			return false;
		}

		return is_visibility (sym);
	}

1031
	private void write_signature (Method m, string tag_name, bool write_doc, bool instance = false) {
1032 1033 1034 1035
		var parent = this.hierarchy.get (0);
		string name;
		if (m.parent_symbol != parent) {
			instance = false;
1036 1037
			name = get_ccode_name (m);
			var parent_prefix = get_ccode_lower_case_prefix (parent);
1038
			if (name.has_prefix (parent_prefix)) {
1039
				name = name.substring (parent_prefix.length);
1040 1041 1042 1043 1044
			}
		} else {
			name = m.name;
		}

1045
		if (m.coroutine) {
1046
			string finish_name = name;
1047 1048 1049 1050
			if (finish_name.has_suffix ("_async")) {
				finish_name = finish_name.substring (0, finish_name.length - "_async".length);
			}
			finish_name += "_finish";
1051 1052
			do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, true);
			do_write_signature (m, tag_name, instance, finish_name, get_ccode_finish_name (m), m.get_async_end_parameters (), m.return_type, m.tree_can_fail, false);
1053
		} else {
1054
			do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, true);
1055 1056 1057
		}
	}

1058
	private void do_write_signature (Method m, string tag_name, bool instance, string name, string cname, List<Vala.Parameter> params, DataType return_type, bool can_fail, bool write_comment) {
1059
		write_indent ();
1060
		buffer.append_printf ("<%s name=\"%s\"", tag_name, name);
1061
		if (tag_name == "virtual-method") {
1062
			buffer.append_printf (" invoker=\"%s\"", name);
1063
		} else if (tag_name == "callback") {
1064
			/* this is only used for vfuncs */
1065
			buffer.append_printf (" c:type=\"%s\"", name);
1066
		} else {
1067
			buffer.append_printf (" c:identifier=\"%s\"", cname);
1068
		}
1069
		if (can_fail) {
1070
			buffer.append_printf (" throws=\"1\"");
1071
		}
1072
		write_symbol_attributes (m);
1073
		buffer.append_printf (">\n");
1074 1075
		indent++;

1076 1077 1078 1079 1080 1081
		string? return_comment = null;
		if (write_comment) {
			return_comment = get_method_return_comment (m);
			write_doc (get_method_comment (m));
		}

1082
		DataType instance_type = null;
1083
		if (instance) {
1084
			instance_type = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
1085 1086
		}

1087
		write_params_and_return (params, return_type, get_ccode_array_length (m), return_comment, false, instance_type);
1088 1089 1090

		indent--;
		write_indent ();
1091
		buffer.append_printf ("</%s>\n", tag_name);
1092
	}
1093

1094
	public override void visit_creation_method (CreationMethod m) {
1095
		if (m.external_package) {
1096 1097 1098 1099 1100 1101 1102
			return;
		}

		if (!check_accessibility (m)) {
			return;
		}

1103 1104 1105 1106
		if (m.parent_symbol is Class && ((Class) m.parent_symbol).is_abstract) {
			return;
		}

1107
		write_indent ();
1108

1109 1110 1111 1112
		bool is_struct = m.parent_symbol is Struct;
		// GI doesn't like constructors that return void type
		string tag_name = is_struct ? "function" : "constructor";

1113 1114
		if (m.parent_symbol is Class && m == ((Class)m.parent_symbol).default_construction_method ||
			m.parent_symbol is Struct && m == ((Struct)m.parent_symbol).default_construction_method) {
1115
			string m_name = is_struct ? "init" : "new";
1116
			buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m_name, get_ccode_name (m));
1117
		} else {
1118
			buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m.name, get_ccode_name (m));
1119 1120
		}

1121
		if (m.tree_can_fail) {
1122
			buffer.append_printf (" throws=\"1\"");
1123
		}
1124
		write_symbol_attributes (m);
1125
		buffer.append_printf (">\n");
1126 1127
		indent++;

1128 1129
		write_doc (get_method_comment (m));

1130
		var datatype = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
1131
		write_params_and_return (m.get_parameters (), datatype, false, get_method_return_comment (m), true);
1132 1133 1134

		indent--;
		write_indent ();
1135
		buffer.append_printf ("</%s>\n", tag_name);
1136 1137
	}

1138
	public override void visit_property (Property prop) {
1139
		if (!check_accessibility (prop) || prop.overrides || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
1140 1141 1142 1143
			return;
		}

		write_indent ();
1144
		buffer.append_printf ("<property name=\"%s\"", get_ccode_name (prop));
1145
		if (prop.get_accessor == null) {
1146
			buffer.append_printf (" readable=\"0\"");
1147 1148
		}
		if (prop.set_accessor != null) {
1149
			buffer.append_printf (" writable=\"1\"");
1150 1151
			if (prop.set_accessor.construction) {
				if (!prop.set_accessor.writable) {
1152
					buffer.append_printf (" construct-only=\"1\"");
1153
				} else {
1154
					buffer.append_printf (" construct=\"1\"");
1155 1156
				}
			}
1157
		}
1158
		write_symbol_attributes (prop);
1159
		buffer.append_printf (">\n");
1160 1161
		indent++;

1162 1163
		write_doc (get_property_comment (prop));