3
* Copyright (C) 2008-2009 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) {
289
current_source_file.add_node (sym);
291
end_element ("namespace");
293
postprocess_gtype_callbacks (ns);
295
if (!new_namespace) {
302
Struct parse_alias () {
303
start_element ("alias");
304
var st = new Struct (reader.get_attribute ("name"), get_current_src ());
305
st.access = SymbolAccessibility.PUBLIC;
309
st.base_type = parse_type (null, null, true);
311
end_element ("alias");
315
private void calculate_common_prefix (ref string common_prefix, string cname) {
316
if (common_prefix == null) {
317
common_prefix = cname;
318
while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
319
// FIXME: could easily be made faster
320
common_prefix = common_prefix.ndup (common_prefix.size () - 1);
323
while (!cname.has_prefix (common_prefix)) {
324
common_prefix = common_prefix.ndup (common_prefix.size () - 1);
327
while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
328
(cname.offset (common_prefix.length).get_char ().isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
329
// enum values may not consist solely of digits
330
common_prefix = common_prefix.ndup (common_prefix.size () - 1);
334
Enum parse_enumeration () {
335
start_element ("enumeration");
336
var en = new Enum (reader.get_attribute ("name"), get_current_src ());
337
en.access = SymbolAccessibility.PUBLIC;
339
string enum_cname = reader.get_attribute ("c:type");
340
if (enum_cname != null) {
341
en.set_cname (enum_cname);
346
string common_prefix = null;
348
while (current_token == MarkupTokenType.START_ELEMENT) {
349
if (reader.get_attribute ("introspectable") == "0") {
354
if (reader.name == "member") {
355
var ev = parse_enumeration_member ();
357
calculate_common_prefix (ref common_prefix, ev.get_cname ());
364
en.set_cprefix (common_prefix);
366
end_element ("enumeration");
370
ErrorDomain parse_error_domain () {
371
start_element ("enumeration");
373
var ed = new ErrorDomain (reader.get_attribute ("name"), get_current_src ());
374
ed.access = SymbolAccessibility.PUBLIC;
376
string enum_cname = reader.get_attribute ("c:type");
377
if (enum_cname != null) {
378
ed.set_cname (enum_cname);
383
string common_prefix = null;
385
while (current_token == MarkupTokenType.START_ELEMENT) {
386
if (reader.get_attribute ("introspectable") == "0") {
391
if (reader.name == "member") {
392
ErrorCode ec = parse_error_member ();
394
calculate_common_prefix (ref common_prefix, ec.get_cname ());
401
ed.set_cprefix (common_prefix);
403
end_element ("enumeration");
407
Enum parse_bitfield () {
408
start_element ("bitfield");
409
var en = new Enum (reader.get_attribute ("name"), get_current_src ());
410
en.access = SymbolAccessibility.PUBLIC;
412
while (current_token == MarkupTokenType.START_ELEMENT) {
413
if (reader.get_attribute ("introspectable") == "0") {
418
if (reader.name == "member") {
419
en.add_value (parse_enumeration_member ());
425
end_element ("bitfield");
429
EnumValue parse_enumeration_member () {
430
start_element ("member");
431
var ev = new EnumValue (string.joinv ("_", reader.get_attribute ("name").up ().split ("-")), null);
432
ev.set_cname (reader.get_attribute ("c:identifier"));
434
end_element ("member");
438
ErrorCode parse_error_member () {
439
start_element ("member");
442
string name = string.joinv ("_", reader.get_attribute ("name").up ().split ("-"));
443
string value = reader.get_attribute ("value");
445
ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
447
ec = new ErrorCode (name);
451
end_element ("member");
455
DataType parse_return_value (out string? ctype = null) {
456
start_element ("return-value");
457
string transfer = reader.get_attribute ("transfer-ownership");
458
string allow_none = reader.get_attribute ("allow-none");
460
var transfer_elements = transfer == "full";
461
var type = &ctype != null ? parse_type(out ctype, null, transfer_elements) : parse_type (null, null, transfer_elements);
462
if (transfer == "full" || transfer == "container") {
463
type.value_owned = true;
465
if (allow_none == "1") {
466
type.nullable = true;
468
end_element ("return-value");
472
FormalParameter parse_parameter (out int array_length_idx = null, out int closure_idx = null, out int destroy_idx = null, out string? scope = null) {
473
FormalParameter param;
475
if (&array_length_idx != null) {
476
array_length_idx = -1;
478
if (&closure_idx != null) {
481
if (&destroy_idx != null) {
485
start_element ("parameter");
486
string name = reader.get_attribute ("name");
487
string direction = reader.get_attribute ("direction");
488
string transfer = reader.get_attribute ("transfer-ownership");
489
string allow_none = reader.get_attribute ("allow-none");
491
if (&scope != null) {
492
scope = reader.get_attribute ("scope");
495
string closure = reader.get_attribute ("closure");
496
string destroy = reader.get_attribute ("destroy");
497
if (closure != null && &closure_idx != null) {
498
closure_idx = closure.to_int ();
500
if (destroy != null && &destroy_idx != null) {
501
destroy_idx = destroy.to_int ();
505
if (reader.name == "varargs") {
506
start_element ("varargs");
508
param = new FormalParameter.with_ellipsis (get_current_src ());
509
end_element ("varargs");
511
var type = parse_type (null, out array_length_idx, transfer == "full");
512
if (transfer == "full" || transfer == "container" || destroy != null) {
513
type.value_owned = true;
515
if (allow_none == "1") {
516
type.nullable = true;
518
param = new FormalParameter (name, type, get_current_src ());
519
if (direction == "out") {
520
param.direction = ParameterDirection.OUT;
521
} else if (direction == "inout") {
522
param.direction = ParameterDirection.REF;
525
end_element ("parameter");
529
DataType parse_type (out string? ctype = null, out int array_length_index = null, bool transfer_elements = false) {
530
bool is_array = false;
531
string type_name = reader.get_attribute ("name");
533
if (reader.name == "array") {
535
start_element ("array");
537
if (!(type_name == "GLib.Array" || type_name == "GLib.PtrArray")) {
538
if (reader.get_attribute ("length") != null
539
&& &array_length_index != null) {
540
array_length_index = reader.get_attribute ("length").to_int ();
543
var element_type = parse_type ();
544
end_element ("array");
545
return new ArrayType (element_type, 1, null);
547
} else if (reader.name == "callback"){
548
var callback = parse_callback ();
549
return new DelegateType (callback);
551
start_element ("type");
554
if (&ctype != null) {
555
ctype = reader.get_attribute("c:type");
560
if (type_name == "GLib.PtrArray"
561
&& current_token == MarkupTokenType.START_ELEMENT) {
562
type_name = "GLib.GenericArray";
565
DataType type = parse_type_from_name (type_name);
567
// type arguments / element types
568
while (current_token == MarkupTokenType.START_ELEMENT) {
569
var element_type = parse_type ();
570
element_type.value_owned = transfer_elements;
571
type.add_type_argument (element_type);
574
end_element (is_array ? "array" : "type");
578
DataType parse_type_from_name (string type_name) {
580
if (type_name == "none") {
581
type = new VoidType ();
582
} else if (type_name == "gpointer") {
583
type = new PointerType (new VoidType ());
584
} else if (type_name == "GObject.Strv") {
585
type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, null);
587
if (type_name == "utf8") {
588
type_name = "string";
589
} else if (type_name == "gboolean") {
591
} else if (type_name == "gchar") {
593
} else if (type_name == "gshort") {
595
} else if (type_name == "gushort") {
596
type_name = "ushort";
597
} else if (type_name == "gint") {
599
} else if (type_name == "guint") {
601
} else if (type_name == "glong") {
603
} else if (type_name == "gulong") {
605
} else if (type_name == "gint8") {
607
} else if (type_name == "guint8") {
609
} else if (type_name == "gint16") {
611
} else if (type_name == "guint16") {
612
type_name = "uint16";
613
} else if (type_name == "gint32") {
615
} else if (type_name == "guint32") {
616
type_name = "uint32";
617
} else if (type_name == "gint64") {
619
} else if (type_name == "guint64") {
620
type_name = "uint64";
621
} else if (type_name == "gfloat") {
623
} else if (type_name == "gdouble") {
624
type_name = "double";
625
} else if (type_name == "filename") {
626
type_name = "string";
627
} else if (type_name == "GLib.offset") {
629
} else if (type_name == "gsize") {
630
type_name = "size_t";
631
} else if (type_name == "gssize") {
632
type_name = "ssize_t";
633
} else if (type_name == "GType") {
634
type_name = "GLib.Type";
635
} else if (type_name == "GLib.String") {
636
type_name = "GLib.StringBuilder";
637
} else if (type_name == "GObject.Class") {
638
type_name = "GLib.ObjectClass";
639
} else if (type_name == "GLib.unichar") {
640
type_name = "unichar";
641
} else if (type_name == "GLib.Data") {
642
type_name = "GLib.Datalist";
643
} else if (type_name == "Atk.ImplementorIface") {
644
type_name = "Atk.Implementor";
646
string[] type_components = type_name.split (".");
647
if (type_components[1] != null) {
649
string namespace_name = transform_namespace_name (type_components[0]);
650
string transformed_type_name = type_components[1];
651
type = new UnresolvedType.from_symbol (new UnresolvedSymbol (new UnresolvedSymbol (null, namespace_name), transformed_type_name));
653
type = new UnresolvedType.from_symbol (new UnresolvedSymbol (null, type_name));
660
string transform_namespace_name (string gir_module_name) {
661
if (gir_module_name == "GObject") {
663
} else if (gir_module_name == "Gio") {
665
} else if (gir_module_name == "GModule") {
668
return gir_module_name;
671
Struct parse_record () {
672
start_element ("record");
673
var st = new Struct (reader.get_attribute ("name"), get_current_src ());
676
string glib_is_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
678
st.access = SymbolAccessibility.PUBLIC;
680
while (current_token == MarkupTokenType.START_ELEMENT) {
681
if (reader.get_attribute ("introspectable") == "0") {
686
if (reader.name == "field") {
687
st.add_field (parse_field ());
688
} else if (reader.name == "callback") {
689
if (glib_is_gtype_struct_for != null) {
690
ArrayList<Method> callbacks = gtype_callbacks.get (glib_is_gtype_struct_for);
691
if (callbacks == null) {
692
callbacks = new ArrayList<Method> ();
693
gtype_callbacks.set (glib_is_gtype_struct_for, callbacks);
695
callbacks.add (parse_method ("callback"));
699
} else if (reader.name == "constructor") {
700
parse_constructor ();
701
} else if (reader.name == "method") {
702
st.add_method (parse_method ("method"));
703
} else if (reader.name == "union") {
704
Struct s = parse_union ();
705
var s_fields = s.get_fields ();
706
foreach (var f in s_fields) {
707
f.set_cname (s.get_cname () + "." + f.get_cname ());
708
f.name = s.name + "_" + f.name;
713
Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
717
end_element ("record");
721
void postprocess_gtype_callbacks (Namespace ns) {
722
foreach (string gtype_name in gtype_callbacks.get_keys ()) {
723
var gtype = ns.scope.lookup (gtype_name) as ObjectTypeSymbol;
724
ArrayList<Method> callbacks = gtype_callbacks.get (gtype_name);
725
foreach (Method m in callbacks) {
726
var symbol = gtype.scope.lookup (m.name);
727
if (symbol == null) {
729
} else if (symbol is Method) {
730
var meth = (Method) symbol;
731
if (gtype is Class) {
732
meth.is_virtual = true;
733
} else if (gtype is Interface) {
734
meth.is_abstract = true;
736
} else if (symbol is Signal) {
737
var sig = (Signal) symbol;
738
sig.is_virtual = true;
740
Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (m.name, gtype.name));
746
Class parse_class () {
747
start_element ("class");
748
var cl = new Class (reader.get_attribute ("name"), get_current_src ());
749
cl.access = SymbolAccessibility.PUBLIC;
752
string cname = reader.get_attribute ("c:type");
754
cl.set_cname (cname);
757
string parent = reader.get_attribute ("parent");
758
if (parent != null) {
759
cl.add_base_type (parse_type_from_name (parent));
763
var signals = new ArrayList<Signal> ();
764
var methods = new ArrayList<Method> ();
765
var vmethods = new ArrayList<Method> ();
766
var fields = new ArrayList<Field> ();
767
while (current_token == MarkupTokenType.START_ELEMENT) {
768
if (reader.get_attribute ("introspectable") == "0") {
773
if (reader.name == "implements") {
774
start_element ("implements");
775
cl.add_base_type (parse_type_from_name (reader.get_attribute ("name")));
777
end_element ("implements");
778
} else if (reader.name == "constant") {
779
cl.add_constant (parse_constant ());
780
} else if (reader.name == "field") {
781
fields.add (parse_field ());
782
} else if (reader.name == "property") {
783
cl.add_property (parse_property ());
784
} else if (reader.name == "constructor") {
785
cl.add_method (parse_constructor (cname));
786
} else if (reader.name == "function") {
787
methods.add (parse_method ("function"));
788
} else if (reader.name == "method") {
789
methods.add (parse_method ("method"));
790
} else if (reader.name == "virtual-method") {
791
vmethods.add (parse_method ("virtual-method"));
792
} else if (reader.name == "union") {
793
Struct s = parse_union ();
794
var s_fields = s.get_fields ();
795
foreach (var f in s_fields) {
796
f.set_cname (s.get_cname () + "." + f.get_cname ());
797
f.name = s.name + "_" + f.name;
800
} else if (reader.name == "glib:signal") {
801
signals.add (parse_signal ());
804
Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
810
foreach (Signal sig in signals) {
811
var symbol = cl.scope.lookup (sig.name);
812
if (symbol == null) {
814
} else if (symbol is Property) {
815
// properties take precedence
817
Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (sig.name, cl.name));
821
// virtual method merging
822
foreach (Method m in vmethods) {
823
var symbol = cl.scope.lookup (m.name);
824
if (symbol == null) {
826
} else if (symbol is Signal) {
827
var sig = (Signal) symbol;
828
sig.is_virtual = true;
829
} else if (symbol is Property || symbol is Field) {
830
// assume method is getter for property/field ignore method
832
Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, cl.name));
837
foreach (Method m in methods) {
838
var symbol = cl.scope.lookup (m.name);
839
if (symbol == null) {
841
} else if (symbol is Signal) {
842
var sig = (Signal) symbol;
843
sig.has_emitter = true;
844
} else if (symbol is Property || symbol is Field) {
845
// assume method is getter for property/field ignore method
846
} else if (symbol is Method) {
847
// assume method is wrapper for virtual method
849
Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, cl.name));
853
// fields have lowest priority
854
foreach (Field f in fields) {
855
var symbol = cl.scope.lookup (f.name);
856
if (symbol == null) {
861
handle_async_methods (cl);
863
end_element ("class");
867
Interface parse_interface () {
868
start_element ("interface");
869
var iface = new Interface (reader.get_attribute ("name"), get_current_src ());
870
iface.access = SymbolAccessibility.PUBLIC;
871
iface.external = true;
873
string cname = reader.get_attribute ("c:type");
875
iface.set_cname (cname);
879
var methods = new ArrayList<Method> ();
880
var vmethods = new ArrayList<Method> ();
881
while (current_token == MarkupTokenType.START_ELEMENT) {
882
if (reader.get_attribute ("introspectable") == "0") {
887
if (reader.name == "prerequisite") {
888
start_element ("prerequisite");
889
iface.add_prerequisite (parse_type_from_name (reader.get_attribute ("name")));
891
end_element ("prerequisite");
892
} else if (reader.name == "field") {
894
} else if (reader.name == "property") {
895
iface.add_property (parse_property ());
896
} else if (reader.name == "virtual-method") {
897
vmethods.add (parse_method ("virtual-method"));
898
} else if (reader.name == "function") {
899
methods.add (parse_method ("function"));
900
} else if (reader.name == "method") {
901
methods.add (parse_method ("method"));
902
} else if (reader.name == "glib:signal") {
903
iface.add_signal (parse_signal ());
906
Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
911
// ensure we have at least one instantiable prerequisite (GLib.Object)
912
bool has_instantiable_prereq = false;
913
foreach (DataType prereq in iface.get_prerequisites ()) {
914
if (prereq.data_type is Class) {
915
has_instantiable_prereq = true;
920
if (!has_instantiable_prereq)
921
iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
923
// virtual method merging
924
foreach (Method m in vmethods) {
925
var symbol = iface.scope.lookup (m.name);
926
if (symbol == null) {
927
iface.add_method (m);
928
} else if (symbol is Signal) {
929
var sig = (Signal) symbol;
930
sig.is_virtual = true;
932
Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, iface.name));
937
foreach (Method m in methods) {
938
var symbol = iface.scope.lookup (m.name);
939
if (symbol == null) {
940
iface.add_method (m);
941
} else if (symbol is Signal) {
942
var sig = (Signal) symbol;
943
sig.has_emitter = true;
944
} else if (symbol is Method) {
945
// assume method is wrapper for virtual method
947
Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, iface.name));
951
handle_async_methods (iface);
953
end_element ("interface");
957
void handle_async_methods (ObjectTypeSymbol type_symbol) {
958
var methods = type_symbol.get_methods ();
959
for (int method_n = 0 ; method_n < methods.size ; method_n++) {
960
var m = methods.get (method_n);
963
string finish_method_base;
964
if (m.name.has_suffix ("_async")) {
965
finish_method_base = m.name.substring (0, m.name.length - "_async".length);
967
finish_method_base = m.name;
969
var finish_method = type_symbol.scope.lookup (finish_method_base + "_finish") as Method;
971
// check if the method is using non-standard finish method name
972
if (finish_method == null) {
973
var method_cname = m.get_finish_cname ();
974
foreach (Method method in type_symbol.get_methods ()) {
975
if (method.get_cname () == method_cname) {
976
finish_method = method;
982
if (finish_method != null) {
983
m.return_type = finish_method.return_type.copy ();
984
m.no_array_length = finish_method.no_array_length;
985
m.array_null_terminated = finish_method.array_null_terminated;
986
foreach (var param in finish_method.get_parameters ()) {
987
if (param.direction == ParameterDirection.OUT) {
988
var async_param = param.copy ();
989
if (m.scope.lookup (param.name) != null) {
990
// parameter name conflict
991
async_param.name += "_out";
993
m.add_parameter (async_param);
996
foreach (DataType error_type in finish_method.get_error_types ()) {
997
m.add_error_type (error_type.copy ());
999
if (methods.index_of (finish_method) < method_n) {
1002
type_symbol.scope.remove (finish_method.name);
1003
methods.remove (finish_method);
1009
Field parse_field () {
1010
start_element ("field");
1011
string name = reader.get_attribute ("name");
1012
string allow_none = reader.get_attribute ("allow-none");
1014
var type = parse_type ();
1015
var field = new Field (name, type, null, get_current_src ());
1016
field.access = SymbolAccessibility.PUBLIC;
1017
if (allow_none == "1") {
1018
type.nullable = true;
1020
end_element ("field");
1024
Property parse_property () {
1025
start_element ("property");
1026
string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
1027
string readable = reader.get_attribute ("readable");
1028
string writable = reader.get_attribute ("writable");
1029
string construct_ = reader.get_attribute ("construct");
1030
string construct_only = reader.get_attribute ("construct-only");
1032
var type = parse_type ();
1033
var prop = new Property (name, type, null, null, get_current_src ());
1034
prop.access = SymbolAccessibility.PUBLIC;
1035
if (readable != "0") {
1036
prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
1038
if (writable == "1" || construct_only == "1") {
1039
prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
1041
end_element ("property");
1045
Delegate parse_callback () {
1046
return this.parse_function ("callback") as Delegate;
1049
Method parse_constructor (string? parent_ctype = null) {
1050
start_element ("constructor");
1051
string name = reader.get_attribute ("name");
1052
string throws_string = reader.get_attribute ("throws");
1053
string cname = reader.get_attribute ("c:identifier");
1057
parse_return_value (out ctype);
1059
var m = new CreationMethod (null, name, get_current_src ());
1060
m.access = SymbolAccessibility.PUBLIC;
1061
m.has_construct_function = false;
1062
if (ctype != null && (parent_ctype == null || ctype != parent_ctype + "*")) {
1063
m.custom_return_type_cname = ctype;
1065
if (m.name == "new") {
1067
} else if (m.name.has_prefix ("new_")) {
1068
m.name = m.name.offset ("new_".length);
1070
if (cname != null) {
1071
m.set_cname (cname);
1073
if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
1074
start_element ("parameters");
1076
while (current_token == MarkupTokenType.START_ELEMENT) {
1077
m.add_parameter (parse_parameter ());
1079
end_element ("parameters");
1082
if (throws_string == "1") {
1083
m.add_error_type (new ErrorType (null, null));
1085
end_element ("constructor");
1090
public MethodInfo (FormalParameter param, int array_length_idx, int closure_idx, int destroy_idx) {
1092
this.array_length_idx = array_length_idx;
1093
this.closure_idx = closure_idx;
1094
this.destroy_idx = destroy_idx;
1095
this.vala_idx = 0.0F;
1099
public FormalParameter param;
1100
public float vala_idx;
1101
public int array_length_idx;
1102
public int closure_idx;
1103
public int destroy_idx;
1107
Symbol parse_function (string element_name) {
1108
start_element (element_name);
1109
string name = reader.get_attribute ("name");
1110
string cname = reader.get_attribute ("c:identifier");
1111
string throws_string = reader.get_attribute ("throws");
1112
string invoker = reader.get_attribute ("invoker");
1114
DataType return_type;
1115
if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
1116
return_type = parse_return_value ();
1118
return_type = new VoidType ();
1123
if (element_name == "callback") {
1124
s = new Delegate (name, return_type, get_current_src ());
1126
s = new Method (name, return_type, get_current_src ());
1129
s.access = SymbolAccessibility.PUBLIC;
1130
if (cname != null) {
1132
((Method) s).set_cname (cname);
1134
((Delegate) s).set_cname (cname);
1138
if (element_name == "virtual-method" || element_name == "callback") {
1140
((Method) s).is_virtual = true;
1143
if (invoker != null){
1146
} else if (element_name == "function") {
1147
((Method) s).binding = MemberBinding.STATIC;
1150
var parameters = new ArrayList<MethodInfo> ();
1151
var array_length_parameters = new ArrayList<int> ();
1152
var closure_parameters = new ArrayList<int> ();
1153
var destroy_parameters = new ArrayList<int> ();
1154
if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
1155
start_element ("parameters");
1158
while (current_token == MarkupTokenType.START_ELEMENT) {
1159
int array_length_idx, closure_idx, destroy_idx;
1161
var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope);
1162
if (array_length_idx != -1) {
1163
array_length_parameters.add (array_length_idx);
1165
if (closure_idx != -1) {
1166
closure_parameters.add (closure_idx);
1168
if (destroy_idx != -1) {
1169
destroy_parameters.add (destroy_idx);
1172
var info = new MethodInfo(param, array_length_idx, closure_idx, destroy_idx);
1174
if (s is Method && scope == "async") {
1175
var unresolved_type = param.variable_type as UnresolvedType;
1176
if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
1177
// GAsync-style method
1178
((Method) s).coroutine = true;
1183
parameters.add (info);
1185
end_element ("parameters");
1190
foreach (MethodInfo info in parameters) {
1191
if (s is Delegate && info.closure_idx == i) {
1192
var d = (Delegate) s;
1193
d.has_target = true;
1194
d.cinstance_parameter_position = (float) j - 0.1;
1196
} else if (info.keep
1197
&& !array_length_parameters.contains (i)
1198
&& !closure_parameters.contains (i)
1199
&& !destroy_parameters.contains (i)) {
1200
info.vala_idx = (float) j;
1203
/* interpolate for vala_idx between this and last*/
1204
float last_idx = 0.0F;
1206
last_idx = parameters[last].vala_idx;
1208
for (int k=last+1; k < i; k++) {
1209
parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
1215
// make sure that vala_idx is always set
1216
// the above if branch does not set vala_idx for
1217
// hidden parameters at the end of the parameter list
1218
info.vala_idx = (j - 1) + (i - last) * 0.1F;
1223
foreach (MethodInfo info in parameters) {
1226
/* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
1229
((Method) s).add_parameter (info.param);
1231
((Delegate) s).add_parameter (info.param);
1234
if (info.array_length_idx != -1) {
1235
if ((info.array_length_idx) >= parameters.size) {
1236
Report.error (get_current_src (), "invalid array_length index");
1239
info.param.carray_length_parameter_position = parameters[info.array_length_idx].vala_idx;
1240
info.param.set_array_length_cname (parameters[info.array_length_idx].param.name);
1242
if (info.param.variable_type is ArrayType && info.array_length_idx == -1) {
1243
info.param.no_array_length = true;
1246
if (info.closure_idx != -1) {
1247
if ((info.closure_idx) >= parameters.size) {
1248
Report.error (get_current_src (), "invalid closure index");
1251
info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
1253
if (info.destroy_idx != -1) {
1254
if (info.destroy_idx >= parameters.size) {
1255
Report.error (get_current_src (), "invalid destroy index");
1258
info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
1263
if (throws_string == "1") {
1264
s.add_error_type (new ErrorType (null, null));
1266
end_element (element_name);
1270
Method parse_method (string element_name) {
1271
return this.parse_function (element_name) as Method;
1274
Signal parse_signal () {
1275
start_element ("glib:signal");
1276
string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
1278
DataType return_type;
1279
if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
1280
return_type = parse_return_value ();
1282
return_type = new VoidType ();
1284
var sig = new Signal (name, return_type);
1285
sig.access = SymbolAccessibility.PUBLIC;
1286
sig.external = true;
1287
if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
1288
start_element ("parameters");
1290
while (current_token == MarkupTokenType.START_ELEMENT) {
1291
sig.add_parameter (parse_parameter ());
1293
end_element ("parameters");
1295
end_element ("glib:signal");
1299
Class parse_boxed () {
1300
string name = reader.get_attribute ("name");
1302
name = reader.get_attribute ("glib:name");
1304
var cl = new Class (name);
1305
cl.access = SymbolAccessibility.PUBLIC;
1307
cl.is_compact = true;
1309
string cname = reader.get_attribute ("c:type");
1310
if (cname != null) {
1311
cl.set_cname (cname);
1314
cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
1315
cl.set_free_function ("g_boxed_free");
1316
cl.set_dup_function ("g_boxed_copy");
1320
while (current_token == MarkupTokenType.START_ELEMENT) {
1321
if (reader.get_attribute ("introspectable") == "0") {
1326
if (reader.name == "field") {
1327
cl.add_field (parse_field ());
1328
} else if (reader.name == "constructor") {
1329
parse_constructor ();
1330
} else if (reader.name == "method") {
1331
cl.add_method (parse_method ("method"));
1334
Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
1339
if (current_token != MarkupTokenType.END_ELEMENT) {
1341
Report.error (get_current_src (), "expected end element");
1347
Struct parse_union () {
1348
start_element ("union");
1349
var st = new Struct (reader.get_attribute ("name"));
1350
st.access = SymbolAccessibility.PUBLIC;
1354
while (current_token == MarkupTokenType.START_ELEMENT) {
1355
if (reader.get_attribute ("introspectable") == "0") {
1360
if (reader.name == "field") {
1361
st.add_field (parse_field ());
1362
} else if (reader.name == "constructor") {
1363
parse_constructor ();
1364
} else if (reader.name == "method") {
1365
st.add_method (parse_method ("method"));
1366
} else if (reader.name == "record") {
1367
Struct s = parse_record ();
1368
var fs = s.get_fields ();
1369
foreach (var f in fs) {
1370
f.set_cname (s.get_cname () + "." + f.get_cname ());
1371
f.name = s.name + "_" + f.name;
1376
Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
1381
end_element ("union");
1385
Constant parse_constant () {
1386
start_element ("constant");
1387
string name = reader.get_attribute ("name");
1389
var type = parse_type ();
1390
var c = new Constant (name, type, null, get_current_src ());
1391
c.access = SymbolAccessibility.PUBLIC;
1393
end_element ("constant");
1397
public void parse_metadata (string metadata_filename) {
1398
if (FileUtils.test (metadata_filename, FileTest.EXISTS)) {
1401
FileUtils.get_contents (metadata_filename, out metadata, null);
1403
foreach (string line in metadata.split ("\n")) {
1404
if (line.has_prefix ("#")) {
1405
// ignore comment lines
1409
string[] tokens = line.split (" ", 2);
1411
if (null == tokens[0]) {
1415
foreach (string attribute in tokens[1].split (" ")) {
1416
string[] pair = attribute.split ("=", 2);
1417
if (pair[0] == null || pair[1] == null) {
1421
string key = "%s/@%s".printf (tokens[0], pair[0]);
1422
attributes_map.set (key, pair[1].substring (1, pair[1].length - 2));
1425
} catch (FileError e) {
1426
Report.error (null, "Unable to read metadata file: %s".printf (e.message));
1429
Report.error (null, "Metadata file `%s' not found".printf (metadata_filename));
1433
void add_package_name (string name) {
1434
if (package_names == null) {
1435
package_names = new string[0];
1438
foreach (var existing in package_names) {
1439
if (name == existing) {
1444
package_names += name;
1447
public string[]? get_package_names () {
1448
return package_names;