3 * Copyright (C) 2008-2010 Jürg Billeter
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Jürg Billeter <j@bitron.ch>
26 * Code visitor parsing all Vala source files.
28 public class Vala
.GirParser
: CodeVisitor
{
34 SourceFile current_source_file
;
37 MarkupTokenType current_token
;
39 string[] cheader_filenames
;
40 string[] package_names
;
42 HashMap
<string,string> attributes_map
= new HashMap
<string,string> (str_hash
, str_equal
);
44 HashMap
<string,ArrayList
<Method
>> gtype_callbacks
= new HashMap
<string,ArrayList
<Method
>> (str_hash
, str_equal
);
47 * Parses all .gir source files in the specified code
48 * context and builds a code tree.
50 * @param context a code context
52 public void parse (CodeContext context
) {
53 this
.context
= context
;
54 glib_ns
= context
.root
.scope
.lookup ("GLib") as Namespace
;
55 context
.accept (this
);
58 public override void visit_source_file (SourceFile source_file
) {
59 if (source_file
.filename
.has_suffix (".gir")) {
60 parse_file (source_file
);
64 public void parse_file (SourceFile source_file
) {
65 this
.current_source_file
= source_file
;
66 reader
= new
MarkupReader (source_file
.filename
);
75 var remove_queue
= new ArrayList
<CodeNode
> ();
77 foreach (CodeNode node
in source_file
.get_nodes ()) {
79 var cl
= (Class
) node
;
80 var ns
= cl
.parent_symbol as Namespace
;
81 // remove Class records
82 var class_struct
= ns
.scope
.lookup (cl
.name
+ "Class") as Struct
;
83 if (class_struct
!= null) {
84 ns
.remove_struct ((Struct
) class_struct
);
85 remove_queue
.add (class_struct
);
87 } else if (node is Interface
) {
88 var iface
= (Interface
) node
;
89 var ns
= iface
.parent_symbol as Namespace
;
90 // remove Iface records
91 var iface_struct
= ns
.scope
.lookup (iface
.name
+ "Iface") as Struct
;
92 if (iface_struct
!= null) {
93 ns
.remove_struct ((Struct
) iface_struct
);
94 remove_queue
.add (iface_struct
);
99 foreach (CodeNode node
in remove_queue
) {
100 source_file
.remove_node (node
);
104 this
.current_source_file
= null;
108 current_token
= reader
.read_token (out begin
, out end
);
110 // Skip *all* <doc> tags
111 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "doc")
115 void start_element (string name
) {
116 if (current_token
!= MarkupTokenType
.START_ELEMENT
|| reader
.name
!= name
) {
118 Report
.error (get_current_src (), "expected start element of `%s'".printf (name
));
122 void end_element (string name
) {
123 if (current_token
!= MarkupTokenType
.END_ELEMENT
|| reader
.name
!= name
) {
125 Report
.error (get_current_src (), "expected end element of `%s'".printf (name
));
130 SourceReference
get_current_src () {
131 return new
SourceReference (this
.current_source_file
, begin
.line
, begin
.column
, end
.line
, end
.column
);
134 const string GIR_VERSION
= "1.2";
136 void parse_repository () {
137 start_element ("repository");
138 if (reader
.get_attribute ("version") != GIR_VERSION
) {
139 Report
.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader
.get_attribute ("version"), GIR_VERSION
));
143 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
144 if (reader
.name
== "namespace") {
145 var ns
= parse_namespace ();
147 context
.root
.add_namespace (ns
);
149 } else if (reader
.name
== "include") {
151 } else if (reader
.name
== "package") {
153 } else if (reader
.name
== "c:include") {
157 Report
.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader
.name
));
161 end_element ("repository");
164 void parse_include () {
165 start_element ("include");
167 end_element ("include");
170 void parse_package () {
171 start_element ("package");
172 add_package_name (reader
.get_attribute ("name"));
174 end_element ("package");
177 void parse_c_include () {
178 start_element ("c:include");
179 cheader_filenames
+= reader
.get_attribute ("name");
181 end_element ("c:include");
184 void skip_element () {
189 if (current_token
== MarkupTokenType
.START_ELEMENT
) {
191 } else if (current_token
== MarkupTokenType
.END_ELEMENT
) {
193 } else if (current_token
== MarkupTokenType
.EOF
) {
194 Report
.error (get_current_src (), "unexpected end of file");
201 Namespace?
parse_namespace () {
202 start_element ("namespace");
204 bool new_namespace
= false;
205 string namespace_name
= transform_namespace_name (reader
.get_attribute ("name"));
206 var ns
= context
.root
.scope
.lookup (namespace_name
) as Namespace
;
208 ns
= new
Namespace (namespace_name
, get_current_src ());
209 new_namespace
= true;
211 if (ns
.external_package
) {
212 ns
.attributes
= null;
213 ns
.source_reference
= get_current_src ();
217 string? cprefix
= reader
.get_attribute ("c:identifier-prefixes");
218 if (cprefix
!= null) {
219 ns
.add_cprefix (cprefix
);
220 ns
.set_lower_case_cprefix (Symbol
.camel_case_to_lower_case (cprefix
) + "_");
223 foreach (string c_header
in cheader_filenames
) {
224 ns
.add_cheader_filename (c_header
);
227 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
228 if (reader
.get_attribute ("introspectable") == "0") {
234 if (reader
.name
== "alias") {
235 sym
= parse_alias ();
236 } else if (reader
.name
== "enumeration") {
237 if (reader
.get_attribute ("glib:error-quark") != null) {
238 sym
= parse_error_domain ();
240 sym
= parse_enumeration ();
242 } else if (reader
.name
== "bitfield") {
243 sym
= parse_bitfield ();
244 } else if (reader
.name
== "function") {
245 sym
= parse_method ("function");
246 } else if (reader
.name
== "callback") {
247 sym
= parse_callback ();
248 } else if (reader
.name
== "record") {
249 if (reader
.get_attribute ("glib:get-type") != null) {
250 sym
= parse_boxed ();
252 sym
= parse_record ();
254 } else if (reader
.name
== "class") {
255 sym
= parse_class ();
256 } else if (reader
.name
== "interface") {
257 sym
= parse_interface ();
258 } else if (reader
.name
== "glib:boxed") {
259 sym
= parse_boxed ();
260 } else if (reader
.name
== "union") {
261 sym
= parse_union ();
262 } else if (reader
.name
== "constant") {
263 sym
= parse_constant ();
266 Report
.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader
.name
));
271 ns
.add_class ((Class
) sym
);
272 } else if (sym is Interface
) {
273 ns
.add_interface ((Interface
) sym
);
274 } else if (sym is Struct
) {
275 ns
.add_struct ((Struct
) sym
);
276 } else if (sym is Enum
) {
277 ns
.add_enum ((Enum
) sym
);
278 } else if (sym is ErrorDomain
) {
279 ns
.add_error_domain ((ErrorDomain
) sym
);
280 } else if (sym is Delegate
) {
281 ns
.add_delegate ((Delegate
) sym
);
282 } else if (sym is Method
) {
283 ns
.add_method ((Method
) sym
);
284 } else if (sym is Constant
) {
285 ns
.add_constant ((Constant
) sym
);
286 } else if (sym
== null) {
290 end_element ("namespace");
292 postprocess_gtype_callbacks (ns
);
294 if (!new_namespace
) {
301 Struct
parse_alias () {
302 start_element ("alias");
303 var st
= new
Struct (reader
.get_attribute ("name"), get_current_src ());
304 st
.access
= SymbolAccessibility
.PUBLIC
;
308 st
.base_type
= parse_type (null, null, true);
310 end_element ("alias");
314 private void calculate_common_prefix (ref string common_prefix
, string cname
) {
315 if (common_prefix
== null) {
316 common_prefix
= cname
;
317 while (common_prefix
.length
> 0 && !common_prefix
.has_suffix ("_")) {
318 // FIXME: could easily be made faster
319 common_prefix
= common_prefix
.ndup (common_prefix
.length
- 1);
322 while (!cname
.has_prefix (common_prefix
)) {
323 common_prefix
= common_prefix
.ndup (common_prefix
.length
- 1);
326 while (common_prefix
.length
> 0 && (!common_prefix
.has_suffix ("_") ||
327 (cname
.offset (common_prefix
.length
).get_char ().isdigit ()) && (cname
.length
- common_prefix
.length
) <= 1)) {
328 // enum values may not consist solely of digits
329 common_prefix
= common_prefix
.ndup (common_prefix
.length
- 1);
333 Enum
parse_enumeration () {
334 start_element ("enumeration");
335 var en
= new
Enum (reader
.get_attribute ("name"), get_current_src ());
336 en
.access
= SymbolAccessibility
.PUBLIC
;
338 string enum_cname
= reader
.get_attribute ("c:type");
339 if (enum_cname
!= null) {
340 en
.set_cname (enum_cname
);
345 string common_prefix
= null;
347 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
348 if (reader
.get_attribute ("introspectable") == "0") {
353 if (reader
.name
== "member") {
354 var ev
= parse_enumeration_member ();
356 calculate_common_prefix (ref common_prefix
, ev
.get_cname ());
363 en
.set_cprefix (common_prefix
);
365 end_element ("enumeration");
369 ErrorDomain
parse_error_domain () {
370 start_element ("enumeration");
372 var ed
= new
ErrorDomain (reader
.get_attribute ("name"), get_current_src ());
373 ed
.access
= SymbolAccessibility
.PUBLIC
;
375 string enum_cname
= reader
.get_attribute ("c:type");
376 if (enum_cname
!= null) {
377 ed
.set_cname (enum_cname
);
382 string common_prefix
= null;
384 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
385 if (reader
.get_attribute ("introspectable") == "0") {
390 if (reader
.name
== "member") {
391 ErrorCode ec
= parse_error_member ();
393 calculate_common_prefix (ref common_prefix
, ec
.get_cname ());
400 ed
.set_cprefix (common_prefix
);
402 end_element ("enumeration");
406 Enum
parse_bitfield () {
407 start_element ("bitfield");
408 var en
= new
Enum (reader
.get_attribute ("name"), get_current_src ());
409 en
.access
= SymbolAccessibility
.PUBLIC
;
411 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
412 if (reader
.get_attribute ("introspectable") == "0") {
417 if (reader
.name
== "member") {
418 en
.add_value (parse_enumeration_member ());
424 end_element ("bitfield");
428 EnumValue
parse_enumeration_member () {
429 start_element ("member");
430 var ev
= new
EnumValue (string.joinv ("_", reader
.get_attribute ("name").up ().split ("-")), null);
431 ev
.set_cname (reader
.get_attribute ("c:identifier"));
433 end_element ("member");
437 ErrorCode
parse_error_member () {
438 start_element ("member");
441 string name
= string.joinv ("_", reader
.get_attribute ("name").up ().split ("-"));
442 string value
= reader
.get_attribute ("value");
444 ec
= new ErrorCode
.with_value (name
, new
IntegerLiteral (value
));
446 ec
= new
ErrorCode (name
);
450 end_element ("member");
454 DataType
parse_return_value (out string? ctype
= null) {
455 start_element ("return-value");
456 string transfer
= reader
.get_attribute ("transfer-ownership");
457 string allow_none
= reader
.get_attribute ("allow-none");
459 var transfer_elements
= transfer
== "full";
460 var type
= &ctype
!= null ?
parse_type(out ctype
, null, transfer_elements
) : parse_type (null, null, transfer_elements
);
461 if (transfer
== "full" || transfer
== "container") {
462 type
.value_owned
= true;
464 if (allow_none
== "1") {
465 type
.nullable
= true;
467 end_element ("return-value");
471 FormalParameter
parse_parameter (out int array_length_idx
= null, out int closure_idx
= null, out int destroy_idx
= null, out string? scope
= null) {
472 FormalParameter param
;
474 if (&array_length_idx
!= null) {
475 array_length_idx
= -1;
477 if (&closure_idx
!= null) {
480 if (&destroy_idx
!= null) {
484 start_element ("parameter");
485 string name
= reader
.get_attribute ("name");
486 string direction
= reader
.get_attribute ("direction");
487 string transfer
= reader
.get_attribute ("transfer-ownership");
488 string allow_none
= reader
.get_attribute ("allow-none");
490 if (&scope
!= null) {
491 scope
= reader
.get_attribute ("scope");
494 string closure
= reader
.get_attribute ("closure");
495 string destroy
= reader
.get_attribute ("destroy");
496 if (closure
!= null && &closure_idx
!= null) {
497 closure_idx
= closure
.to_int ();
499 if (destroy
!= null && &destroy_idx
!= null) {
500 destroy_idx
= destroy
.to_int ();
504 if (reader
.name
== "varargs") {
505 start_element ("varargs");
507 param
= new FormalParameter
.with_ellipsis (get_current_src ());
508 end_element ("varargs");
510 var type
= parse_type (null, out array_length_idx
, transfer
== "full");
511 if (transfer
== "full" || transfer
== "container" || destroy
!= null) {
512 type
.value_owned
= true;
514 if (allow_none
== "1") {
515 type
.nullable
= true;
517 param
= new
FormalParameter (name
, type
, get_current_src ());
518 if (direction
== "out") {
519 param
.direction
= ParameterDirection
.OUT
;
520 } else if (direction
== "inout") {
521 param
.direction
= ParameterDirection
.REF
;
524 end_element ("parameter");
528 DataType
parse_type (out string? ctype
= null, out int array_length_index
= null, bool transfer_elements
= false) {
529 bool is_array
= false;
530 string type_name
= reader
.get_attribute ("name");
532 if (reader
.name
== "array") {
534 start_element ("array");
536 if (!(type_name
== "GLib.Array" || type_name
== "GLib.PtrArray")) {
537 if (reader
.get_attribute ("length") != null
538 && &array_length_index
!= null) {
539 array_length_index
= reader
.get_attribute ("length").to_int ();
542 var element_type
= parse_type ();
543 end_element ("array");
544 return new
ArrayType (element_type
, 1, null);
546 } else if (reader
.name
== "callback"){
547 var callback = parse_callback ();
548 return new
DelegateType (callback);
550 start_element ("type");
553 if (&ctype
!= null) {
554 ctype
= reader
.get_attribute("c:type");
559 if (type_name
== "GLib.PtrArray"
560 && current_token
== MarkupTokenType
.START_ELEMENT
) {
561 type_name
= "GLib.GenericArray";
564 DataType type
= parse_type_from_name (type_name
);
566 // type arguments / element types
567 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
568 var element_type
= parse_type ();
569 element_type
.value_owned
= transfer_elements
;
570 type
.add_type_argument (element_type
);
573 end_element (is_array ?
"array" : "type");
577 DataType
parse_type_from_name (string type_name
) {
579 if (type_name
== "none") {
580 type
= new
VoidType ();
581 } else if (type_name
== "gpointer") {
582 type
= new
PointerType (new
VoidType ());
583 } else if (type_name
== "GObject.Strv") {
584 type
= new
ArrayType (new UnresolvedType
.from_symbol (new
UnresolvedSymbol (null, "string")), 1, null);
586 if (type_name
== "utf8") {
587 type_name
= "string";
588 } else if (type_name
== "gboolean") {
590 } else if (type_name
== "gchar") {
592 } else if (type_name
== "gshort") {
594 } else if (type_name
== "gushort") {
595 type_name
= "ushort";
596 } else if (type_name
== "gint") {
598 } else if (type_name
== "guint") {
600 } else if (type_name
== "glong") {
602 } else if (type_name
== "gulong") {
604 } else if (type_name
== "gint8") {
606 } else if (type_name
== "guint8") {
608 } else if (type_name
== "gint16") {
610 } else if (type_name
== "guint16") {
611 type_name
= "uint16";
612 } else if (type_name
== "gint32") {
614 } else if (type_name
== "guint32") {
615 type_name
= "uint32";
616 } else if (type_name
== "gint64") {
618 } else if (type_name
== "guint64") {
619 type_name
= "uint64";
620 } else if (type_name
== "gfloat") {
622 } else if (type_name
== "gdouble") {
623 type_name
= "double";
624 } else if (type_name
== "filename") {
625 type_name
= "string";
626 } else if (type_name
== "GLib.offset") {
628 } else if (type_name
== "gsize") {
629 type_name
= "size_t";
630 } else if (type_name
== "gssize") {
631 type_name
= "ssize_t";
632 } else if (type_name
== "GType") {
633 type_name
= "GLib.Type";
634 } else if (type_name
== "GLib.String") {
635 type_name
= "GLib.StringBuilder";
636 } else if (type_name
== "GObject.Class") {
637 type_name
= "GLib.ObjectClass";
638 } else if (type_name
== "GLib.unichar") {
639 type_name
= "unichar";
640 } else if (type_name
== "GLib.Data") {
641 type_name
= "GLib.Datalist";
642 } else if (type_name
== "Atk.ImplementorIface") {
643 type_name
= "Atk.Implementor";
645 string[] type_components
= type_name
.split (".");
646 if (type_components
[1] != null) {
648 string namespace_name
= transform_namespace_name (type_components
[0]);
649 string transformed_type_name
= type_components
[1];
650 type
= new UnresolvedType
.from_symbol (new
UnresolvedSymbol (new
UnresolvedSymbol (null, namespace_name
), transformed_type_name
));
652 type
= new UnresolvedType
.from_symbol (new
UnresolvedSymbol (null, type_name
));
659 string transform_namespace_name (string gir_module_name
) {
660 if (gir_module_name
== "GObject") {
662 } else if (gir_module_name
== "Gio") {
664 } else if (gir_module_name
== "GModule") {
667 return gir_module_name
;
670 Struct
parse_record () {
671 start_element ("record");
672 var st
= new
Struct (reader
.get_attribute ("name"), get_current_src ());
675 string glib_is_gtype_struct_for
= reader
.get_attribute ("glib:is-gtype-struct-for");
677 st
.access
= SymbolAccessibility
.PUBLIC
;
679 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
680 if (reader
.get_attribute ("introspectable") == "0") {
685 if (reader
.name
== "field") {
686 st
.add_field (parse_field ());
687 } else if (reader
.name
== "callback") {
688 if (glib_is_gtype_struct_for
!= null) {
689 ArrayList
<Method
> callbacks
= gtype_callbacks
.get (glib_is_gtype_struct_for
);
690 if (callbacks
== null) {
691 callbacks
= new ArrayList
<Method
> ();
692 gtype_callbacks
.set (glib_is_gtype_struct_for
, callbacks
);
694 callbacks
.add (parse_method ("callback"));
698 } else if (reader
.name
== "constructor") {
699 parse_constructor ();
700 } else if (reader
.name
== "method") {
701 st
.add_method (parse_method ("method"));
702 } else if (reader
.name
== "union") {
703 Struct s
= parse_union ();
704 var s_fields
= s
.get_fields ();
705 foreach (var f
in s_fields
) {
706 f
.set_cname (s
.get_cname () + "." + f
.get_cname ());
707 f
.name
= s
.name
+ "_" + f
.name
;
712 Report
.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader
.name
));
716 end_element ("record");
720 void postprocess_gtype_callbacks (Namespace ns
) {
721 foreach (string gtype_name
in gtype_callbacks
.get_keys ()) {
722 var gtype
= ns
.scope
.lookup (gtype_name
) as ObjectTypeSymbol
;
723 ArrayList
<Method
> callbacks
= gtype_callbacks
.get (gtype_name
);
724 foreach (Method m
in callbacks
) {
725 var symbol
= gtype
.scope
.lookup (m
.name
);
726 if (symbol
== null) {
728 } else if (symbol is Method
) {
729 var meth
= (Method
) symbol
;
730 if (gtype is Class
) {
731 meth
.is_virtual
= true;
732 } else if (gtype is Interface
) {
733 meth
.is_abstract
= true;
735 } else if (symbol is Signal
) {
736 var sig
= (Signal
) symbol
;
737 sig
.is_virtual
= true;
739 Report
.error (get_current_src (), "unknown member type `%s' in `%s'".printf (m
.name
, gtype
.name
));
745 Class
parse_class () {
746 start_element ("class");
747 var cl
= new
Class (reader
.get_attribute ("name"), get_current_src ());
748 cl
.access
= SymbolAccessibility
.PUBLIC
;
751 string cname
= reader
.get_attribute ("c:type");
753 cl
.set_cname (cname
);
756 string parent
= reader
.get_attribute ("parent");
757 if (parent
!= null) {
758 cl
.add_base_type (parse_type_from_name (parent
));
762 var signals
= new ArrayList
<Signal
> ();
763 var methods
= new ArrayList
<Method
> ();
764 var vmethods
= new ArrayList
<Method
> ();
765 var fields
= new ArrayList
<Field
> ();
766 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
767 if (reader
.get_attribute ("introspectable") == "0") {
772 if (reader
.name
== "implements") {
773 start_element ("implements");
774 cl
.add_base_type (parse_type_from_name (reader
.get_attribute ("name")));
776 end_element ("implements");
777 } else if (reader
.name
== "constant") {
778 cl
.add_constant (parse_constant ());
779 } else if (reader
.name
== "field") {
780 fields
.add (parse_field ());
781 } else if (reader
.name
== "property") {
782 cl
.add_property (parse_property ());
783 } else if (reader
.name
== "constructor") {
784 cl
.add_method (parse_constructor (cname
));
785 } else if (reader
.name
== "function") {
786 methods
.add (parse_method ("function"));
787 } else if (reader
.name
== "method") {
788 methods
.add (parse_method ("method"));
789 } else if (reader
.name
== "virtual-method") {
790 vmethods
.add (parse_method ("virtual-method"));
791 } else if (reader
.name
== "union") {
792 Struct s
= parse_union ();
793 var s_fields
= s
.get_fields ();
794 foreach (var f
in s_fields
) {
795 f
.set_cname (s
.get_cname () + "." + f
.get_cname ());
796 f
.name
= s
.name
+ "_" + f
.name
;
799 } else if (reader
.name
== "glib:signal") {
800 signals
.add (parse_signal ());
803 Report
.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader
.name
));
809 foreach (Signal sig
in signals
) {
810 var symbol
= cl
.scope
.lookup (sig
.name
);
811 if (symbol
== null) {
813 } else if (symbol is Property
) {
814 // properties take precedence
816 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (sig
.name
, cl
.name
));
820 // virtual method merging
821 foreach (Method m
in vmethods
) {
822 var symbol
= cl
.scope
.lookup (m
.name
);
823 if (symbol
== null) {
825 } else if (symbol is Signal
) {
826 var sig
= (Signal
) symbol
;
827 sig
.is_virtual
= true;
828 } else if (symbol is Property
|| symbol is Field
) {
829 // assume method is getter for property/field ignore method
831 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m
.name
, cl
.name
));
836 foreach (Method m
in methods
) {
837 var symbol
= cl
.scope
.lookup (m
.name
);
838 if (symbol
== null) {
840 } else if (symbol is Signal
) {
841 var sig
= (Signal
) symbol
;
842 sig
.has_emitter
= true;
843 } else if (symbol is Property
|| symbol is Field
) {
844 // assume method is getter for property/field ignore method
845 } else if (symbol is Method
) {
846 // assume method is wrapper for virtual method
848 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m
.name
, cl
.name
));
852 // fields have lowest priority
853 foreach (Field f
in fields
) {
854 var symbol
= cl
.scope
.lookup (f
.name
);
855 if (symbol
== null) {
860 handle_async_methods (cl
);
862 end_element ("class");
866 Interface
parse_interface () {
867 start_element ("interface");
868 var iface
= new
Interface (reader
.get_attribute ("name"), get_current_src ());
869 iface
.access
= SymbolAccessibility
.PUBLIC
;
870 iface
.external
= true;
872 string cname
= reader
.get_attribute ("c:type");
874 iface
.set_cname (cname
);
878 var methods
= new ArrayList
<Method
> ();
879 var vmethods
= new ArrayList
<Method
> ();
880 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
881 if (reader
.get_attribute ("introspectable") == "0") {
886 if (reader
.name
== "prerequisite") {
887 start_element ("prerequisite");
888 iface
.add_prerequisite (parse_type_from_name (reader
.get_attribute ("name")));
890 end_element ("prerequisite");
891 } else if (reader
.name
== "field") {
893 } else if (reader
.name
== "property") {
894 iface
.add_property (parse_property ());
895 } else if (reader
.name
== "virtual-method") {
896 vmethods
.add (parse_method ("virtual-method"));
897 } else if (reader
.name
== "function") {
898 methods
.add (parse_method ("function"));
899 } else if (reader
.name
== "method") {
900 methods
.add (parse_method ("method"));
901 } else if (reader
.name
== "glib:signal") {
902 iface
.add_signal (parse_signal ());
905 Report
.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader
.name
));
910 // ensure we have at least one instantiable prerequisite (GLib.Object)
911 bool has_instantiable_prereq
= false;
912 foreach (DataType prereq
in iface
.get_prerequisites ()) {
913 if (prereq
.data_type is Class
) {
914 has_instantiable_prereq
= true;
919 if (!has_instantiable_prereq
)
920 iface
.add_prerequisite (new
ObjectType ((ObjectTypeSymbol
) glib_ns
.scope
.lookup ("Object")));
922 // virtual method merging
923 foreach (Method m
in vmethods
) {
924 var symbol
= iface
.scope
.lookup (m
.name
);
925 if (symbol
== null) {
926 iface
.add_method (m
);
927 } else if (symbol is Signal
) {
928 var sig
= (Signal
) symbol
;
929 sig
.is_virtual
= true;
931 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m
.name
, iface
.name
));
936 foreach (Method m
in methods
) {
937 var symbol
= iface
.scope
.lookup (m
.name
);
938 if (symbol
== null) {
939 iface
.add_method (m
);
940 } else if (symbol is Signal
) {
941 var sig
= (Signal
) symbol
;
942 sig
.has_emitter
= true;
943 } else if (symbol is Method
) {
944 // assume method is wrapper for virtual method
946 Report
.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m
.name
, iface
.name
));
950 handle_async_methods (iface
);
952 end_element ("interface");
956 void handle_async_methods (ObjectTypeSymbol type_symbol
) {
957 var methods
= type_symbol
.get_methods ();
958 for (int method_n
= 0 ; method_n
< methods
.size
; method_n
++) {
959 var m
= methods
.get (method_n
);
962 string finish_method_base
;
963 if (m
.name
.has_suffix ("_async")) {
964 finish_method_base
= m
.name
.substring (0, m
.name
.length
- "_async".length
);
966 finish_method_base
= m
.name
;
968 var finish_method
= type_symbol
.scope
.lookup (finish_method_base
+ "_finish") as Method
;
970 // check if the method is using non-standard finish method name
971 if (finish_method
== null) {
972 var method_cname
= m
.get_finish_cname ();
973 foreach (Method method
in type_symbol
.get_methods ()) {
974 if (method
.get_cname () == method_cname
) {
975 finish_method
= method
;
981 if (finish_method
!= null) {
982 m
.return_type
= finish_method
.return_type
.copy ();
983 m
.no_array_length
= finish_method
.no_array_length
;
984 m
.array_null_terminated
= finish_method
.array_null_terminated
;
985 foreach (var param
in finish_method
.get_parameters ()) {
986 if (param
.direction
== ParameterDirection
.OUT
) {
987 var async_param
= param
.copy ();
988 if (m
.scope
.lookup (param
.name
) != null) {
989 // parameter name conflict
990 async_param
.name
+= "_out";
992 m
.add_parameter (async_param
);
995 foreach (DataType error_type
in finish_method
.get_error_types ()) {
996 m
.add_error_type (error_type
.copy ());
998 if (methods
.index_of (finish_method
) < method_n
) {
1001 type_symbol
.scope
.remove (finish_method
.name
);
1002 methods
.remove (finish_method
);
1008 Field
parse_field () {
1009 start_element ("field");
1010 string name
= reader
.get_attribute ("name");
1011 string allow_none
= reader
.get_attribute ("allow-none");
1013 var type
= parse_type ();
1014 var field
= new
Field (name
, type
, null, get_current_src ());
1015 field
.access
= SymbolAccessibility
.PUBLIC
;
1016 if (allow_none
== "1") {
1017 type
.nullable
= true;
1019 end_element ("field");
1023 Property
parse_property () {
1024 start_element ("property");
1025 string name
= string.joinv ("_", reader
.get_attribute ("name").split ("-"));
1026 string readable
= reader
.get_attribute ("readable");
1027 string writable
= reader
.get_attribute ("writable");
1028 string construct_
= reader
.get_attribute ("construct");
1029 string construct_only
= reader
.get_attribute ("construct-only");
1031 var type
= parse_type ();
1032 var prop
= new
Property (name
, type
, null, null, get_current_src ());
1033 prop
.access
= SymbolAccessibility
.PUBLIC
;
1034 if (readable
!= "0") {
1035 prop
.get_accessor
= new
PropertyAccessor (true, false, false, prop
.property_type
.copy (), null, null);
1037 if (writable
== "1" || construct_only
== "1") {
1038 prop
.set_accessor
= new
PropertyAccessor (false, (construct_only
!= "1") && (writable
== "1"), (construct_only
== "1") || (construct_
== "1"), prop
.property_type
.copy (), null, null);
1040 end_element ("property");
1044 Delegate
parse_callback () {
1045 return this
.parse_function ("callback") as Delegate
;
1048 Method
parse_constructor (string? parent_ctype
= null) {
1049 start_element ("constructor");
1050 string name
= reader
.get_attribute ("name");
1051 string throws_string
= reader
.get_attribute ("throws");
1052 string cname
= reader
.get_attribute ("c:identifier");
1056 parse_return_value (out ctype
);
1058 var m
= new
CreationMethod (null, name
, get_current_src ());
1059 m
.access
= SymbolAccessibility
.PUBLIC
;
1060 m
.has_construct_function
= false;
1061 if (ctype
!= null && (parent_ctype
== null || ctype
!= parent_ctype
+ "*")) {
1062 m
.custom_return_type_cname
= ctype
;
1064 if (m
.name
== "new") {
1066 } else if (m
.name
.has_prefix ("new_")) {
1067 m
.name
= m
.name
.offset ("new_".length
);
1069 if (cname
!= null) {
1070 m
.set_cname (cname
);
1072 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
1073 start_element ("parameters");
1075 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
1076 m
.add_parameter (parse_parameter ());
1078 end_element ("parameters");
1081 if (throws_string
== "1") {
1082 m
.add_error_type (new
ErrorType (null, null));
1084 end_element ("constructor");
1089 public MethodInfo (FormalParameter param
, int array_length_idx
, int closure_idx
, int destroy_idx
) {
1091 this
.array_length_idx
= array_length_idx
;
1092 this
.closure_idx
= closure_idx
;
1093 this
.destroy_idx
= destroy_idx
;
1094 this
.vala_idx
= 0.0F
;
1098 public FormalParameter param
;
1099 public float vala_idx
;
1100 public int array_length_idx
;
1101 public int closure_idx
;
1102 public int destroy_idx
;
1106 Symbol
parse_function (string element_name
) {
1107 start_element (element_name
);
1108 string name
= reader
.get_attribute ("name");
1109 string cname
= reader
.get_attribute ("c:identifier");
1110 string throws_string
= reader
.get_attribute ("throws");
1111 string invoker
= reader
.get_attribute ("invoker");
1113 DataType return_type
;
1114 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "return-value") {
1115 return_type
= parse_return_value ();
1117 return_type
= new
VoidType ();
1122 if (element_name
== "callback") {
1123 s
= new
Delegate (name
, return_type
, get_current_src ());
1125 s
= new
Method (name
, return_type
, get_current_src ());
1128 s
.access
= SymbolAccessibility
.PUBLIC
;
1129 if (cname
!= null) {
1131 ((Method
) s
).set_cname (cname
);
1133 ((Delegate
) s
).set_cname (cname
);
1137 if (element_name
== "virtual-method" || element_name
== "callback") {
1139 ((Method
) s
).is_virtual
= true;
1142 if (invoker
!= null){
1145 } else if (element_name
== "function") {
1146 ((Method
) s
).binding
= MemberBinding
.STATIC
;
1149 var parameters
= new ArrayList
<MethodInfo
> ();
1150 var array_length_parameters
= new ArrayList
<int> ();
1151 var closure_parameters
= new ArrayList
<int> ();
1152 var destroy_parameters
= new ArrayList
<int> ();
1153 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
1154 start_element ("parameters");
1157 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
1158 int array_length_idx
, closure_idx
, destroy_idx
;
1160 var param
= parse_parameter (out array_length_idx
, out closure_idx
, out destroy_idx
, out scope
);
1161 if (array_length_idx
!= -1) {
1162 array_length_parameters
.add (array_length_idx
);
1164 if (closure_idx
!= -1) {
1165 closure_parameters
.add (closure_idx
);
1167 if (destroy_idx
!= -1) {
1168 destroy_parameters
.add (destroy_idx
);
1171 var info
= new
MethodInfo(param
, array_length_idx
, closure_idx
, destroy_idx
);
1173 if (s is Method
&& scope
== "async") {
1174 var unresolved_type
= param
.variable_type as UnresolvedType
;
1175 if (unresolved_type
!= null && unresolved_type
.unresolved_symbol
.name
== "AsyncReadyCallback") {
1176 // GAsync-style method
1177 ((Method
) s
).coroutine
= true;
1182 parameters
.add (info
);
1184 end_element ("parameters");
1189 foreach (MethodInfo info
in parameters
) {
1190 if (s is Delegate
&& info
.closure_idx
== i
) {
1191 var d
= (Delegate
) s
;
1192 d
.has_target
= true;
1193 d
.cinstance_parameter_position
= (float) j
- 0.1;
1195 } else if (info
.keep
1196 && !array_length_parameters
.contains (i
)
1197 && !closure_parameters
.contains (i
)
1198 && !destroy_parameters
.contains (i
)) {
1199 info
.vala_idx
= (float) j
;
1202 /* interpolate for vala_idx between this and last*/
1203 float last_idx
= 0.0F
;
1205 last_idx
= parameters
[last
].vala_idx
;
1207 for (int k
=last
+1; k
< i
; k
++) {
1208 parameters
[k
].vala_idx
= last_idx
+ (((j
- last_idx
) / (i
-last
)) * (k
-last
));
1214 // make sure that vala_idx is always set
1215 // the above if branch does not set vala_idx for
1216 // hidden parameters at the end of the parameter list
1217 info
.vala_idx
= (j
- 1) + (i
- last
) * 0.1F
;
1222 foreach (MethodInfo info
in parameters
) {
1225 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
1228 ((Method
) s
).add_parameter (info
.param
);
1230 ((Delegate
) s
).add_parameter (info
.param
);
1233 if (info
.array_length_idx
!= -1) {
1234 if ((info
.array_length_idx
) >= parameters
.size
) {
1235 Report
.error (get_current_src (), "invalid array_length index");
1238 info
.param
.carray_length_parameter_position
= parameters
[info
.array_length_idx
].vala_idx
;
1239 info
.param
.set_array_length_cname (parameters
[info
.array_length_idx
].param
.name
);
1241 if (info
.param
.variable_type is ArrayType
&& info
.array_length_idx
== -1) {
1242 info
.param
.no_array_length
= true;
1245 if (info
.closure_idx
!= -1) {
1246 if ((info
.closure_idx
) >= parameters
.size
) {
1247 Report
.error (get_current_src (), "invalid closure index");
1250 info
.param
.cdelegate_target_parameter_position
= parameters
[info
.closure_idx
].vala_idx
;
1252 if (info
.destroy_idx
!= -1) {
1253 if (info
.destroy_idx
>= parameters
.size
) {
1254 Report
.error (get_current_src (), "invalid destroy index");
1257 info
.param
.cdestroy_notify_parameter_position
= parameters
[info
.destroy_idx
].vala_idx
;
1262 if (throws_string
== "1") {
1263 s
.add_error_type (new
ErrorType (null, null));
1265 end_element (element_name
);
1269 Method
parse_method (string element_name
) {
1270 return this
.parse_function (element_name
) as Method
;
1273 Signal
parse_signal () {
1274 start_element ("glib:signal");
1275 string name
= string.joinv ("_", reader
.get_attribute ("name").split ("-"));
1277 DataType return_type
;
1278 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "return-value") {
1279 return_type
= parse_return_value ();
1281 return_type
= new
VoidType ();
1283 var sig
= new
Signal (name
, return_type
);
1284 sig
.access
= SymbolAccessibility
.PUBLIC
;
1285 sig
.external
= true;
1286 if (current_token
== MarkupTokenType
.START_ELEMENT
&& reader
.name
== "parameters") {
1287 start_element ("parameters");
1289 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
1290 sig
.add_parameter (parse_parameter ());
1292 end_element ("parameters");
1294 end_element ("glib:signal");
1298 Class
parse_boxed () {
1299 string name
= reader
.get_attribute ("name");
1301 name
= reader
.get_attribute ("glib:name");
1303 var cl
= new
Class (name
);
1304 cl
.access
= SymbolAccessibility
.PUBLIC
;
1306 cl
.is_compact
= true;
1308 string cname
= reader
.get_attribute ("c:type");
1309 if (cname
!= null) {
1310 cl
.set_cname (cname
);
1313 cl
.set_type_id ("%s ()".printf (reader
.get_attribute ("glib:get-type")));
1314 cl
.set_free_function ("g_boxed_free");
1315 cl
.set_dup_function ("g_boxed_copy");
1319 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
1320 if (reader
.get_attribute ("introspectable") == "0") {
1325 if (reader
.name
== "field") {
1326 cl
.add_field (parse_field ());
1327 } else if (reader
.name
== "constructor") {
1328 parse_constructor ();
1329 } else if (reader
.name
== "method") {
1330 cl
.add_method (parse_method ("method"));
1333 Report
.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader
.name
));
1338 if (current_token
!= MarkupTokenType
.END_ELEMENT
) {
1340 Report
.error (get_current_src (), "expected end element");
1346 Struct
parse_union () {
1347 start_element ("union");
1348 var st
= new
Struct (reader
.get_attribute ("name"));
1349 st
.access
= SymbolAccessibility
.PUBLIC
;
1353 while (current_token
== MarkupTokenType
.START_ELEMENT
) {
1354 if (reader
.get_attribute ("introspectable") == "0") {
1359 if (reader
.name
== "field") {
1360 st
.add_field (parse_field ());
1361 } else if (reader
.name
== "constructor") {
1362 parse_constructor ();
1363 } else if (reader
.name
== "method") {
1364 st
.add_method (parse_method ("method"));
1365 } else if (reader
.name
== "record") {
1366 Struct s
= parse_record ();
1367 var fs
= s
.get_fields ();
1368 foreach (var f
in fs
) {
1369 f
.set_cname (s
.get_cname () + "." + f
.get_cname ());
1370 f
.name
= s
.name
+ "_" + f
.name
;
1375 Report
.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader
.name
));
1380 end_element ("union");
1384 Constant
parse_constant () {
1385 start_element ("constant");
1386 string name
= reader
.get_attribute ("name");
1388 var type
= parse_type ();
1389 var c
= new
Constant (name
, type
, null, get_current_src ());
1390 c
.access
= SymbolAccessibility
.PUBLIC
;
1392 end_element ("constant");
1396 public void parse_metadata (string metadata_filename
) {
1397 if (FileUtils
.test (metadata_filename
, FileTest
.EXISTS
)) {
1400 FileUtils
.get_contents (metadata_filename
, out metadata
, null);
1402 foreach (string line
in metadata
.split ("\n")) {
1403 if (line
.has_prefix ("#")) {
1404 // ignore comment lines
1408 string[] tokens
= line
.split (" ", 2);
1410 if (null == tokens
[0]) {
1414 foreach (string attribute
in tokens
[1].split (" ")) {
1415 string[] pair
= attribute
.split ("=", 2);
1416 if (pair
[0] == null || pair
[1] == null) {
1420 string key
= "%s/@%s".printf (tokens
[0], pair
[0]);
1421 attributes_map
.set (key
, pair
[1].substring (1, pair
[1].length
- 2));
1424 } catch (FileError e
) {
1425 Report
.error (null, "Unable to read metadata file: %s".printf (e
.message
));
1428 Report
.error (null, "Metadata file `%s' not found".printf (metadata_filename
));
1432 void add_package_name (string name
) {
1433 if (package_names
== null) {
1434 package_names
= new
string[0];
1437 foreach (var existing
in package_names
) {
1438 if (name
== existing
) {
1443 package_names
+= name
;
1446 public string[]?
get_package_names () {
1447 return package_names
;