Fix #7250 - better handling of adding properties
[roobuilder] / src / Palete / VapiParser.vala
1
2
3  // valac -g  --pkg libvala-0.26  --pkg gee-1.0 --pkg json-glib-1.0  --pkg gtk+-3.0   VapiParser.vala Gir.vala GirObject.vala -o /tmp/vdoc
4
5 namespace Palete {
6          
7          public errordomain VapiParserError {
8                 PARSE_FAILED 
9         }
10          
11  
12         public class VapiParser : Vala.CodeVisitor {
13                 
14                 Vala.CodeContext context;
15                  
16                 Project.Gtk project;
17                 
18                 public VapiParser(Project.Gtk project) {
19                         base();
20                         this.project = project;
21                         // should not really happen..
22                         if (project.gir_cache == null) {
23                                 project.gir_cache =       new Gee.HashMap<string,Gir>();
24                         }
25                 }
26                  
27                 
28                 public override void visit_namespace (Vala.Namespace element) 
29                 {
30                         if (element == null) {
31                                 
32                                 return;
33                         }
34                          
35                         
36                         //print("parsing namespace %s\n", element.name);
37                         if (element.name == null) {
38                                 element.accept_children(this); // catch sub namespaces..
39                                 return;
40                         }
41                         this.add_namespace(null, element);
42                 }
43                 public void add_namespace(GirObject? parent, Vala.Namespace element)
44                 {
45                         
46                         
47                         var g = new GirObject("Package",element.name) ;
48                         if (parent == null) {
49                                 this.project.gir_cache.set(element.name, (Gir)g);
50                         } else {
51                                 // we add it as a class of the package.. even though its a namespace..
52                                 parent.classes.set(element.name, g);
53                         }
54                         
55                         
56                         foreach(var c in element.get_classes()) {
57                                 this.add_class(g, c);
58                         }
59                         foreach(var c in element.get_enums()) {
60                                 this.add_enum(g, c);
61                         }
62                         foreach(var c in element.get_interfaces()) {
63                                 this.add_interface(g, c);
64                         }
65                         foreach(var c in element.get_namespaces()) {
66                                 this.add_namespace(g, c);
67                         }
68                         foreach(var c in element.get_methods()) {
69                                 this.add_method(g, c);
70                         }
71                         element.accept_children(this); // catch sub namespaces..
72                         
73                         
74                 }
75                  
76                 
77                 public void add_enum(GirObject parent, Vala.Enum cls)
78                 {
79                 
80                         var c = new GirObject("Enum",   cls.name);
81                         parent.consts.set(cls.name, c);
82                         c.ns = parent.name;
83                         
84                         c.gparent = parent;
85                         
86                         foreach(var e in cls.get_values()) {
87                                 var em = new GirObject("EnumMember",e.name);
88                                 em.gparent = c;
89                                 em.ns = c.ns;
90                                 
91 #if VALA_0_56
92                                 em.type  = e.type_reference == null ||  e.type_reference.type_symbol == null ? "" : e.type_reference.type_symbol.get_full_name();                       
93 #elif VALA_0_36
94                                 em.type  = e.type_reference == null ||  e.type_reference.data_type == null ? "" : e.type_reference.data_type.get_full_name();
95 #endif                          
96                                 
97                                 
98                                 // unlikely to get value..
99                                 //c.value = element->get_prop("value");
100                                 c.consts.set(e.name,em);
101                         }
102                         
103                          
104                 }
105                 
106                 public void add_interface(GirObject parent, Vala.Interface cls)
107                 {
108                 
109                         var c = new GirObject("Interface", parent.name + "." + cls.name);
110                         parent.classes.set(cls.name, c);
111                         c.ns = parent.name;
112                         //c.parent = cls.base_class == null ? "" : cls.base_class.get_full_name() ;  // extends...
113                         c.gparent = parent;
114                         
115                         foreach(var p in cls.get_properties()) {
116                                 this.add_property(c, p);
117                         }
118                         // methods...
119                         foreach(var p in cls.get_signals()) {
120                                 this.add_signal(c, p);
121                         }
122                         
123                         foreach(var p in cls.get_methods()) {
124                                 // skip static methods..
125                                 if (p.binding != Vala.MemberBinding.INSTANCE &&
126                                         !(p is Vala.CreationMethod)
127                                 ) {
128                                         continue;
129                                 }
130                                 
131                                 this.add_method(c, p);
132                         }
133                         
134                         //if (cls.base_class != null) {
135                         //      c.inherits.add(cls.base_class.get_full_name());
136                         //}
137                         //foreach(var p in cls.get_base_types()) {
138                         //      if (p.data_type != null) {
139                         //              c.implements.add(p.data_type.get_full_name());
140                         //      }
141                         //}
142                           
143                         
144                         
145                          
146                 }
147                 
148                 public void add_class(GirObject parent, Vala.Class cls)
149                 {
150                 
151                         var c = new GirObject("Class", parent.name + "." + cls.name);
152                         parent.classes.set(cls.name, c);
153                         c.ns = parent.name;
154                         c.parent = cls.base_class == null ? "" : cls.base_class.get_full_name() ;  // extends...
155                         c.gparent = parent;
156                         c.is_abstract = cls.is_abstract;
157                         foreach(var p in cls.get_properties()) {
158                                 this.add_property(c, p);
159                         }
160                         // methods...
161                         foreach(var p in cls.get_signals()) {
162                                 this.add_signal(c, p);
163                         }
164                         
165                         foreach(var p in cls.get_methods()) {
166                                 // skip static methods..
167                                 if (p.binding != Vala.MemberBinding.INSTANCE &&
168                                         !(p is Vala.CreationMethod)
169                                 ) {
170                                         continue;
171                                 }
172                                 
173                                 this.add_method(c, p);
174                         }
175                         
176                         if (cls.base_class != null) {
177                                 c.inherits.add(cls.base_class.get_full_name());
178                         }
179                         foreach(var p in cls.get_base_types()) {
180 #if VALA_0_56
181                                 if (p.type_symbol != null) {
182                                         c.implements.add(p.type_symbol.get_full_name());
183                                 }
184 #elif VALA_0_36
185                                 if (p.data_type != null) {
186                                         c.implements.add(p.data_type.get_full_name());
187                                 }
188
189 #endif                          
190                                  
191                         }
192                         
193                         if (cls.version.deprecated) { 
194                                 GLib.debug("class %s is deprecated", c.name);
195                                 c.is_deprecated = true;
196                         }
197                         
198                 }
199                 
200                 public GirObject? fqn_to_cls(string fqn)
201                 {
202                         var ar = fqn.split(".");
203                         var pkg = this.project.gir_cache.get(ar[0]);
204                         var cls = pkg != null ? pkg.classes.get(ar[1]) : null;
205                         return cls;
206                 }
207                 
208                 public void augment_inherits_for(GirObject cls, Gee.ArrayList<string> to_check, bool is_top)
209                 {
210                         foreach (var chk_cls in to_check) {
211                                 if (!cls.inherits.contains(chk_cls)) { 
212                                         cls.inherits.add(chk_cls);
213                                         
214                                 } else {
215                                         if (!is_top) {
216                                                 continue;
217                                         }
218                                 }
219                                 
220                                 
221                                 var subcls = this.fqn_to_cls(chk_cls);
222                                 if (subcls == null) {
223                                         continue;
224                                 }
225                                 this.augment_inherits_for(cls, subcls.inherits, false);
226                                 this.augment_implements_for(cls, subcls.implements);
227                         }
228                 
229                 }
230                 public void augment_implements_for(GirObject cls, Gee.ArrayList<string> to_check)
231                 {
232                         foreach (var chk_cls in to_check) {
233                                 if (cls.implements.contains(chk_cls)) { 
234                                         continue;
235                                 }
236                                 cls.implements.add(chk_cls);
237                                 
238                                 var subcls = this.fqn_to_cls(chk_cls);
239                                 if (subcls == null) {
240                                         continue;
241                                 }
242                                 this.augment_implements_for(cls, subcls.implements);
243
244                         }
245                 
246                 }
247                 
248                 // this might miss out interfaces of child classes?
249                 public void augment_all_inheritence()
250                 {
251                         // this works out all the children...
252                         foreach(var pkgname in this.project.gir_cache.keys) {
253                         
254                                 var pkg = this.project.gir_cache.get(pkgname);
255                                 foreach (var clsname in pkg.classes.keys) {
256                                         var cls = pkg.classes.get(clsname);
257                                         this.augment_inherits_for(cls, cls.inherits, true);
258                                         this.augment_implements_for(cls, cls.implements);
259                                 }
260                         }
261                         // now do the implementations
262                         foreach(var pkgname in this.project.gir_cache.keys) {
263                         
264                                 var pkg = this.project.gir_cache.get(pkgname);
265                                 foreach (var clsname in pkg.classes.keys) {
266                                         var cls = pkg.classes.get(clsname);
267                                         foreach(var parentname in cls.inherits) {
268                                                 var parent =  this.fqn_to_cls(parentname);
269                                                 if (parent == null) { 
270                                                         continue;
271                                                 }
272                                                 if (parent.implementations.contains(cls.fqn())) {
273                                                         continue;
274                                                 }
275                                                 parent.implementations.add(cls.fqn());
276                                         
277                                         }
278                                         foreach(var parentname in cls.implements) {
279                                                 var parent =  this.fqn_to_cls(parentname);
280                                                 if (parent == null) { 
281                                                         continue;
282                                                 }
283                                                 if (parent.implementations.contains(cls.fqn())) {
284                                                         continue;
285                                                 }
286                                                 parent.implementations.add(cls.fqn());
287                                         
288                                         }
289                                 }
290                         }
291                         
292                                 
293                 }
294                 
295                  
296                 
297                 public void add_property(GirObject parent, Vala.Property prop)
298                 {
299                         var c = new GirObject("Prop",prop.name);
300                         c.gparent = parent;
301                         c.ns = parent.ns;
302                         c.propertyof = parent.name;
303 #if VALA_0_56
304                         c.type  = prop.property_type.type_symbol == null ? "" : prop.property_type.type_symbol.get_full_name();
305 #elif VALA_0_36
306                         c.type  = prop.property_type.data_type == null ? "" : prop.property_type.data_type.get_full_name();             
307 #endif
308                         c.is_readable = prop.get_accessor != null ?  prop.get_accessor.readable : false;
309                         c.is_writable = prop.get_accessor != null ?  prop.get_accessor.writable : false;
310                         if (prop.version.deprecated) { 
311                                 GLib.debug("class %s is deprecated", c.name);
312                                 c.is_deprecated = true;
313                         }
314                         parent.props.set(prop.name,c);
315
316                         
317                 }
318                 public void add_signal(GirObject parent, Vala.Signal sig)
319                 {
320                         var c = new GirObject("Signal",sig.name);
321                         c.gparent = parent;
322                         c.ns = parent.ns;
323
324 #if VALA_0_56
325                         var dt  = sig.return_type.type_symbol  ;
326 #elif VALA_0_36
327                         var dt  = sig.return_type.data_type;
328 #endif                  
329                          
330                         
331                         var retval = "";
332                         
333                         if (dt != null) {
334                                 //print("creating return type on signal %s\n", sig.name);
335                                 var cc = new GirObject("Return", "return-value");
336                                 cc.gparent = c;
337                                 cc.ns = c.ns;
338                                 cc.type  =  dt.get_full_name();
339                                 c.return_value = cc;
340                                 
341                                  retval = "\treturn " + cc.type +";";
342                         }
343                         parent.signals.set(sig.name,c);
344                         
345                         var params =  sig.get_parameters() ;
346                         if (params.size < 1) {
347                         
348                                 c.sig = "( ) => {\n\n"+ retval + "\n}\n";
349                         
350                                 return;
351                         }
352                         var cc = new GirObject("Paramset",sig.name); // what's the name on this?
353                         cc.gparent = c;
354                         cc.ns = c.ns;
355                         c.paramset = cc;
356                         
357                         var args = "";                  
358                         foreach(var p in params) {
359                                 this.add_param(cc, p);
360                                 args += args.length > 0 ? ", " : "";
361                                 args += p.name;
362                         }
363                         // add c.sig -> this is the empty 
364                         c.sig = "(" + args + ") => {\n\n"+ retval + "\n}\n";
365                         
366                         
367                         
368                 }       
369                 
370                 public void add_method(GirObject parent, Vala.Method met)
371                 {
372                         var n = met.name == null ? "" : met.name;
373                         var ty  = "Method";
374                         if (met is Vala.CreationMethod) {
375                                 ty = "Ctor";
376                                 if(n == "" || n == ".new") {
377                                         n = "new";
378                                 }
379                                 
380                         }
381                         //print("add_method :  %s\n", n);
382                         
383                         var c = new GirObject(ty,n);
384                         c.gparent = parent;
385                         c.ns = parent.ns;
386 #if VALA_0_56                                           
387                         if (met.return_type.type_symbol != null) {
388 #elif VALA_0_36
389                         if (met.return_type.data_type != null) {
390 #endif  
391                         
392                         
393                                 //print("creating return type on method %s\n", met.name);
394                                 var cc = new GirObject("Return", "return-value");
395                                 cc.gparent = c;
396                                 cc.ns = c.ns;
397
398 #if VALA_0_56                   
399                                 cc.type  =  met.return_type.type_symbol.get_full_name();
400 #elif VALA_0_36
401                                 cc.type  =  met.return_type.data_type.get_full_name();
402 #endif  
403                                 
404                                 
405                                 
406                                 c.return_value = cc;
407                         }
408                         if (met is Vala.CreationMethod) {
409                                 parent.ctors.set(c.name,c);
410                         } else {
411                                 parent.methods.set(met.name,c);
412                         }
413                         
414                         var params =  met.get_parameters() ;
415                         if (params.size < 1) {
416                                 return;
417                         }
418                         var cc = new GirObject("Paramset",met.name); // what's the name on this?
419                         cc.gparent = c;
420                         cc.ns = c.ns;
421                         c.paramset = cc;
422                         c.sig = "(";
423                         
424                         foreach(var p in params) {
425                                 if (p.name == null && !p.ellipsis) {
426                                         continue;
427                                 }
428                                 var pp = this.add_param(cc, p);
429                                 c.sig += (c.sig == "(" ? "" : ",");
430                                 c.sig += " " + (pp.direction == "in" ? "" : pp.direction) + " " + pp.type + " " + pp.name;
431                         }
432                         c.sig += (c.sig == "(" ? ")" : " )");
433                         
434                 }
435                 
436                 public GirObject add_param(GirObject parent, Vala.Parameter pam)
437                 {
438                         
439                         var n = pam.name;
440                         if (pam.ellipsis) {
441                                 n = "___";
442                         }
443                         var c = new GirObject("Param",n);
444                         c.gparent = parent;
445                         c.ns = parent.ns;
446                         c.direction = "??";
447                         switch (pam.direction) {
448                                 case Vala.ParameterDirection.IN:
449                                         c.direction = "in";
450                                         break;
451                                 case Vala.ParameterDirection.OUT:
452                                         c.direction = "out";
453                                         break;
454                                 case Vala.ParameterDirection.REF:
455                                         c.direction = "ref";
456                                         break;
457                         }
458                         
459                         parent.params.add(c);
460                         
461                         if (!pam.ellipsis) {
462 #if VALA_0_56                   
463                                 c.type = pam.variable_type.type_symbol == null ? "" : pam.variable_type.type_symbol.get_full_name();
464 #elif VALA_0_36
465                                 c.type = pam.variable_type.data_type == null ? "" : pam.variable_type.data_type.get_full_name();
466 #endif                          
467                         }
468                         Gir.checkParamOverride(c); 
469                         return c;
470                         
471                 }
472                 
473                 public void create_valac_tree( )
474                 {
475                         // init context:
476                         context = new Vala.CodeContext ();
477                         Vala.CodeContext.push (context);
478                 
479                         context.experimental = false;
480                         context.experimental_non_null = false;
481 #if VALA_0_56
482                         var ver=56;
483 #elif VALA_0_36
484                         var ver=36;
485 #endif
486                         
487                         for (int i = 2; i <= ver; i += 2) {
488                                 context.add_define ("VALA_0_%d".printf (i));
489                         }
490                         
491                          
492                         //var vapidirs = ((Project.Gtk)this.file.project).vapidirs();
493                         // what's the current version of vala???
494                         
495                         
496                         //vapidirs +=  Path.get_dirname (context.get_vapi_path("glib-2.0")) ;
497                          
498                         var vapidirs = context.vapi_directories;
499                         
500                         vapidirs += (BuilderApplication.configDirectory() + "/resources/vapi");
501                         vapidirs += "/usr/share/vala-0.%d/vapi".printf(ver);
502                         vapidirs += "/usr/share/vala/vapi";
503                         context.vapi_directories = vapidirs;
504                         
505                         // or context.get_vapi_path("glib-2.0"); // should return path..
506                         //context.vapi_directories = vapidirs;
507                         context.report.enable_warnings = true;
508                         context.metadata_directories = { };
509                         context.gir_directories = {};
510                         //context.thread = true; 
511                         
512                         
513                         //this.report = new ValaSourceReport(this.file);
514                         //context.report = this.report;
515                         
516                         
517                         context.basedir = "/tmp"; //Posix.realpath (".");
518                 
519                         context.directory = context.basedir;
520                 
521
522                         // add default packages:
523                         //if (settings.profile == "gobject-2.0" || settings.profile == "gobject" || settings.profile == null) {
524 #if VALA_0_56
525                         context.set_target_profile (Vala.Profile.GOBJECT);
526 #elif VALA_0_36
527                         context.profile = Vala.Profile.GOBJECT;
528 #endif
529                          
530                         var ns_ref = new Vala.UsingDirective (new Vala.UnresolvedSymbol (null, "GLib", null));
531                         context.root.add_using_directive (ns_ref);
532                         
533                         
534                         context.add_external_package ("glib-2.0"); 
535                         context.add_external_package ("gobject-2.0");
536                         // user defined ones..
537                         
538                 var dcg = this.project.compilegroups.get("_default_");
539                 for (var i = 0; i < dcg.packages.size; i++) {
540                 
541                         var pkg = dcg.packages.get(i);
542                         // do not add libvala versions except the one that matches the one we are compiled against..
543                         if (Regex.match_simple("^libvala", pkg) && pkg != ("libvala-0." + ver.to_string())) {
544                                 continue;
545                         }
546                                 //valac += " --pkg " + dcg.packages.get(i);
547                                  if (!this.has_vapi(context.vapi_directories, dcg.packages.get(i))) {
548                                  
549                                         continue;
550                                 }
551                                 GLib.debug("ADD vapi '%s'", dcg.packages.get(i));
552                                 context.add_external_package (dcg.packages.get(i));
553                         }                       
554                         
555                         
556                         
557                          
558                         // core packages we are interested in for the builder..
559                         // some of these may fail... - we probalby need a better way to handle this..
560                         /*
561                         context.add_external_package ("gtk+-3.0");
562                         context.add_external_package ("libsoup-2.4");
563                         if (!context.add_external_package ("webkit2gtk-4.0")) {
564                                 context.add_external_package ("webkit2gtk-3.0");
565                         }
566                         // these are supposed to be in the 'deps' file, but it's not getting read..
567                         context.add_external_package ("cogl-1.0");
568                         context.add_external_package ("json-glib-1.0");
569                         context.add_external_package ("clutter-gtk-1.0");
570
571
572                     
573                         context.add_external_package ("gdl-3.0");
574                         context.add_external_package ("gtksourceview-3.0");
575                         context.add_external_package ("vte-2.90"); //??? -- hopefullly that works..
576                         */
577                         //add_documented_files (context, settings.source_files);
578                 
579                         Vala.Parser parser = new Vala.Parser ();
580                         parser.parse (context);
581                         //gir_parser.parse (context);
582                         if (context.report.get_errors () > 0) {
583                                 
584                                 //throw new VapiParserError.PARSE_FAILED("failed parse VAPIS, so we can not write file correctly");
585                                 
586                                 print("parse got errors");
587                                  
588                                 
589                                 Vala.CodeContext.pop ();
590                                 return ;
591                         }
592
593
594                         
595                         // check context:
596                         context.check ();
597                         if (context.report.get_errors () > 0) {
598                                 GLib.error("failed check VAPIS, so we can not write file correctly");
599                                 // throw new VapiParserError.PARSE_FAILED("failed check VAPIS, so we can not write file correctly");
600                                 Vala.CodeContext.pop ();
601                                  
602                                 return;
603                                 
604                         }
605                          
606                         
607                          
608                         context.accept(this);
609                         
610                         context = null;
611                         // dump the tree for Gtk?
612                         
613                         Vala.CodeContext.pop ();
614                         
615                         
616                         this.augment_all_inheritence();
617                         
618                         
619                         
620                         print("ALL OK?\n");
621                  
622                 }
623         //
624                 // startpoint:
625                 //
626          public bool has_vapi(string[] dirs,  string vapi) 
627                 {
628                         for(var i =0 ; i < dirs.length; i++) {
629                                 GLib.debug("check VAPI - %s", dirs[i] + "/" + vapi + ".vapi");
630                                 if (!FileUtils.test( dirs[i] + "/" + vapi + ".vapi", FileTest.EXISTS)) {
631                                         continue;
632                                 }   
633                                 return true;
634                         }
635                         return false;
636                         
637                 }
638         }
639 }
640  /*
641 int main (string[] args) {
642         
643         var g = Palete.Gir.factoryFqn("Gtk.SourceView");
644         print("%s\n", g.asJSONString());
645         
646         return 0;
647 }
648  
649
650 */