1 /**
2  * Code for generating .json descriptions of the module when passing the `-X` flag to dmd.
3  *
4  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/json.d, _json.d)
8  * Documentation:  https://dlang.org/phobos/dmd_json.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/json.d
10  */
11 
12 module dmd.json;
13 
14 import core.stdc.stdio;
15 import core.stdc.string;
16 import dmd.aggregate;
17 import dmd.arraytypes;
18 import dmd.astenums;
19 import dmd.attrib;
20 import dmd.cond;
21 import dmd.dclass;
22 import dmd.declaration;
23 import dmd.denum;
24 import dmd.dimport;
25 import dmd.dmodule;
26 import dmd.dsymbol;
27 import dmd.dtemplate;
28 import dmd.errors;
29 import dmd.expression;
30 import dmd.func;
31 import dmd.globals;
32 import dmd.hdrgen;
33 import dmd.id;
34 import dmd.identifier;
35 import dmd.mtype;
36 import dmd.root.outbuffer;
37 import dmd.root.rootobject;
38 import dmd.root.string;
39 import dmd.target;
40 import dmd.visitor;
41 
version(Windows)42 version(Windows) {
43     extern (C) char* getcwd(char* buffer, size_t maxlen);
44 } else {
45     import core.sys.posix.unistd : getcwd;
46 }
47 
48 private extern (C++) final class ToJsonVisitor : Visitor
49 {
50     alias visit = Visitor.visit;
51 public:
52     OutBuffer* buf;
53     int indentLevel;
54     const(char)[] filename;
55 
this(OutBuffer * buf)56     extern (D) this(OutBuffer* buf)
57     {
58         this.buf = buf;
59     }
60 
61 
indent()62     void indent()
63     {
64         if (buf.length >= 1 && (*buf)[buf.length - 1] == '\n')
65             for (int i = 0; i < indentLevel; i++)
66                 buf.writeByte(' ');
67     }
68 
removeComma()69     void removeComma()
70     {
71         if (buf.length >= 2 && (*buf)[buf.length - 2] == ',' && ((*buf)[buf.length - 1] == '\n' || (*buf)[buf.length - 1] == ' '))
72             buf.setsize(buf.length - 2);
73     }
74 
comma()75     void comma()
76     {
77         if (indentLevel > 0)
78             buf.writestring(",\n");
79     }
80 
stringStart()81     void stringStart()
82     {
83         buf.writeByte('\"');
84     }
85 
stringEnd()86     void stringEnd()
87     {
88         buf.writeByte('\"');
89     }
90 
stringPart(const char[]s)91     extern(D) void stringPart(const char[] s)
92     {
93         foreach (char c; s)
94         {
95             switch (c)
96             {
97             case '\n':
98                 buf.writestring("\\n");
99                 break;
100             case '\r':
101                 buf.writestring("\\r");
102                 break;
103             case '\t':
104                 buf.writestring("\\t");
105                 break;
106             case '\"':
107                 buf.writestring("\\\"");
108                 break;
109             case '\\':
110                 buf.writestring("\\\\");
111                 break;
112             case '\b':
113                 buf.writestring("\\b");
114                 break;
115             case '\f':
116                 buf.writestring("\\f");
117                 break;
118             default:
119                 if (c < 0x20)
120                     buf.printf("\\u%04x", c);
121                 else
122                 {
123                     // Note that UTF-8 chars pass through here just fine
124                     buf.writeByte(c);
125                 }
126                 break;
127             }
128         }
129     }
130 
131     // Json value functions
132     /*********************************
133      * Encode string into buf, and wrap it in double quotes.
134      */
value(const char[]s)135     extern(D) void value(const char[] s)
136     {
137         stringStart();
138         stringPart(s);
139         stringEnd();
140     }
141 
value(int value)142     void value(int value)
143     {
144         if (value < 0)
145         {
146             buf.writeByte('-');
147             value = -value;
148         }
149         buf.print(value);
150     }
151 
valueBool(bool value)152     void valueBool(bool value)
153     {
154         buf.writestring(value ? "true" : "false");
155     }
156 
157     /*********************************
158      * Item is an intented value and a comma, for use in arrays
159      */
item(const char[]s)160     extern(D) void item(const char[] s)
161     {
162         indent();
163         value(s);
164         comma();
165     }
166 
item(int i)167     void item(int i)
168     {
169         indent();
170         value(i);
171         comma();
172     }
173 
itemBool(const bool b)174     void itemBool(const bool b)
175     {
176         indent();
177         valueBool(b);
178         comma();
179     }
180 
181     // Json array functions
arrayStart()182     void arrayStart()
183     {
184         indent();
185         buf.writestring("[\n");
186         indentLevel++;
187     }
188 
arrayEnd()189     void arrayEnd()
190     {
191         indentLevel--;
192         removeComma();
193         if (buf.length >= 2 && (*buf)[buf.length - 2] == '[' && (*buf)[buf.length - 1] == '\n')
194             buf.setsize(buf.length - 1);
195         else if (!(buf.length >= 1 && (*buf)[buf.length - 1] == '['))
196         {
197             buf.writestring("\n");
198             indent();
199         }
200         buf.writestring("]");
201         comma();
202     }
203 
204     // Json object functions
objectStart()205     void objectStart()
206     {
207         indent();
208         buf.writestring("{\n");
209         indentLevel++;
210     }
211 
objectEnd()212     void objectEnd()
213     {
214         indentLevel--;
215         removeComma();
216         if (buf.length >= 2 && (*buf)[buf.length - 2] == '{' && (*buf)[buf.length - 1] == '\n')
217             buf.setsize(buf.length - 1);
218         else
219         {
220             buf.writestring("\n");
221             indent();
222         }
223         buf.writestring("}");
224         comma();
225     }
226 
227     // Json object property functions
propertyStart(const char[]name)228     extern(D) void propertyStart(const char[] name)
229     {
230         indent();
231         value(name);
232         buf.writestring(" : ");
233     }
234 
235     /**
236     Write the given string object property only if `s` is not null.
237 
238     Params:
239      name = the name of the object property
240      s = the string value of the object property
241     */
property(const char[]name,const char[]s)242     extern(D) void property(const char[] name, const char[] s)
243     {
244         if (s is null)
245             return;
246         propertyStart(name);
247         value(s);
248         comma();
249     }
250 
251     /**
252     Write the given string object property.
253 
254     Params:
255      name = the name of the object property
256      s = the string value of the object property
257     */
requiredProperty(const char[]name,const char[]s)258     extern(D) void requiredProperty(const char[] name, const char[] s)
259     {
260         propertyStart(name);
261         if (s is null)
262             buf.writestring("null");
263         else
264             value(s);
265         comma();
266     }
267 
property(const char[]name,int i)268     extern(D) void property(const char[] name, int i)
269     {
270         propertyStart(name);
271         value(i);
272         comma();
273     }
274 
propertyBool(const char[]name,const bool b)275     extern(D) void propertyBool(const char[] name, const bool b)
276     {
277         propertyStart(name);
278         valueBool(b);
279         comma();
280     }
281 
property(const char[]name,TRUST trust)282     extern(D) void property(const char[] name, TRUST trust)
283     {
284         final switch (trust)
285         {
286         case TRUST.default_:
287             // Should not be printed
288             //property(name, "default");
289             break;
290         case TRUST.system:  return property(name, "system");
291         case TRUST.trusted: return property(name, "trusted");
292         case TRUST.safe:    return property(name, "safe");
293         }
294     }
295 
property(const char[]name,PURE purity)296     extern(D) void property(const char[] name, PURE purity)
297     {
298         final switch (purity)
299         {
300         case PURE.impure:
301             // Should not be printed
302             //property(name, "impure");
303             break;
304         case PURE.weak:     return property(name, "weak");
305         case PURE.const_:   return property(name, "const");
306         case PURE.strong:   return property(name, "strong");
307         case PURE.fwdref:   return property(name, "fwdref");
308         }
309     }
310 
property(const char[]name,const LINK linkage)311     extern(D) void property(const char[] name, const LINK linkage)
312     {
313         final switch (linkage)
314         {
315         case LINK.default_:
316             // Should not be printed
317             //property(name, "default");
318             break;
319         case LINK.d:
320             // Should not be printed
321             //property(name, "d");
322             break;
323         case LINK.system:
324             // Should not be printed
325             //property(name, "system");
326             break;
327         case LINK.c:        return property(name, "c");
328         case LINK.cpp:      return property(name, "cpp");
329         case LINK.windows:  return property(name, "windows");
330         case LINK.objc:     return property(name, "objc");
331         }
332     }
333 
propertyStorageClass(const char[]name,StorageClass stc)334     extern(D) void propertyStorageClass(const char[] name, StorageClass stc)
335     {
336         stc &= STC.visibleStorageClasses;
337         if (stc)
338         {
339             propertyStart(name);
340             arrayStart();
341             while (stc)
342             {
343                 auto p = stcToString(stc);
344                 assert(p.length);
345                 item(p);
346             }
347             arrayEnd();
348         }
349     }
350 
property(const char[]linename,const char[]charname,const ref Loc loc)351     extern(D) void property(const char[] linename, const char[] charname, const ref Loc loc)
352     {
353         if (loc.isValid())
354         {
355             if (auto filename = loc.filename.toDString)
356             {
357                 if (filename != this.filename)
358                 {
359                     this.filename = filename;
360                     property("file", filename);
361                 }
362             }
363             if (loc.linnum)
364             {
365                 property(linename, loc.linnum);
366                 if (loc.charnum)
367                     property(charname, loc.charnum);
368             }
369         }
370     }
371 
property(const char[]name,Type type)372     extern(D) void property(const char[] name, Type type)
373     {
374         if (type)
375         {
376             property(name, type.toString());
377         }
378     }
379 
property(const char[]name,const char[]deconame,Type type)380     extern(D) void property(const char[] name, const char[] deconame, Type type)
381     {
382         if (type)
383         {
384             if (type.deco)
385                 property(deconame, type.deco.toDString);
386             else
387                 property(name, type.toString());
388         }
389     }
390 
property(const char[]name,Parameters * parameters)391     extern(D) void property(const char[] name, Parameters* parameters)
392     {
393         if (parameters is null || parameters.dim == 0)
394             return;
395         propertyStart(name);
396         arrayStart();
397         if (parameters)
398         {
399             for (size_t i = 0; i < parameters.dim; i++)
400             {
401                 Parameter p = (*parameters)[i];
402                 objectStart();
403                 if (p.ident)
404                     property("name", p.ident.toString());
405                 property("type", "deco", p.type);
406                 propertyStorageClass("storageClass", p.storageClass);
407                 if (p.defaultArg)
408                     property("default", p.defaultArg.toString());
409                 objectEnd();
410             }
411         }
412         arrayEnd();
413     }
414 
415     /* ========================================================================== */
jsonProperties(Dsymbol s)416     void jsonProperties(Dsymbol s)
417     {
418         if (s.isModule())
419             return;
420         if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
421         {
422             property("name", s.toString());
423             if (s.isStaticCtorDeclaration())
424             {
425                 property("kind", s.isSharedStaticCtorDeclaration()
426                          ? "shared static constructor" : "static constructor");
427             }
428             else if (s.isStaticDtorDeclaration())
429             {
430                 property("kind", s.isSharedStaticDtorDeclaration()
431                          ? "shared static destructor" : "static destructor");
432             }
433             else
434                 property("kind", s.kind.toDString);
435         }
436         // TODO: How about package(names)?
437         property("protection", visibilityToString(s.visible().kind));
438         if (EnumMember em = s.isEnumMember())
439         {
440             if (em.origValue)
441                 property("value", em.origValue.toString());
442         }
443         property("comment", s.comment.toDString);
444         property("line", "char", s.loc);
445     }
446 
jsonProperties(Declaration d)447     void jsonProperties(Declaration d)
448     {
449         if (d.storage_class & STC.local)
450             return;
451         jsonProperties(cast(Dsymbol)d);
452         propertyStorageClass("storageClass", d.storage_class);
453         property("linkage", d.linkage);
454         property("type", "deco", d.type);
455         // Emit originalType if it differs from type
456         if (d.type != d.originalType && d.originalType)
457         {
458             auto ostr = d.originalType.toString();
459             if (d.type)
460             {
461                 auto tstr = d.type.toString();
462                 if (ostr != tstr)
463                 {
464                     //printf("tstr = %s, ostr = %s\n", tstr, ostr);
465                     property("originalType", ostr);
466                 }
467             }
468             else
469                 property("originalType", ostr);
470         }
471     }
472 
jsonProperties(TemplateDeclaration td)473     void jsonProperties(TemplateDeclaration td)
474     {
475         jsonProperties(cast(Dsymbol)td);
476         if (td.onemember && td.onemember.isCtorDeclaration())
477             property("name", "this"); // __ctor -> this
478         else
479             property("name", td.ident.toString()); // Foo(T) -> Foo
480     }
481 
482     /* ========================================================================== */
visit(Dsymbol s)483     override void visit(Dsymbol s)
484     {
485     }
486 
visit(Module s)487     override void visit(Module s)
488     {
489         objectStart();
490         if (s.md)
491             property("name", s.md.toString());
492         property("kind", s.kind.toDString);
493         filename = s.srcfile.toString();
494         property("file", filename);
495         property("comment", s.comment.toDString);
496         propertyStart("members");
497         arrayStart();
498         for (size_t i = 0; i < s.members.dim; i++)
499         {
500             (*s.members)[i].accept(this);
501         }
502         arrayEnd();
503         objectEnd();
504     }
505 
visit(Import s)506     override void visit(Import s)
507     {
508         if (s.id == Id.object)
509             return;
510         objectStart();
511         propertyStart("name");
512         stringStart();
513         foreach (const pid; s.packages){
514             stringPart(pid.toString());
515             buf.writeByte('.');
516         }
517         stringPart(s.id.toString());
518         stringEnd();
519         comma();
520         property("kind", s.kind.toDString);
521         property("comment", s.comment.toDString);
522         property("line", "char", s.loc);
523         if (s.visible().kind != Visibility.Kind.public_)
524             property("protection", visibilityToString(s.visible().kind));
525         if (s.aliasId)
526             property("alias", s.aliasId.toString());
527         bool hasRenamed = false;
528         bool hasSelective = false;
529         for (size_t i = 0; i < s.aliases.dim; i++)
530         {
531             // avoid empty "renamed" and "selective" sections
532             if (hasRenamed && hasSelective)
533                 break;
534             else if (s.aliases[i])
535                 hasRenamed = true;
536             else
537                 hasSelective = true;
538         }
539         if (hasRenamed)
540         {
541             // import foo : alias1 = target1;
542             propertyStart("renamed");
543             objectStart();
544             for (size_t i = 0; i < s.aliases.dim; i++)
545             {
546                 const name = s.names[i];
547                 const _alias = s.aliases[i];
548                 if (_alias)
549                     property(_alias.toString(), name.toString());
550             }
551             objectEnd();
552         }
553         if (hasSelective)
554         {
555             // import foo : target1;
556             propertyStart("selective");
557             arrayStart();
558             foreach (i, name; s.names)
559             {
560                 if (!s.aliases[i])
561                     item(name.toString());
562             }
563             arrayEnd();
564         }
565         objectEnd();
566     }
567 
visit(AttribDeclaration d)568     override void visit(AttribDeclaration d)
569     {
570         Dsymbols* ds = d.include(null);
571         if (ds)
572         {
573             for (size_t i = 0; i < ds.dim; i++)
574             {
575                 Dsymbol s = (*ds)[i];
576                 s.accept(this);
577             }
578         }
579     }
580 
visit(ConditionalDeclaration d)581     override void visit(ConditionalDeclaration d)
582     {
583         if (d.condition.inc != Include.notComputed)
584         {
585             visit(cast(AttribDeclaration)d);
586             return; // Don't visit the if/else bodies again below
587         }
588         Dsymbols* ds = d.decl ? d.decl : d.elsedecl;
589         for (size_t i = 0; i < ds.dim; i++)
590         {
591             Dsymbol s = (*ds)[i];
592             s.accept(this);
593         }
594     }
595 
visit(TypeInfoDeclaration d)596     override void visit(TypeInfoDeclaration d)
597     {
598     }
599 
visit(PostBlitDeclaration d)600     override void visit(PostBlitDeclaration d)
601     {
602     }
603 
visit(Declaration d)604     override void visit(Declaration d)
605     {
606         objectStart();
607         //property("unknown", "declaration");
608         jsonProperties(d);
609         objectEnd();
610     }
611 
visit(AggregateDeclaration d)612     override void visit(AggregateDeclaration d)
613     {
614         objectStart();
615         jsonProperties(d);
616         ClassDeclaration cd = d.isClassDeclaration();
617         if (cd)
618         {
619             if (cd.baseClass && cd.baseClass.ident != Id.Object)
620             {
621                 property("base", cd.baseClass.toPrettyChars(true).toDString);
622             }
623             if (cd.interfaces.length)
624             {
625                 propertyStart("interfaces");
626                 arrayStart();
627                 foreach (b; cd.interfaces)
628                 {
629                     item(b.sym.toPrettyChars(true).toDString);
630                 }
631                 arrayEnd();
632             }
633         }
634         if (d.members)
635         {
636             propertyStart("members");
637             arrayStart();
638             for (size_t i = 0; i < d.members.dim; i++)
639             {
640                 Dsymbol s = (*d.members)[i];
641                 s.accept(this);
642             }
643             arrayEnd();
644         }
645         objectEnd();
646     }
647 
visit(FuncDeclaration d)648     override void visit(FuncDeclaration d)
649     {
650         objectStart();
651         jsonProperties(d);
652         TypeFunction tf = cast(TypeFunction)d.type;
653         if (tf && tf.ty == Tfunction)
654             property("parameters", tf.parameterList.parameters);
655         property("endline", "endchar", d.endloc);
656         if (d.foverrides.dim)
657         {
658             propertyStart("overrides");
659             arrayStart();
660             for (size_t i = 0; i < d.foverrides.dim; i++)
661             {
662                 FuncDeclaration fd = d.foverrides[i];
663                 item(fd.toPrettyChars().toDString);
664             }
665             arrayEnd();
666         }
667         if (d.fdrequire)
668         {
669             propertyStart("in");
670             d.fdrequire.accept(this);
671         }
672         if (d.fdensure)
673         {
674             propertyStart("out");
675             d.fdensure.accept(this);
676         }
677         objectEnd();
678     }
679 
visit(TemplateDeclaration d)680     override void visit(TemplateDeclaration d)
681     {
682         objectStart();
683         // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one
684         property("kind", "template");
685         jsonProperties(d);
686         propertyStart("parameters");
687         arrayStart();
688         for (size_t i = 0; i < d.parameters.dim; i++)
689         {
690             TemplateParameter s = (*d.parameters)[i];
691             objectStart();
692             property("name", s.ident.toString());
693 
694             if (auto type = s.isTemplateTypeParameter())
695             {
696                 if (s.isTemplateThisParameter())
697                     property("kind", "this");
698                 else
699                     property("kind", "type");
700                 property("type", "deco", type.specType);
701                 property("default", "defaultDeco", type.defaultType);
702             }
703 
704             if (auto value = s.isTemplateValueParameter())
705             {
706                 property("kind", "value");
707                 property("type", "deco", value.valType);
708                 if (value.specValue)
709                     property("specValue", value.specValue.toString());
710                 if (value.defaultValue)
711                     property("defaultValue", value.defaultValue.toString());
712             }
713 
714             if (auto _alias = s.isTemplateAliasParameter())
715             {
716                 property("kind", "alias");
717                 property("type", "deco", _alias.specType);
718                 if (_alias.specAlias)
719                     property("specAlias", _alias.specAlias.toString());
720                 if (_alias.defaultAlias)
721                     property("defaultAlias", _alias.defaultAlias.toString());
722             }
723 
724             if (auto tuple = s.isTemplateTupleParameter())
725             {
726                 property("kind", "tuple");
727             }
728 
729             objectEnd();
730         }
731         arrayEnd();
732         Expression expression = d.constraint;
733         if (expression)
734         {
735             property("constraint", expression.toString());
736         }
737         propertyStart("members");
738         arrayStart();
739         for (size_t i = 0; i < d.members.dim; i++)
740         {
741             Dsymbol s = (*d.members)[i];
742             s.accept(this);
743         }
744         arrayEnd();
745         objectEnd();
746     }
747 
visit(EnumDeclaration d)748     override void visit(EnumDeclaration d)
749     {
750         if (d.isAnonymous())
751         {
752             if (d.members)
753             {
754                 for (size_t i = 0; i < d.members.dim; i++)
755                 {
756                     Dsymbol s = (*d.members)[i];
757                     s.accept(this);
758                 }
759             }
760             return;
761         }
762         objectStart();
763         jsonProperties(d);
764         property("base", "baseDeco", d.memtype);
765         if (d.members)
766         {
767             propertyStart("members");
768             arrayStart();
769             for (size_t i = 0; i < d.members.dim; i++)
770             {
771                 Dsymbol s = (*d.members)[i];
772                 s.accept(this);
773             }
774             arrayEnd();
775         }
776         objectEnd();
777     }
778 
visit(EnumMember s)779     override void visit(EnumMember s)
780     {
781         objectStart();
782         jsonProperties(cast(Dsymbol)s);
783         property("type", "deco", s.origType);
784         objectEnd();
785     }
786 
visit(VarDeclaration d)787     override void visit(VarDeclaration d)
788     {
789         if (d.storage_class & STC.local)
790             return;
791         objectStart();
792         jsonProperties(d);
793         if (d._init)
794             property("init", d._init.toString());
795         if (d.isField())
796             property("offset", d.offset);
797         if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT)
798             property("align", d.alignment);
799         objectEnd();
800     }
801 
visit(TemplateMixin d)802     override void visit(TemplateMixin d)
803     {
804         objectStart();
805         jsonProperties(d);
806         objectEnd();
807     }
808 
809     /**
810     Generate an array of module objects that represent the syntax of each
811     "root module".
812 
813     Params:
814      modules = array of the "root modules"
815     */
generateModules(Modules * modules)816     private void generateModules(Modules* modules)
817     {
818         arrayStart();
819         if (modules)
820         {
821             foreach (m; *modules)
822             {
823                 if (global.params.verbose)
824                     message("json gen %s", m.toChars());
825                 m.accept(this);
826             }
827         }
828         arrayEnd();
829     }
830 
831     /**
832     Generate the "compilerInfo" object which contains information about the compiler
833     such as the filename, version, supported features, etc.
834     */
generateCompilerInfo()835     private void generateCompilerInfo()
836     {
837         import dmd.target : target;
838         objectStart();
839         requiredProperty("vendor", global.vendor);
840         requiredProperty("version", global.versionString());
841         property("__VERSION__", global.versionNumber());
842         requiredProperty("interface", determineCompilerInterface());
843         property("size_t", size_t.sizeof);
844         propertyStart("platforms");
845         arrayStart();
846         if (target.os == Target.OS.Windows)
847         {
848             item("windows");
849         }
850         else
851         {
852             item("posix");
853             if (target.os == Target.OS.linux)
854                 item("linux");
855             else if (target.os == Target.OS.OSX)
856                 item("osx");
857             else if (target.os == Target.OS.FreeBSD)
858             {
859                 item("freebsd");
860                 item("bsd");
861             }
862             else if (target.os == Target.OS.OpenBSD)
863             {
864                 item("openbsd");
865                 item("bsd");
866             }
867             else if (target.os == Target.OS.Solaris)
868             {
869                 item("solaris");
870                 item("bsd");
871             }
872         }
873         arrayEnd();
874 
875         propertyStart("architectures");
876         arrayStart();
877         item(target.architectureName);
878         arrayEnd();
879 
880         propertyStart("predefinedVersions");
881         arrayStart();
882         if (global.versionids)
883         {
884             foreach (const versionid; *global.versionids)
885             {
886                 item(versionid.toString());
887             }
888         }
889         arrayEnd();
890 
891         propertyStart("supportedFeatures");
892         {
893             objectStart();
894             scope(exit) objectEnd();
895             propertyBool("includeImports", true);
896         }
897         objectEnd();
898     }
899 
900     /**
901     Generate the "buildInfo" object which contains information specific to the
902     current build such as CWD, importPaths, configFile, etc.
903     */
generateBuildInfo()904     private void generateBuildInfo()
905     {
906         objectStart();
907         requiredProperty("cwd", getcwd(null, 0).toDString);
908         requiredProperty("argv0", global.params.argv0);
909         requiredProperty("config", global.inifilename);
910         requiredProperty("libName", global.params.libname);
911 
912         propertyStart("importPaths");
913         arrayStart();
914         if (global.params.imppath)
915         {
916             foreach (importPath; *global.params.imppath)
917             {
918                 item(importPath.toDString);
919             }
920         }
921         arrayEnd();
922 
923         propertyStart("objectFiles");
924         arrayStart();
925         foreach (objfile; global.params.objfiles)
926         {
927             item(objfile.toDString);
928         }
929         arrayEnd();
930 
931         propertyStart("libraryFiles");
932         arrayStart();
933         foreach (lib; global.params.libfiles)
934         {
935             item(lib.toDString);
936         }
937         arrayEnd();
938 
939         propertyStart("ddocFiles");
940         arrayStart();
941         foreach (ddocFile; global.params.ddocfiles)
942         {
943             item(ddocFile.toDString);
944         }
945         arrayEnd();
946 
947         requiredProperty("mapFile", global.params.mapfile);
948         requiredProperty("resourceFile", global.params.resfile);
949         requiredProperty("defFile", global.params.deffile);
950 
951         objectEnd();
952     }
953 
954     /**
955     Generate the "semantics" object which contains a 'modules' field representing
956     semantic information about all the modules used in the compilation such as
957     module name, isRoot, contentImportedFiles, etc.
958     */
generateSemantics()959     private void generateSemantics()
960     {
961         objectStart();
962         propertyStart("modules");
963         arrayStart();
964         foreach (m; Module.amodules)
965         {
966             objectStart();
967             requiredProperty("name", m.md ? m.md.toString() : null);
968             requiredProperty("file", m.srcfile.toString());
969             propertyBool("isRoot", m.isRoot());
970             if(m.contentImportedFiles.dim > 0)
971             {
972                 propertyStart("contentImports");
973                 arrayStart();
974                 foreach (file; m.contentImportedFiles)
975                 {
976                     item(file.toDString);
977                 }
978                 arrayEnd();
979             }
980             objectEnd();
981         }
982         arrayEnd();
983         objectEnd();
984     }
985 }
986 
json_generate(OutBuffer * buf,Modules * modules)987 extern (C++) void json_generate(OutBuffer* buf, Modules* modules)
988 {
989     scope ToJsonVisitor json = new ToJsonVisitor(buf);
990     // write trailing newline
991     scope(exit) buf.writeByte('\n');
992 
993     if (global.params.jsonFieldFlags == 0)
994     {
995         // Generate the original format, which is just an array
996         // of modules representing their syntax.
997         json.generateModules(modules);
998         json.removeComma();
999     }
1000     else
1001     {
1002         // Generate the new format which is an object where each
1003         // output option is its own field.
1004 
1005         json.objectStart();
1006         if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo)
1007         {
1008             json.propertyStart("compilerInfo");
1009             json.generateCompilerInfo();
1010         }
1011         if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo)
1012         {
1013             json.propertyStart("buildInfo");
1014             json.generateBuildInfo();
1015         }
1016         if (global.params.jsonFieldFlags & JsonFieldFlags.modules)
1017         {
1018             json.propertyStart("modules");
1019             json.generateModules(modules);
1020         }
1021         if (global.params.jsonFieldFlags & JsonFieldFlags.semantics)
1022         {
1023             json.propertyStart("semantics");
1024             json.generateSemantics();
1025         }
1026         json.objectEnd();
1027     }
1028 }
1029 
1030 /**
1031 A string listing the name of each JSON field. Useful for errors messages.
1032 */
1033 enum jsonFieldNames = () {
1034     string s;
1035     string prefix = "";
foreach(idx,enumName;__traits (allMembers,JsonFieldFlags))1036     foreach (idx, enumName; __traits(allMembers, JsonFieldFlags))
1037     {
1038         static if (idx > 0)
1039         {
1040             s ~= prefix ~ "`" ~ enumName ~ "`";
1041             prefix = ", ";
1042         }
1043     }
1044     return s;
1045 }();
1046 
1047 /**
1048 Parse the given `fieldName` and return its corresponding JsonFieldFlags value.
1049 
1050 Params:
1051  fieldName = the field name to parse
1052 
1053 Returns: JsonFieldFlags.none on error, otherwise the JsonFieldFlags value
1054          corresponding to the given fieldName.
1055 */
tryParseJsonField(const (char)* fieldName)1056 extern (C++) JsonFieldFlags tryParseJsonField(const(char)* fieldName)
1057 {
1058     auto fieldNameString = fieldName.toDString();
1059     foreach (idx, enumName; __traits(allMembers, JsonFieldFlags))
1060     {
1061         static if (idx > 0)
1062         {
1063             if (fieldNameString == enumName)
1064                 return __traits(getMember, JsonFieldFlags, enumName);
1065         }
1066     }
1067     return JsonFieldFlags.none;
1068 }
1069 
1070 /**
1071 Determines and returns the compiler interface which is one of `dmd`, `ldc`,
1072 `gdc` or `sdc`. Returns `null` if no interface can be determined.
1073 */
private(D)1074 private extern(D) string determineCompilerInterface()
1075 {
1076     if (global.vendor == "Digital Mars D")
1077         return "dmd";
1078     if (global.vendor == "LDC")
1079         return "ldc";
1080     if (global.vendor == "GNU D")
1081         return "gdc";
1082     if (global.vendor == "SDC")
1083         return "sdc";
1084     return null;
1085 }
1086