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