1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _json.d)
9  */
10 
11 module ddmd.json;
12 
13 import core.stdc.stdio;
14 import core.stdc..string;
15 import ddmd.aggregate;
16 import ddmd.arraytypes;
17 import ddmd.attrib;
18 import ddmd.dclass;
19 import ddmd.declaration;
20 import ddmd.denum;
21 import ddmd.dimport;
22 import ddmd.dmodule;
23 import ddmd.dsymbol;
24 import ddmd.dtemplate;
25 import ddmd.expression;
26 import ddmd.func;
27 import ddmd.globals;
28 import ddmd.hdrgen;
29 import ddmd.id;
30 import ddmd.identifier;
31 import ddmd.mtype;
32 import ddmd.root.outbuffer;
33 import ddmd.visitor;
34 
35 extern (C++) final class ToJsonVisitor : Visitor
36 {
37     alias visit = super.visit;
38 public:
39     OutBuffer* buf;
40     int indentLevel;
41     const(char)* filename;
42 
43     extern (D) this(OutBuffer* buf)
44     {
45         this.buf = buf;
46     }
47 
48     void indent()
49     {
50         if (buf.offset >= 1 && buf.data[buf.offset - 1] == '\n')
51             for (int i = 0; i < indentLevel; i++)
52                 buf.writeByte(' ');
53     }
54 
55     void removeComma()
56     {
57         if (buf.offset >= 2 && buf.data[buf.offset - 2] == ',' && (buf.data[buf.offset - 1] == '\n' || buf.data[buf.offset - 1] == ' '))
58             buf.offset -= 2;
59     }
60 
61     void comma()
62     {
63         if (indentLevel > 0)
64             buf.writestring(",\n");
65     }
66 
67     void stringStart()
68     {
69         buf.writeByte('\"');
70     }
71 
72     void stringEnd()
73     {
74         buf.writeByte('\"');
75     }
76 
77     void stringPart(const(char)* s)
78     {
79         for (; *s; s++)
80         {
81             char c = cast(char)*s;
82             switch (c)
83             {
84             case '\n':
85                 buf.writestring("\\n");
86                 break;
87             case '\r':
88                 buf.writestring("\\r");
89                 break;
90             case '\t':
91                 buf.writestring("\\t");
92                 break;
93             case '\"':
94                 buf.writestring("\\\"");
95                 break;
96             case '\\':
97                 buf.writestring("\\\\");
98                 break;
99             case '\b':
100                 buf.writestring("\\b");
101                 break;
102             case '\f':
103                 buf.writestring("\\f");
104                 break;
105             default:
106                 if (c < 0x20)
107                     buf.printf("\\u%04x", c);
108                 else
109                 {
110                     // Note that UTF-8 chars pass through here just fine
111                     buf.writeByte(c);
112                 }
113                 break;
114             }
115         }
116     }
117 
118     // Json value functions
119     /*********************************
120      * Encode string into buf, and wrap it in double quotes.
121      */
122     void value(const(char)* s)
123     {
124         stringStart();
125         stringPart(s);
126         stringEnd();
127     }
128 
129     void value(int value)
130     {
131         buf.printf("%d", value);
132     }
133 
134     void valueBool(bool value)
135     {
136         buf.writestring(value ? "true" : "false");
137     }
138 
139     /*********************************
140      * Item is an intented value and a comma, for use in arrays
141      */
142     void item(const(char)* s)
143     {
144         indent();
145         value(s);
146         comma();
147     }
148 
149     void item(int i)
150     {
151         indent();
152         value(i);
153         comma();
154     }
155 
156     void itemBool(bool b)
157     {
158         indent();
159         valueBool(b);
160         comma();
161     }
162 
163     // Json array functions
164     void arrayStart()
165     {
166         indent();
167         buf.writestring("[\n");
168         indentLevel++;
169     }
170 
171     void arrayEnd()
172     {
173         indentLevel--;
174         removeComma();
175         if (buf.offset >= 2 && buf.data[buf.offset - 2] == '[' && buf.data[buf.offset - 1] == '\n')
176             buf.offset -= 1;
177         else if (!(buf.offset >= 1 && buf.data[buf.offset - 1] == '['))
178         {
179             buf.writestring("\n");
180             indent();
181         }
182         buf.writestring("]");
183         comma();
184     }
185 
186     // Json object functions
187     void objectStart()
188     {
189         indent();
190         buf.writestring("{\n");
191         indentLevel++;
192     }
193 
194     void objectEnd()
195     {
196         indentLevel--;
197         removeComma();
198         if (buf.offset >= 2 && buf.data[buf.offset - 2] == '{' && buf.data[buf.offset - 1] == '\n')
199             buf.offset -= 1;
200         else
201         {
202             buf.writestring("\n");
203             indent();
204         }
205         buf.writestring("}");
206         comma();
207     }
208 
209     // Json object property functions
210     void propertyStart(const(char)* name)
211     {
212         indent();
213         value(name);
214         buf.writestring(" : ");
215     }
216 
217     void property(const(char)* name, const(char)* s)
218     {
219         if (s is null)
220             return;
221         propertyStart(name);
222         value(s);
223         comma();
224     }
225 
226     void property(const(char)* name, int i)
227     {
228         propertyStart(name);
229         value(i);
230         comma();
231     }
232 
233     void propertyBool(const(char)* name, bool b)
234     {
235         propertyStart(name);
236         valueBool(b);
237         comma();
238     }
239 
240     void property(const(char)* name, TRUST trust)
241     {
242         switch (trust)
243         {
244         case TRUSTdefault:
245             // Should not be printed
246             //property(name, "default");
247             break;
248         case TRUSTsystem:
249             property(name, "system");
250             break;
251         case TRUSTtrusted:
252             property(name, "trusted");
253             break;
254         case TRUSTsafe:
255             property(name, "safe");
256             break;
257         default:
258             assert(false);
259         }
260     }
261 
262     void property(const(char)* name, PURE purity)
263     {
264         switch (purity)
265         {
266         case PUREimpure:
267             // Should not be printed
268             //property(name, "impure");
269             break;
270         case PUREweak:
271             property(name, "weak");
272             break;
273         case PUREconst:
274             property(name, "const");
275             break;
276         case PUREstrong:
277             property(name, "strong");
278             break;
279         case PUREfwdref:
280             property(name, "fwdref");
281             break;
282         default:
283             assert(false);
284         }
285     }
286 
287     void property(const(char)* name, LINK linkage)
288     {
289         switch (linkage)
290         {
291         case LINKdefault:
292             // Should not be printed
293             //property(name, "default");
294             break;
295         case LINKd:
296             // Should not be printed
297             //property(name, "d");
298             break;
299         case LINKc:
300             property(name, "c");
301             break;
302         case LINKcpp:
303             property(name, "cpp");
304             break;
305         case LINKwindows:
306             property(name, "windows");
307             break;
308         case LINKpascal:
309             property(name, "pascal");
310             break;
311         default:
312             assert(false);
313         }
314     }
315 
316     void propertyStorageClass(const(char)* name, StorageClass stc)
317     {
318         stc &= STCStorageClass;
319         if (stc)
320         {
321             propertyStart(name);
322             arrayStart();
323             while (stc)
324             {
325                 const(char)* p = stcToChars(stc);
326                 assert(p);
327                 item(p);
328             }
329             arrayEnd();
330         }
331     }
332 
333     void property(const(char)* linename, const(char)* charname, Loc* loc)
334     {
335         if (loc)
336         {
337             const(char)* filename = loc.filename;
338             if (filename)
339             {
340                 if (!this.filename || strcmp(filename, this.filename))
341                 {
342                     this.filename = filename;
343                     property("file", filename);
344                 }
345             }
346             if (loc.linnum)
347             {
348                 property(linename, loc.linnum);
349                 if (loc.charnum)
350                     property(charname, loc.charnum);
351             }
352         }
353     }
354 
355     void property(const(char)* name, Type type)
356     {
357         if (type)
358         {
359             property(name, type.toChars());
360         }
361     }
362 
363     void property(const(char)* name, const(char)* deconame, Type type)
364     {
365         if (type)
366         {
367             if (type.deco)
368                 property(deconame, type.deco);
369             else
370                 property(name, type.toChars());
371         }
372     }
373 
374     void property(const(char)* name, Parameters* parameters)
375     {
376         if (parameters is null || parameters.dim == 0)
377             return;
378         propertyStart(name);
379         arrayStart();
380         if (parameters)
381         {
382             for (size_t i = 0; i < parameters.dim; i++)
383             {
384                 Parameter p = (*parameters)[i];
385                 objectStart();
386                 if (p.ident)
387                     property("name", p.ident.toChars());
388                 property("type", "deco", p.type);
389                 propertyStorageClass("storageClass", p.storageClass);
390                 if (p.defaultArg)
391                     property("default", p.defaultArg.toChars());
392                 objectEnd();
393             }
394         }
395         arrayEnd();
396     }
397 
398     /* ========================================================================== */
399     void jsonProperties(Dsymbol s)
400     {
401         if (s.isModule())
402             return;
403         if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
404         {
405             property("name", s.toChars());
406             property("kind", s.kind());
407         }
408         if (s.prot().kind != PROTpublic) // TODO: How about package(names)?
409             property("protection", protectionToChars(s.prot().kind));
410         if (EnumMember em = s.isEnumMember())
411         {
412             if (em.origValue)
413                 property("value", em.origValue.toChars());
414         }
415         property("comment", s.comment);
416         property("line", "char", &s.loc);
417     }
418 
419     void jsonProperties(Declaration d)
420     {
421         jsonProperties(cast(Dsymbol)d);
422         propertyStorageClass("storageClass", d.storage_class);
423         property("type", "deco", d.type);
424         // Emit originalType if it differs from type
425         if (d.type != d.originalType && d.originalType)
426         {
427             const(char)* ostr = d.originalType.toChars();
428             if (d.type)
429             {
430                 const(char)* tstr = d.type.toChars();
431                 if (strcmp(tstr, ostr))
432                 {
433                     //printf("tstr = %s, ostr = %s\n", tstr, ostr);
434                     property("originalType", ostr);
435                 }
436             }
437             else
438                 property("originalType", ostr);
439         }
440     }
441 
442     void jsonProperties(TemplateDeclaration td)
443     {
444         jsonProperties(cast(Dsymbol)td);
445         if (td.onemember && td.onemember.isCtorDeclaration())
446             property("name", "this"); // __ctor -> this
447         else
448             property("name", td.ident.toChars()); // Foo(T) -> Foo
449     }
450 
451     /* ========================================================================== */
452     override void visit(Dsymbol s)
453     {
454     }
455 
456     override void visit(Module s)
457     {
458         objectStart();
459         if (s.md)
460             property("name", s.md.toChars());
461         property("kind", s.kind());
462         filename = s.srcfile.toChars();
463         property("file", filename);
464         property("comment", s.comment);
465         propertyStart("members");
466         arrayStart();
467         for (size_t i = 0; i < s.members.dim; i++)
468         {
469             (*s.members)[i].accept(this);
470         }
471         arrayEnd();
472         objectEnd();
473     }
474 
475     override void visit(Import s)
476     {
477         if (s.id == Id.object)
478             return;
479         objectStart();
480         propertyStart("name");
481         stringStart();
482         if (s.packages && s.packages.dim)
483         {
484             for (size_t i = 0; i < s.packages.dim; i++)
485             {
486                 Identifier pid = (*s.packages)[i];
487                 stringPart(pid.toChars());
488                 buf.writeByte('.');
489             }
490         }
491         stringPart(s.id.toChars());
492         stringEnd();
493         comma();
494         property("kind", s.kind());
495         property("comment", s.comment);
496         property("line", "char", &s.loc);
497         if (s.prot().kind != PROTpublic)
498             property("protection", protectionToChars(s.prot().kind));
499         if (s.aliasId)
500             property("alias", s.aliasId.toChars());
501         bool hasRenamed = false;
502         bool hasSelective = false;
503         for (size_t i = 0; i < s.aliases.dim; i++)
504         {
505             // avoid empty "renamed" and "selective" sections
506             if (hasRenamed && hasSelective)
507                 break;
508             else if (s.aliases[i])
509                 hasRenamed = true;
510             else
511                 hasSelective = true;
512         }
513         if (hasRenamed)
514         {
515             // import foo : alias1 = target1;
516             propertyStart("renamed");
517             objectStart();
518             for (size_t i = 0; i < s.aliases.dim; i++)
519             {
520                 Identifier name = s.names[i];
521                 Identifier _alias = s.aliases[i];
522                 if (_alias)
523                     property(_alias.toChars(), name.toChars());
524             }
525             objectEnd();
526         }
527         if (hasSelective)
528         {
529             // import foo : target1;
530             propertyStart("selective");
531             arrayStart();
532             for (size_t i = 0; i < s.names.dim; i++)
533             {
534                 Identifier name = s.names[i];
535                 if (!s.aliases[i])
536                     item(name.toChars());
537             }
538             arrayEnd();
539         }
540         objectEnd();
541     }
542 
543     override void visit(AttribDeclaration d)
544     {
545         Dsymbols* ds = d.include(null, null);
546         if (ds)
547         {
548             for (size_t i = 0; i < ds.dim; i++)
549             {
550                 Dsymbol s = (*ds)[i];
551                 s.accept(this);
552             }
553         }
554     }
555 
556     override void visit(ConditionalDeclaration d)
557     {
558         if (d.condition.inc)
559         {
560             visit(cast(AttribDeclaration)d);
561         }
562     }
563 
564     override void visit(TypeInfoDeclaration d)
565     {
566     }
567 
568     override void visit(PostBlitDeclaration d)
569     {
570     }
571 
572     override void visit(Declaration d)
573     {
574         objectStart();
575         //property("unknown", "declaration");
576         jsonProperties(d);
577         objectEnd();
578     }
579 
580     override void visit(AggregateDeclaration d)
581     {
582         objectStart();
583         jsonProperties(d);
584         ClassDeclaration cd = d.isClassDeclaration();
585         if (cd)
586         {
587             if (cd.baseClass && cd.baseClass.ident != Id.Object)
588             {
589                 property("base", cd.baseClass.toPrettyChars(true));
590             }
591             if (cd.interfaces.length)
592             {
593                 propertyStart("interfaces");
594                 arrayStart();
595                 foreach (b; cd.interfaces)
596                 {
597                     item(b.sym.toPrettyChars(true));
598                 }
599                 arrayEnd();
600             }
601         }
602         if (d.members)
603         {
604             propertyStart("members");
605             arrayStart();
606             for (size_t i = 0; i < d.members.dim; i++)
607             {
608                 Dsymbol s = (*d.members)[i];
609                 s.accept(this);
610             }
611             arrayEnd();
612         }
613         objectEnd();
614     }
615 
616     override void visit(FuncDeclaration d)
617     {
618         objectStart();
619         jsonProperties(d);
620         TypeFunction tf = cast(TypeFunction)d.type;
621         if (tf && tf.ty == Tfunction)
622             property("parameters", tf.parameters);
623         property("endline", "endchar", &d.endloc);
624         if (d.foverrides.dim)
625         {
626             propertyStart("overrides");
627             arrayStart();
628             for (size_t i = 0; i < d.foverrides.dim; i++)
629             {
630                 FuncDeclaration fd = d.foverrides[i];
631                 item(fd.toPrettyChars());
632             }
633             arrayEnd();
634         }
635         if (d.fdrequire)
636         {
637             propertyStart("in");
638             d.fdrequire.accept(this);
639         }
640         if (d.fdensure)
641         {
642             propertyStart("out");
643             d.fdensure.accept(this);
644         }
645         objectEnd();
646     }
647 
648     override void visit(TemplateDeclaration d)
649     {
650         objectStart();
651         // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one
652         property("kind", "template");
653         jsonProperties(d);
654         propertyStart("parameters");
655         arrayStart();
656         for (size_t i = 0; i < d.parameters.dim; i++)
657         {
658             TemplateParameter s = (*d.parameters)[i];
659             objectStart();
660             property("name", s.ident.toChars());
661             TemplateTypeParameter type = s.isTemplateTypeParameter();
662             if (type)
663             {
664                 if (s.isTemplateThisParameter())
665                     property("kind", "this");
666                 else
667                     property("kind", "type");
668                 property("type", "deco", type.specType);
669                 property("default", "defaultDeco", type.defaultType);
670             }
671             TemplateValueParameter value = s.isTemplateValueParameter();
672             if (value)
673             {
674                 property("kind", "value");
675                 property("type", "deco", value.valType);
676                 if (value.specValue)
677                     property("specValue", value.specValue.toChars());
678                 if (value.defaultValue)
679                     property("defaultValue", value.defaultValue.toChars());
680             }
681             TemplateAliasParameter _alias = s.isTemplateAliasParameter();
682             if (_alias)
683             {
684                 property("kind", "alias");
685                 property("type", "deco", _alias.specType);
686                 if (_alias.specAlias)
687                     property("specAlias", _alias.specAlias.toChars());
688                 if (_alias.defaultAlias)
689                     property("defaultAlias", _alias.defaultAlias.toChars());
690             }
691             TemplateTupleParameter tuple = s.isTemplateTupleParameter();
692             if (tuple)
693             {
694                 property("kind", "tuple");
695             }
696             objectEnd();
697         }
698         arrayEnd();
699         Expression expression = d.constraint;
700         if (expression)
701         {
702             property("constraint", expression.toChars());
703         }
704         propertyStart("members");
705         arrayStart();
706         for (size_t i = 0; i < d.members.dim; i++)
707         {
708             Dsymbol s = (*d.members)[i];
709             s.accept(this);
710         }
711         arrayEnd();
712         objectEnd();
713     }
714 
715     override void visit(EnumDeclaration d)
716     {
717         if (d.isAnonymous())
718         {
719             if (d.members)
720             {
721                 for (size_t i = 0; i < d.members.dim; i++)
722                 {
723                     Dsymbol s = (*d.members)[i];
724                     s.accept(this);
725                 }
726             }
727             return;
728         }
729         objectStart();
730         jsonProperties(d);
731         property("base", "baseDeco", d.memtype);
732         if (d.members)
733         {
734             propertyStart("members");
735             arrayStart();
736             for (size_t i = 0; i < d.members.dim; i++)
737             {
738                 Dsymbol s = (*d.members)[i];
739                 s.accept(this);
740             }
741             arrayEnd();
742         }
743         objectEnd();
744     }
745 
746     override void visit(EnumMember s)
747     {
748         objectStart();
749         jsonProperties(cast(Dsymbol)s);
750         property("type", "deco", s.origType);
751         objectEnd();
752     }
753 
754     override void visit(VarDeclaration d)
755     {
756         objectStart();
757         jsonProperties(d);
758         if (d._init)
759             property("init", d._init.toChars());
760         if (d.isField())
761             property("offset", d.offset);
762         if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT)
763             property("align", d.alignment);
764         objectEnd();
765     }
766 
767     override void visit(TemplateMixin d)
768     {
769         objectStart();
770         jsonProperties(d);
771         objectEnd();
772     }
773 }
774 
775 extern (C++) void json_generate(OutBuffer* buf, Modules* modules)
776 {
777     scope ToJsonVisitor json = new ToJsonVisitor(buf);
778     json.arrayStart();
779     for (size_t i = 0; i < modules.dim; i++)
780     {
781         Module m = (*modules)[i];
782         if (global.params.verbose)
783             fprintf(global.stdmsg, "json gen %s\n", m.toChars());
784         m.accept(json);
785     }
786     json.arrayEnd();
787     json.removeComma();
788 }