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