1 /**
2  * Code for generating .json descriptions of the module when passing the `-X` flag to dmd.
3  *
4  * Copyright:   Copyright (C) 1999-2020 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.pascal:   return property(name, "pascal");
329         case LINK.objc:     return property(name, "objc");
330         }
331     }
332 
333     extern(D) void propertyStorageClass(const char[] name, StorageClass stc)
334     {
335         stc &= STCStorageClass;
336         if (stc)
337         {
338             propertyStart(name);
339             arrayStart();
340             while (stc)
341             {
342                 auto p = stcToString(stc);
343                 assert(p.length);
344                 item(p);
345             }
346             arrayEnd();
347         }
348     }
349 
350     extern(D) void property(const char[] linename, const char[] charname, const ref Loc loc)
351     {
352         if (loc.isValid())
353         {
354             if (auto filename = loc.filename.toDString)
355             {
356                 if (filename != this.filename)
357                 {
358                     this.filename = filename;
359                     property("file", filename);
360                 }
361             }
362             if (loc.linnum)
363             {
364                 property(linename, loc.linnum);
365                 if (loc.charnum)
366                     property(charname, loc.charnum);
367             }
368         }
369     }
370 
371     extern(D) void property(const char[] name, Type type)
372     {
373         if (type)
374         {
375             property(name, type.toString());
376         }
377     }
378 
379     extern(D) void property(const char[] name, const char[] deconame, Type type)
380     {
381         if (type)
382         {
383             if (type.deco)
384                 property(deconame, type.deco.toDString);
385             else
386                 property(name, type.toString());
387         }
388     }
389 
390     extern(D) void property(const char[] name, Parameters* parameters)
391     {
392         if (parameters is null || parameters.dim == 0)
393             return;
394         propertyStart(name);
395         arrayStart();
396         if (parameters)
397         {
398             for (size_t i = 0; i < parameters.dim; i++)
399             {
400                 Parameter p = (*parameters)[i];
401                 objectStart();
402                 if (p.ident)
403                     property("name", p.ident.toString());
404                 property("type", "deco", p.type);
405                 propertyStorageClass("storageClass", p.storageClass);
406                 if (p.defaultArg)
407                     property("default", p.defaultArg.toString());
408                 objectEnd();
409             }
410         }
411         arrayEnd();
412     }
413 
414     /* ========================================================================== */
415     void jsonProperties(Dsymbol s)
416     {
417         if (s.isModule())
418             return;
419         if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
420         {
421             property("name", s.toString());
422             property("kind", s.kind.toDString);
423         }
424         if (s.prot().kind != Prot.Kind.public_) // TODO: How about package(names)?
425             property("protection", protectionToString(s.prot().kind));
426         if (EnumMember em = s.isEnumMember())
427         {
428             if (em.origValue)
429                 property("value", em.origValue.toString());
430         }
431         property("comment", s.comment.toDString);
432         property("line", "char", s.loc);
433     }
434 
435     void jsonProperties(Declaration d)
436     {
437         if (d.storage_class & STC.local)
438             return;
439         jsonProperties(cast(Dsymbol)d);
440         propertyStorageClass("storageClass", d.storage_class);
441         property("linkage", d.linkage);
442         property("type", "deco", d.type);
443         // Emit originalType if it differs from type
444         if (d.type != d.originalType && d.originalType)
445         {
446             auto ostr = d.originalType.toString();
447             if (d.type)
448             {
449                 auto tstr = d.type.toString();
450                 if (ostr != tstr)
451                 {
452                     //printf("tstr = %s, ostr = %s\n", tstr, ostr);
453                     property("originalType", ostr);
454                 }
455             }
456             else
457                 property("originalType", ostr);
458         }
459     }
460 
461     void jsonProperties(TemplateDeclaration td)
462     {
463         jsonProperties(cast(Dsymbol)td);
464         if (td.onemember && td.onemember.isCtorDeclaration())
465             property("name", "this"); // __ctor -> this
466         else
467             property("name", td.ident.toString()); // Foo(T) -> Foo
468     }
469 
470     /* ========================================================================== */
471     override void visit(Dsymbol s)
472     {
473     }
474 
475     override void visit(Module s)
476     {
477         objectStart();
478         if (s.md)
479             property("name", s.md.toString());
480         property("kind", s.kind.toDString);
481         filename = s.srcfile.toString();
482         property("file", filename);
483         property("comment", s.comment.toDString);
484         propertyStart("members");
485         arrayStart();
486         for (size_t i = 0; i < s.members.dim; i++)
487         {
488             (*s.members)[i].accept(this);
489         }
490         arrayEnd();
491         objectEnd();
492     }
493 
494     override void visit(Import s)
495     {
496         if (s.id == Id.object)
497             return;
498         objectStart();
499         propertyStart("name");
500         stringStart();
501         if (s.packages && s.packages.dim)
502         {
503             for (size_t i = 0; i < s.packages.dim; i++)
504             {
505                 const pid = (*s.packages)[i];
506                 stringPart(pid.toString());
507                 buf.writeByte('.');
508             }
509         }
510         stringPart(s.id.toString());
511         stringEnd();
512         comma();
513         property("kind", s.kind.toDString);
514         property("comment", s.comment.toDString);
515         property("line", "char", s.loc);
516         if (s.prot().kind != Prot.Kind.public_)
517             property("protection", protectionToString(s.prot().kind));
518         if (s.aliasId)
519             property("alias", s.aliasId.toString());
520         bool hasRenamed = false;
521         bool hasSelective = false;
522         for (size_t i = 0; i < s.aliases.dim; i++)
523         {
524             // avoid empty "renamed" and "selective" sections
525             if (hasRenamed && hasSelective)
526                 break;
527             else if (s.aliases[i])
528                 hasRenamed = true;
529             else
530                 hasSelective = true;
531         }
532         if (hasRenamed)
533         {
534             // import foo : alias1 = target1;
535             propertyStart("renamed");
536             objectStart();
537             for (size_t i = 0; i < s.aliases.dim; i++)
538             {
539                 const name = s.names[i];
540                 const _alias = s.aliases[i];
541                 if (_alias)
542                     property(_alias.toString(), name.toString());
543             }
544             objectEnd();
545         }
546         if (hasSelective)
547         {
548             // import foo : target1;
549             propertyStart("selective");
550             arrayStart();
551             foreach (i, name; s.names)
552             {
553                 if (!s.aliases[i])
554                     item(name.toString());
555             }
556             arrayEnd();
557         }
558         objectEnd();
559     }
560 
561     override void visit(AttribDeclaration d)
562     {
563         Dsymbols* ds = d.include(null);
564         if (ds)
565         {
566             for (size_t i = 0; i < ds.dim; i++)
567             {
568                 Dsymbol s = (*ds)[i];
569                 s.accept(this);
570             }
571         }
572     }
573 
574     override void visit(ConditionalDeclaration d)
575     {
576         if (d.condition.inc != Include.notComputed)
577         {
578             visit(cast(AttribDeclaration)d);
579             return; // Don't visit the if/else bodies again below
580         }
581         Dsymbols* ds = d.decl ? d.decl : d.elsedecl;
582         for (size_t i = 0; i < ds.dim; i++)
583         {
584             Dsymbol s = (*ds)[i];
585             s.accept(this);
586         }
587     }
588 
589     override void visit(TypeInfoDeclaration d)
590     {
591     }
592 
593     override void visit(PostBlitDeclaration d)
594     {
595     }
596 
597     override void visit(Declaration d)
598     {
599         objectStart();
600         //property("unknown", "declaration");
601         jsonProperties(d);
602         objectEnd();
603     }
604 
605     override void visit(AggregateDeclaration d)
606     {
607         objectStart();
608         jsonProperties(d);
609         ClassDeclaration cd = d.isClassDeclaration();
610         if (cd)
611         {
612             if (cd.baseClass && cd.baseClass.ident != Id.Object)
613             {
614                 property("base", cd.baseClass.toPrettyChars(true).toDString);
615             }
616             if (cd.interfaces.length)
617             {
618                 propertyStart("interfaces");
619                 arrayStart();
620                 foreach (b; cd.interfaces)
621                 {
622                     item(b.sym.toPrettyChars(true).toDString);
623                 }
624                 arrayEnd();
625             }
626         }
627         if (d.members)
628         {
629             propertyStart("members");
630             arrayStart();
631             for (size_t i = 0; i < d.members.dim; i++)
632             {
633                 Dsymbol s = (*d.members)[i];
634                 s.accept(this);
635             }
636             arrayEnd();
637         }
638         objectEnd();
639     }
640 
641     override void visit(FuncDeclaration d)
642     {
643         objectStart();
644         jsonProperties(d);
645         TypeFunction tf = cast(TypeFunction)d.type;
646         if (tf && tf.ty == Tfunction)
647             property("parameters", tf.parameterList.parameters);
648         property("endline", "endchar", d.endloc);
649         if (d.foverrides.dim)
650         {
651             propertyStart("overrides");
652             arrayStart();
653             for (size_t i = 0; i < d.foverrides.dim; i++)
654             {
655                 FuncDeclaration fd = d.foverrides[i];
656                 item(fd.toPrettyChars().toDString);
657             }
658             arrayEnd();
659         }
660         if (d.fdrequire)
661         {
662             propertyStart("in");
663             d.fdrequire.accept(this);
664         }
665         if (d.fdensure)
666         {
667             propertyStart("out");
668             d.fdensure.accept(this);
669         }
670         objectEnd();
671     }
672 
673     override void visit(TemplateDeclaration d)
674     {
675         objectStart();
676         // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one
677         property("kind", "template");
678         jsonProperties(d);
679         propertyStart("parameters");
680         arrayStart();
681         for (size_t i = 0; i < d.parameters.dim; i++)
682         {
683             TemplateParameter s = (*d.parameters)[i];
684             objectStart();
685             property("name", s.ident.toString());
686 
687             if (auto type = s.isTemplateTypeParameter())
688             {
689                 if (s.isTemplateThisParameter())
690                     property("kind", "this");
691                 else
692                     property("kind", "type");
693                 property("type", "deco", type.specType);
694                 property("default", "defaultDeco", type.defaultType);
695             }
696 
697             if (auto value = s.isTemplateValueParameter())
698             {
699                 property("kind", "value");
700                 property("type", "deco", value.valType);
701                 if (value.specValue)
702                     property("specValue", value.specValue.toString());
703                 if (value.defaultValue)
704                     property("defaultValue", value.defaultValue.toString());
705             }
706 
707             if (auto _alias = s.isTemplateAliasParameter())
708             {
709                 property("kind", "alias");
710                 property("type", "deco", _alias.specType);
711                 if (_alias.specAlias)
712                     property("specAlias", _alias.specAlias.toString());
713                 if (_alias.defaultAlias)
714                     property("defaultAlias", _alias.defaultAlias.toString());
715             }
716 
717             if (auto tuple = s.isTemplateTupleParameter())
718             {
719                 property("kind", "tuple");
720             }
721 
722             objectEnd();
723         }
724         arrayEnd();
725         Expression expression = d.constraint;
726         if (expression)
727         {
728             property("constraint", expression.toString());
729         }
730         propertyStart("members");
731         arrayStart();
732         for (size_t i = 0; i < d.members.dim; i++)
733         {
734             Dsymbol s = (*d.members)[i];
735             s.accept(this);
736         }
737         arrayEnd();
738         objectEnd();
739     }
740 
741     override void visit(EnumDeclaration d)
742     {
743         if (d.isAnonymous())
744         {
745             if (d.members)
746             {
747                 for (size_t i = 0; i < d.members.dim; i++)
748                 {
749                     Dsymbol s = (*d.members)[i];
750                     s.accept(this);
751                 }
752             }
753             return;
754         }
755         objectStart();
756         jsonProperties(d);
757         property("base", "baseDeco", d.memtype);
758         if (d.members)
759         {
760             propertyStart("members");
761             arrayStart();
762             for (size_t i = 0; i < d.members.dim; i++)
763             {
764                 Dsymbol s = (*d.members)[i];
765                 s.accept(this);
766             }
767             arrayEnd();
768         }
769         objectEnd();
770     }
771 
772     override void visit(EnumMember s)
773     {
774         objectStart();
775         jsonProperties(cast(Dsymbol)s);
776         property("type", "deco", s.origType);
777         objectEnd();
778     }
779 
780     override void visit(VarDeclaration d)
781     {
782         if (d.storage_class & STC.local)
783             return;
784         objectStart();
785         jsonProperties(d);
786         if (d._init)
787             property("init", d._init.toString());
788         if (d.isField())
789             property("offset", d.offset);
790         if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT)
791             property("align", d.alignment);
792         objectEnd();
793     }
794 
795     override void visit(TemplateMixin d)
796     {
797         objectStart();
798         jsonProperties(d);
799         objectEnd();
800     }
801 
802     /**
803     Generate an array of module objects that represent the syntax of each
804     "root module".
805 
806     Params:
807      modules = array of the "root modules"
808     */
809     private void generateModules(Modules* modules)
810     {
811         arrayStart();
812         if (modules)
813         {
814             foreach (m; *modules)
815             {
816                 if (global.params.verbose)
817                     message("json gen %s", m.toChars());
818                 m.accept(this);
819             }
820         }
821         arrayEnd();
822     }
823 
824     /**
825     Generate the "compilerInfo" object which contains information about the compiler
826     such as the filename, version, supported features, etc.
827     */
828     private void generateCompilerInfo()
829     {
830         objectStart();
831         requiredProperty("vendor", global.vendor);
832         requiredProperty("version", global._version);
833         property("__VERSION__", global.versionNumber());
834         requiredProperty("interface", determineCompilerInterface());
835         property("size_t", size_t.sizeof);
836         propertyStart("platforms");
837         arrayStart();
838         if (global.params.isWindows)
839         {
840             item("windows");
841         }
842         else
843         {
844             item("posix");
845             if (global.params.isLinux)
846                 item("linux");
847             else if (global.params.isOSX)
848                 item("osx");
849             else if (global.params.isFreeBSD)
850             {
851                 item("freebsd");
852                 item("bsd");
853             }
854             else if (global.params.isOpenBSD)
855             {
856                 item("openbsd");
857                 item("bsd");
858             }
859             else if (global.params.isSolaris)
860             {
861                 item("solaris");
862                 item("bsd");
863             }
864         }
865         arrayEnd();
866 
867         propertyStart("architectures");
868         arrayStart();
869         if (global.params.is64bit)
870             item("x86_64");
871         else
872             version(X86) item("x86");
873         arrayEnd();
874 
875         propertyStart("predefinedVersions");
876         arrayStart();
877         if (global.versionids)
878         {
879             foreach (const versionid; *global.versionids)
880             {
881                 item(versionid.toString());
882             }
883         }
884         arrayEnd();
885 
886         propertyStart("supportedFeatures");
887         {
888             objectStart();
889             scope(exit) objectEnd();
890             propertyBool("includeImports", true);
891         }
892         objectEnd();
893     }
894 
895     /**
896     Generate the "buildInfo" object which contains information specific to the
897     current build such as CWD, importPaths, configFile, etc.
898     */
899     private void generateBuildInfo()
900     {
901         objectStart();
902         requiredProperty("cwd", getcwd(null, 0).toDString);
903         requiredProperty("argv0", global.params.argv0);
904         requiredProperty("config", global.inifilename);
905         requiredProperty("libName", global.params.libname);
906 
907         propertyStart("importPaths");
908         arrayStart();
909         if (global.params.imppath)
910         {
911             foreach (importPath; *global.params.imppath)
912             {
913                 item(importPath.toDString);
914             }
915         }
916         arrayEnd();
917 
918         propertyStart("objectFiles");
919         arrayStart();
920         foreach (objfile; global.params.objfiles)
921         {
922             item(objfile.toDString);
923         }
924         arrayEnd();
925 
926         propertyStart("libraryFiles");
927         arrayStart();
928         foreach (lib; global.params.libfiles)
929         {
930             item(lib.toDString);
931         }
932         arrayEnd();
933 
934         propertyStart("ddocFiles");
935         arrayStart();
936         foreach (ddocFile; global.params.ddocfiles)
937         {
938             item(ddocFile.toDString);
939         }
940         arrayEnd();
941 
942         requiredProperty("mapFile", global.params.mapfile);
943         requiredProperty("resourceFile", global.params.resfile);
944         requiredProperty("defFile", global.params.deffile);
945 
946         objectEnd();
947     }
948 
949     /**
950     Generate the "semantics" object which contains a 'modules' field representing
951     semantic information about all the modules used in the compilation such as
952     module name, isRoot, contentImportedFiles, etc.
953     */
954     private void generateSemantics()
955     {
956         objectStart();
957         propertyStart("modules");
958         arrayStart();
959         foreach (m; Module.amodules)
960         {
961             objectStart();
962             requiredProperty("name", m.md ? m.md.toString() : null);
963             requiredProperty("file", m.srcfile.toString());
964             propertyBool("isRoot", m.isRoot());
965             if(m.contentImportedFiles.dim > 0)
966             {
967                 propertyStart("contentImports");
968                 arrayStart();
969                 foreach (file; m.contentImportedFiles)
970                 {
971                     item(file.toDString);
972                 }
973                 arrayEnd();
974             }
975             objectEnd();
976         }
977         arrayEnd();
978         objectEnd();
979     }
980 }
981 
982 extern (C++) void json_generate(OutBuffer* buf, Modules* modules)
983 {
984     scope ToJsonVisitor json = new ToJsonVisitor(buf);
985     // write trailing newline
986     scope(exit) buf.writeByte('\n');
987 
988     if (global.params.jsonFieldFlags == 0)
989     {
990         // Generate the original format, which is just an array
991         // of modules representing their syntax.
992         json.generateModules(modules);
993         json.removeComma();
994     }
995     else
996     {
997         // Generate the new format which is an object where each
998         // output option is its own field.
999 
1000         json.objectStart();
1001         if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo)
1002         {
1003             json.propertyStart("compilerInfo");
1004             json.generateCompilerInfo();
1005         }
1006         if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo)
1007         {
1008             json.propertyStart("buildInfo");
1009             json.generateBuildInfo();
1010         }
1011         if (global.params.jsonFieldFlags & JsonFieldFlags.modules)
1012         {
1013             json.propertyStart("modules");
1014             json.generateModules(modules);
1015         }
1016         if (global.params.jsonFieldFlags & JsonFieldFlags.semantics)
1017         {
1018             json.propertyStart("semantics");
1019             json.generateSemantics();
1020         }
1021         json.objectEnd();
1022     }
1023 }
1024 
1025 /**
1026 A string listing the name of each JSON field. Useful for errors messages.
1027 */
1028 enum jsonFieldNames = () {
1029     string s;
1030     string prefix = "";
1031     foreach (idx, enumName; __traits(allMembers, JsonFieldFlags))
1032     {
1033         static if (idx > 0)
1034         {
1035             s ~= prefix ~ "`" ~ enumName ~ "`";
1036             prefix = ", ";
1037         }
1038     }
1039     return s;
1040 }();
1041 
1042 /**
1043 Parse the given `fieldName` and return its corresponding JsonFieldFlags value.
1044 
1045 Params:
1046  fieldName = the field name to parse
1047 
1048 Returns: JsonFieldFlags.none on error, otherwise the JsonFieldFlags value
1049          corresponding to the given fieldName.
1050 */
1051 extern (C++) JsonFieldFlags tryParseJsonField(const(char)* fieldName)
1052 {
1053     auto fieldNameString = fieldName.toDString();
1054     foreach (idx, enumName; __traits(allMembers, JsonFieldFlags))
1055     {
1056         static if (idx > 0)
1057         {
1058             if (fieldNameString == enumName)
1059                 return __traits(getMember, JsonFieldFlags, enumName);
1060         }
1061     }
1062     return JsonFieldFlags.none;
1063 }
1064 
1065 /**
1066 Determines and returns the compiler interface which is one of `dmd`, `ldc`,
1067 `gdc` or `sdc`. Returns `null` if no interface can be determined.
1068 */
1069 private extern(D) string determineCompilerInterface()
1070 {
1071     if (global.vendor == "Digital Mars D")
1072         return "dmd";
1073     if (global.vendor == "LDC")
1074         return "ldc";
1075     if (global.vendor == "GNU D")
1076         return "gdc";
1077     if (global.vendor == "SDC")
1078         return "sdc";
1079     return null;
1080 }