| /** |
| * Code for generating .json descriptions of the module when passing the `-X` flag to dmd. |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/json.d, _json.d) |
| * Documentation: https://dlang.org/phobos/dmd_json.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/json.d |
| */ |
| |
| module dmd.json; |
| |
| import core.stdc.stdio; |
| import core.stdc.string; |
| import dmd.aggregate; |
| import dmd.arraytypes; |
| import dmd.astenums; |
| import dmd.attrib; |
| import dmd.cond; |
| import dmd.dclass; |
| import dmd.declaration; |
| import dmd.denum; |
| import dmd.dimport; |
| import dmd.dmodule; |
| import dmd.dsymbol; |
| import dmd.dtemplate; |
| import dmd.errors; |
| import dmd.expression; |
| import dmd.func; |
| import dmd.globals; |
| import dmd.hdrgen; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.mtype; |
| import dmd.common.outbuffer; |
| import dmd.root.rootobject; |
| import dmd.root.string; |
| import dmd.target; |
| import dmd.visitor; |
| |
| version(Windows) { |
| extern (C) char* getcwd(char* buffer, size_t maxlen); |
| } else { |
| import core.sys.posix.unistd : getcwd; |
| } |
| |
| private extern (C++) final class ToJsonVisitor : Visitor |
| { |
| alias visit = Visitor.visit; |
| public: |
| OutBuffer* buf; |
| int indentLevel; |
| const(char)[] filename; |
| |
| extern (D) this(OutBuffer* buf) |
| { |
| this.buf = buf; |
| } |
| |
| |
| void indent() |
| { |
| if (buf.length >= 1 && (*buf)[buf.length - 1] == '\n') |
| for (int i = 0; i < indentLevel; i++) |
| buf.writeByte(' '); |
| } |
| |
| void removeComma() |
| { |
| if (buf.length >= 2 && (*buf)[buf.length - 2] == ',' && ((*buf)[buf.length - 1] == '\n' || (*buf)[buf.length - 1] == ' ')) |
| buf.setsize(buf.length - 2); |
| } |
| |
| void comma() |
| { |
| if (indentLevel > 0) |
| buf.writestring(",\n"); |
| } |
| |
| void stringStart() |
| { |
| buf.writeByte('\"'); |
| } |
| |
| void stringEnd() |
| { |
| buf.writeByte('\"'); |
| } |
| |
| extern(D) void stringPart(const char[] s) |
| { |
| foreach (char c; s) |
| { |
| switch (c) |
| { |
| case '\n': |
| buf.writestring("\\n"); |
| break; |
| case '\r': |
| buf.writestring("\\r"); |
| break; |
| case '\t': |
| buf.writestring("\\t"); |
| break; |
| case '\"': |
| buf.writestring("\\\""); |
| break; |
| case '\\': |
| buf.writestring("\\\\"); |
| break; |
| case '\b': |
| buf.writestring("\\b"); |
| break; |
| case '\f': |
| buf.writestring("\\f"); |
| break; |
| default: |
| if (c < 0x20) |
| buf.printf("\\u%04x", c); |
| else |
| { |
| // Note that UTF-8 chars pass through here just fine |
| buf.writeByte(c); |
| } |
| break; |
| } |
| } |
| } |
| |
| // Json value functions |
| /********************************* |
| * Encode string into buf, and wrap it in double quotes. |
| */ |
| extern(D) void value(const char[] s) |
| { |
| stringStart(); |
| stringPart(s); |
| stringEnd(); |
| } |
| |
| void value(int value) |
| { |
| if (value < 0) |
| { |
| buf.writeByte('-'); |
| value = -value; |
| } |
| buf.print(value); |
| } |
| |
| void valueBool(bool value) |
| { |
| buf.writestring(value ? "true" : "false"); |
| } |
| |
| /********************************* |
| * Item is an intented value and a comma, for use in arrays |
| */ |
| extern(D) void item(const char[] s) |
| { |
| indent(); |
| value(s); |
| comma(); |
| } |
| |
| void item(int i) |
| { |
| indent(); |
| value(i); |
| comma(); |
| } |
| |
| void itemBool(const bool b) |
| { |
| indent(); |
| valueBool(b); |
| comma(); |
| } |
| |
| // Json array functions |
| void arrayStart() |
| { |
| indent(); |
| buf.writestring("[\n"); |
| indentLevel++; |
| } |
| |
| void arrayEnd() |
| { |
| indentLevel--; |
| removeComma(); |
| if (buf.length >= 2 && (*buf)[buf.length - 2] == '[' && (*buf)[buf.length - 1] == '\n') |
| buf.setsize(buf.length - 1); |
| else if (!(buf.length >= 1 && (*buf)[buf.length - 1] == '[')) |
| { |
| buf.writestring("\n"); |
| indent(); |
| } |
| buf.writestring("]"); |
| comma(); |
| } |
| |
| // Json object functions |
| void objectStart() |
| { |
| indent(); |
| buf.writestring("{\n"); |
| indentLevel++; |
| } |
| |
| void objectEnd() |
| { |
| indentLevel--; |
| removeComma(); |
| if (buf.length >= 2 && (*buf)[buf.length - 2] == '{' && (*buf)[buf.length - 1] == '\n') |
| buf.setsize(buf.length - 1); |
| else |
| { |
| buf.writestring("\n"); |
| indent(); |
| } |
| buf.writestring("}"); |
| comma(); |
| } |
| |
| // Json object property functions |
| extern(D) void propertyStart(const char[] name) |
| { |
| indent(); |
| value(name); |
| buf.writestring(" : "); |
| } |
| |
| /** |
| Write the given string object property only if `s` is not null. |
| |
| Params: |
| name = the name of the object property |
| s = the string value of the object property |
| */ |
| extern(D) void property(const char[] name, const char[] s) |
| { |
| if (s is null) |
| return; |
| propertyStart(name); |
| value(s); |
| comma(); |
| } |
| |
| /** |
| Write the given string object property. |
| |
| Params: |
| name = the name of the object property |
| s = the string value of the object property |
| */ |
| extern(D) void requiredProperty(const char[] name, const char[] s) |
| { |
| propertyStart(name); |
| if (s is null) |
| buf.writestring("null"); |
| else |
| value(s); |
| comma(); |
| } |
| |
| extern(D) void property(const char[] name, int i) |
| { |
| propertyStart(name); |
| value(i); |
| comma(); |
| } |
| |
| extern(D) void propertyBool(const char[] name, const bool b) |
| { |
| propertyStart(name); |
| valueBool(b); |
| comma(); |
| } |
| |
| extern(D) void property(const char[] name, TRUST trust) |
| { |
| final switch (trust) |
| { |
| case TRUST.default_: |
| // Should not be printed |
| //property(name, "default"); |
| break; |
| case TRUST.system: return property(name, "system"); |
| case TRUST.trusted: return property(name, "trusted"); |
| case TRUST.safe: return property(name, "safe"); |
| } |
| } |
| |
| extern(D) void property(const char[] name, PURE purity) |
| { |
| final switch (purity) |
| { |
| case PURE.impure: |
| // Should not be printed |
| //property(name, "impure"); |
| break; |
| case PURE.weak: return property(name, "weak"); |
| case PURE.const_: return property(name, "strong"); |
| case PURE.fwdref: return property(name, "fwdref"); |
| } |
| } |
| |
| extern(D) void property(const char[] name, const LINK linkage) |
| { |
| final switch (linkage) |
| { |
| case LINK.default_: |
| // Should not be printed |
| //property(name, "default"); |
| break; |
| case LINK.d: |
| // Should not be printed |
| //property(name, "d"); |
| break; |
| case LINK.system: return property(name, "system"); |
| case LINK.c: return property(name, "c"); |
| case LINK.cpp: return property(name, "cpp"); |
| case LINK.windows: return property(name, "windows"); |
| case LINK.objc: return property(name, "objc"); |
| } |
| } |
| |
| extern(D) void propertyStorageClass(const char[] name, StorageClass stc) |
| { |
| stc &= STC.visibleStorageClasses; |
| if (stc) |
| { |
| propertyStart(name); |
| arrayStart(); |
| while (stc) |
| { |
| auto p = stcToString(stc); |
| assert(p.length); |
| item(p); |
| } |
| arrayEnd(); |
| } |
| } |
| |
| extern(D) void property(const char[] linename, const char[] charname, const ref Loc loc) |
| { |
| if (loc.isValid()) |
| { |
| if (auto filename = loc.filename.toDString) |
| { |
| if (filename != this.filename) |
| { |
| this.filename = filename; |
| property("file", filename); |
| } |
| } |
| if (loc.linnum) |
| { |
| property(linename, loc.linnum); |
| if (loc.charnum) |
| property(charname, loc.charnum); |
| } |
| } |
| } |
| |
| extern(D) void property(const char[] name, Type type) |
| { |
| if (type) |
| { |
| property(name, type.toString()); |
| } |
| } |
| |
| extern(D) void property(const char[] name, const char[] deconame, Type type) |
| { |
| if (type) |
| { |
| if (type.deco) |
| property(deconame, type.deco.toDString); |
| else |
| property(name, type.toString()); |
| } |
| } |
| |
| extern(D) void property(const char[] name, Parameters* parameters) |
| { |
| if (parameters is null || parameters.dim == 0) |
| return; |
| propertyStart(name); |
| arrayStart(); |
| if (parameters) |
| { |
| for (size_t i = 0; i < parameters.dim; i++) |
| { |
| Parameter p = (*parameters)[i]; |
| objectStart(); |
| if (p.ident) |
| property("name", p.ident.toString()); |
| property("type", "deco", p.type); |
| propertyStorageClass("storageClass", p.storageClass); |
| if (p.defaultArg) |
| property("default", p.defaultArg.toString()); |
| objectEnd(); |
| } |
| } |
| arrayEnd(); |
| } |
| |
| /* ========================================================================== */ |
| void jsonProperties(Dsymbol s) |
| { |
| if (s.isModule()) |
| return; |
| if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes |
| { |
| property("name", s.toString()); |
| if (s.isStaticCtorDeclaration()) |
| { |
| property("kind", s.isSharedStaticCtorDeclaration() |
| ? "shared static constructor" : "static constructor"); |
| } |
| else if (s.isStaticDtorDeclaration()) |
| { |
| property("kind", s.isSharedStaticDtorDeclaration() |
| ? "shared static destructor" : "static destructor"); |
| } |
| else |
| property("kind", s.kind.toDString); |
| } |
| // TODO: How about package(names)? |
| property("protection", visibilityToString(s.visible().kind)); |
| if (EnumMember em = s.isEnumMember()) |
| { |
| if (em.origValue) |
| property("value", em.origValue.toString()); |
| } |
| property("comment", s.comment.toDString); |
| property("line", "char", s.loc); |
| } |
| |
| void jsonProperties(Declaration d) |
| { |
| if (d.storage_class & STC.local) |
| return; |
| jsonProperties(cast(Dsymbol)d); |
| propertyStorageClass("storageClass", d.storage_class); |
| property("linkage", d.linkage); |
| property("type", "deco", d.type); |
| // Emit originalType if it differs from type |
| if (d.type != d.originalType && d.originalType) |
| { |
| auto ostr = d.originalType.toString(); |
| if (d.type) |
| { |
| auto tstr = d.type.toString(); |
| if (ostr != tstr) |
| { |
| //printf("tstr = %s, ostr = %s\n", tstr, ostr); |
| property("originalType", ostr); |
| } |
| } |
| else |
| property("originalType", ostr); |
| } |
| } |
| |
| void jsonProperties(TemplateDeclaration td) |
| { |
| jsonProperties(cast(Dsymbol)td); |
| if (td.onemember && td.onemember.isCtorDeclaration()) |
| property("name", "this"); // __ctor -> this |
| else |
| property("name", td.ident.toString()); // Foo(T) -> Foo |
| } |
| |
| /* ========================================================================== */ |
| override void visit(Dsymbol s) |
| { |
| } |
| |
| override void visit(Module s) |
| { |
| objectStart(); |
| if (s.md) |
| property("name", s.md.toString()); |
| property("kind", s.kind.toDString); |
| filename = s.srcfile.toString(); |
| property("file", filename); |
| property("comment", s.comment.toDString); |
| propertyStart("members"); |
| arrayStart(); |
| for (size_t i = 0; i < s.members.dim; i++) |
| { |
| (*s.members)[i].accept(this); |
| } |
| arrayEnd(); |
| objectEnd(); |
| } |
| |
| override void visit(Import s) |
| { |
| if (s.id == Id.object) |
| return; |
| objectStart(); |
| propertyStart("name"); |
| stringStart(); |
| foreach (const pid; s.packages){ |
| stringPart(pid.toString()); |
| buf.writeByte('.'); |
| } |
| stringPart(s.id.toString()); |
| stringEnd(); |
| comma(); |
| property("kind", s.kind.toDString); |
| property("comment", s.comment.toDString); |
| property("line", "char", s.loc); |
| if (s.visible().kind != Visibility.Kind.public_) |
| property("protection", visibilityToString(s.visible().kind)); |
| if (s.aliasId) |
| property("alias", s.aliasId.toString()); |
| bool hasRenamed = false; |
| bool hasSelective = false; |
| for (size_t i = 0; i < s.aliases.dim; i++) |
| { |
| // avoid empty "renamed" and "selective" sections |
| if (hasRenamed && hasSelective) |
| break; |
| else if (s.aliases[i]) |
| hasRenamed = true; |
| else |
| hasSelective = true; |
| } |
| if (hasRenamed) |
| { |
| // import foo : alias1 = target1; |
| propertyStart("renamed"); |
| objectStart(); |
| for (size_t i = 0; i < s.aliases.dim; i++) |
| { |
| const name = s.names[i]; |
| const _alias = s.aliases[i]; |
| if (_alias) |
| property(_alias.toString(), name.toString()); |
| } |
| objectEnd(); |
| } |
| if (hasSelective) |
| { |
| // import foo : target1; |
| propertyStart("selective"); |
| arrayStart(); |
| foreach (i, name; s.names) |
| { |
| if (!s.aliases[i]) |
| item(name.toString()); |
| } |
| arrayEnd(); |
| } |
| objectEnd(); |
| } |
| |
| override void visit(AttribDeclaration d) |
| { |
| Dsymbols* ds = d.include(null); |
| if (ds) |
| { |
| for (size_t i = 0; i < ds.dim; i++) |
| { |
| Dsymbol s = (*ds)[i]; |
| s.accept(this); |
| } |
| } |
| } |
| |
| override void visit(ConditionalDeclaration d) |
| { |
| if (d.condition.inc != Include.notComputed) |
| { |
| visit(cast(AttribDeclaration)d); |
| return; // Don't visit the if/else bodies again below |
| } |
| Dsymbols* ds = d.decl ? d.decl : d.elsedecl; |
| for (size_t i = 0; i < ds.dim; i++) |
| { |
| Dsymbol s = (*ds)[i]; |
| s.accept(this); |
| } |
| } |
| |
| override void visit(TypeInfoDeclaration d) |
| { |
| } |
| |
| override void visit(PostBlitDeclaration d) |
| { |
| } |
| |
| override void visit(Declaration d) |
| { |
| objectStart(); |
| //property("unknown", "declaration"); |
| jsonProperties(d); |
| objectEnd(); |
| } |
| |
| override void visit(AggregateDeclaration d) |
| { |
| objectStart(); |
| jsonProperties(d); |
| ClassDeclaration cd = d.isClassDeclaration(); |
| if (cd) |
| { |
| if (cd.baseClass && cd.baseClass.ident != Id.Object) |
| { |
| property("base", cd.baseClass.toPrettyChars(true).toDString); |
| } |
| if (cd.interfaces.length) |
| { |
| propertyStart("interfaces"); |
| arrayStart(); |
| foreach (b; cd.interfaces) |
| { |
| item(b.sym.toPrettyChars(true).toDString); |
| } |
| arrayEnd(); |
| } |
| } |
| if (d.members) |
| { |
| propertyStart("members"); |
| arrayStart(); |
| for (size_t i = 0; i < d.members.dim; i++) |
| { |
| Dsymbol s = (*d.members)[i]; |
| s.accept(this); |
| } |
| arrayEnd(); |
| } |
| objectEnd(); |
| } |
| |
| override void visit(FuncDeclaration d) |
| { |
| objectStart(); |
| jsonProperties(d); |
| TypeFunction tf = cast(TypeFunction)d.type; |
| if (tf && tf.ty == Tfunction) |
| property("parameters", tf.parameterList.parameters); |
| property("endline", "endchar", d.endloc); |
| if (d.foverrides.dim) |
| { |
| propertyStart("overrides"); |
| arrayStart(); |
| for (size_t i = 0; i < d.foverrides.dim; i++) |
| { |
| FuncDeclaration fd = d.foverrides[i]; |
| item(fd.toPrettyChars().toDString); |
| } |
| arrayEnd(); |
| } |
| if (d.fdrequire) |
| { |
| propertyStart("in"); |
| d.fdrequire.accept(this); |
| } |
| if (d.fdensure) |
| { |
| propertyStart("out"); |
| d.fdensure.accept(this); |
| } |
| objectEnd(); |
| } |
| |
| override void visit(TemplateDeclaration d) |
| { |
| objectStart(); |
| // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one |
| property("kind", "template"); |
| jsonProperties(d); |
| propertyStart("parameters"); |
| arrayStart(); |
| for (size_t i = 0; i < d.parameters.dim; i++) |
| { |
| TemplateParameter s = (*d.parameters)[i]; |
| objectStart(); |
| property("name", s.ident.toString()); |
| |
| if (auto type = s.isTemplateTypeParameter()) |
| { |
| if (s.isTemplateThisParameter()) |
| property("kind", "this"); |
| else |
| property("kind", "type"); |
| property("type", "deco", type.specType); |
| property("default", "defaultDeco", type.defaultType); |
| } |
| |
| if (auto value = s.isTemplateValueParameter()) |
| { |
| property("kind", "value"); |
| property("type", "deco", value.valType); |
| if (value.specValue) |
| property("specValue", value.specValue.toString()); |
| if (value.defaultValue) |
| property("defaultValue", value.defaultValue.toString()); |
| } |
| |
| if (auto _alias = s.isTemplateAliasParameter()) |
| { |
| property("kind", "alias"); |
| property("type", "deco", _alias.specType); |
| if (_alias.specAlias) |
| property("specAlias", _alias.specAlias.toString()); |
| if (_alias.defaultAlias) |
| property("defaultAlias", _alias.defaultAlias.toString()); |
| } |
| |
| if (auto tuple = s.isTemplateTupleParameter()) |
| { |
| property("kind", "tuple"); |
| } |
| |
| objectEnd(); |
| } |
| arrayEnd(); |
| Expression expression = d.constraint; |
| if (expression) |
| { |
| property("constraint", expression.toString()); |
| } |
| propertyStart("members"); |
| arrayStart(); |
| for (size_t i = 0; i < d.members.dim; i++) |
| { |
| Dsymbol s = (*d.members)[i]; |
| s.accept(this); |
| } |
| arrayEnd(); |
| objectEnd(); |
| } |
| |
| override void visit(EnumDeclaration d) |
| { |
| if (d.isAnonymous()) |
| { |
| if (d.members) |
| { |
| for (size_t i = 0; i < d.members.dim; i++) |
| { |
| Dsymbol s = (*d.members)[i]; |
| s.accept(this); |
| } |
| } |
| return; |
| } |
| objectStart(); |
| jsonProperties(d); |
| property("base", "baseDeco", d.memtype); |
| if (d.members) |
| { |
| propertyStart("members"); |
| arrayStart(); |
| for (size_t i = 0; i < d.members.dim; i++) |
| { |
| Dsymbol s = (*d.members)[i]; |
| s.accept(this); |
| } |
| arrayEnd(); |
| } |
| objectEnd(); |
| } |
| |
| override void visit(EnumMember s) |
| { |
| objectStart(); |
| jsonProperties(cast(Dsymbol)s); |
| property("type", "deco", s.origType); |
| objectEnd(); |
| } |
| |
| override void visit(VarDeclaration d) |
| { |
| if (d.storage_class & STC.local) |
| return; |
| objectStart(); |
| jsonProperties(d); |
| if (d._init) |
| property("init", d._init.toString()); |
| if (d.isField()) |
| property("offset", d.offset); |
| if (!d.alignment.isUnknown() && !d.alignment.isDefault()) |
| property("align", d.alignment.get()); |
| objectEnd(); |
| } |
| |
| override void visit(TemplateMixin d) |
| { |
| objectStart(); |
| jsonProperties(d); |
| objectEnd(); |
| } |
| |
| /** |
| Generate an array of module objects that represent the syntax of each |
| "root module". |
| |
| Params: |
| modules = array of the "root modules" |
| */ |
| private void generateModules(Modules* modules) |
| { |
| arrayStart(); |
| if (modules) |
| { |
| foreach (m; *modules) |
| { |
| if (global.params.verbose) |
| message("json gen %s", m.toChars()); |
| m.accept(this); |
| } |
| } |
| arrayEnd(); |
| } |
| |
| /** |
| Generate the "compilerInfo" object which contains information about the compiler |
| such as the filename, version, supported features, etc. |
| */ |
| private void generateCompilerInfo() |
| { |
| import dmd.target : target; |
| objectStart(); |
| requiredProperty("vendor", global.vendor); |
| requiredProperty("version", global.versionString()); |
| property("__VERSION__", global.versionNumber()); |
| requiredProperty("interface", determineCompilerInterface()); |
| property("size_t", size_t.sizeof); |
| propertyStart("platforms"); |
| arrayStart(); |
| if (target.os == Target.OS.Windows) |
| { |
| item("windows"); |
| } |
| else |
| { |
| item("posix"); |
| if (target.os == Target.OS.linux) |
| item("linux"); |
| else if (target.os == Target.OS.OSX) |
| item("osx"); |
| else if (target.os == Target.OS.FreeBSD) |
| { |
| item("freebsd"); |
| item("bsd"); |
| } |
| else if (target.os == Target.OS.OpenBSD) |
| { |
| item("openbsd"); |
| item("bsd"); |
| } |
| else if (target.os == Target.OS.Solaris) |
| { |
| item("solaris"); |
| item("bsd"); |
| } |
| } |
| arrayEnd(); |
| |
| propertyStart("architectures"); |
| arrayStart(); |
| item(target.architectureName); |
| arrayEnd(); |
| |
| propertyStart("predefinedVersions"); |
| arrayStart(); |
| if (global.versionids) |
| { |
| foreach (const versionid; *global.versionids) |
| { |
| item(versionid.toString()); |
| } |
| } |
| arrayEnd(); |
| |
| propertyStart("supportedFeatures"); |
| { |
| objectStart(); |
| scope(exit) objectEnd(); |
| propertyBool("includeImports", true); |
| } |
| objectEnd(); |
| } |
| |
| /** |
| Generate the "buildInfo" object which contains information specific to the |
| current build such as CWD, importPaths, configFile, etc. |
| */ |
| private void generateBuildInfo() |
| { |
| objectStart(); |
| requiredProperty("cwd", getcwd(null, 0).toDString); |
| requiredProperty("argv0", global.params.argv0); |
| requiredProperty("config", global.inifilename); |
| requiredProperty("libName", global.params.libname); |
| |
| propertyStart("importPaths"); |
| arrayStart(); |
| if (global.params.imppath) |
| { |
| foreach (importPath; *global.params.imppath) |
| { |
| item(importPath.toDString); |
| } |
| } |
| arrayEnd(); |
| |
| propertyStart("objectFiles"); |
| arrayStart(); |
| foreach (objfile; global.params.objfiles) |
| { |
| item(objfile.toDString); |
| } |
| arrayEnd(); |
| |
| propertyStart("libraryFiles"); |
| arrayStart(); |
| foreach (lib; global.params.libfiles) |
| { |
| item(lib.toDString); |
| } |
| arrayEnd(); |
| |
| propertyStart("ddocFiles"); |
| arrayStart(); |
| foreach (ddocFile; global.params.ddocfiles) |
| { |
| item(ddocFile.toDString); |
| } |
| arrayEnd(); |
| |
| requiredProperty("mapFile", global.params.mapfile); |
| requiredProperty("resourceFile", global.params.resfile); |
| requiredProperty("defFile", global.params.deffile); |
| |
| objectEnd(); |
| } |
| |
| /** |
| Generate the "semantics" object which contains a 'modules' field representing |
| semantic information about all the modules used in the compilation such as |
| module name, isRoot, contentImportedFiles, etc. |
| */ |
| private void generateSemantics() |
| { |
| objectStart(); |
| propertyStart("modules"); |
| arrayStart(); |
| foreach (m; Module.amodules) |
| { |
| objectStart(); |
| requiredProperty("name", m.md ? m.md.toString() : null); |
| requiredProperty("file", m.srcfile.toString()); |
| propertyBool("isRoot", m.isRoot()); |
| if(m.contentImportedFiles.dim > 0) |
| { |
| propertyStart("contentImports"); |
| arrayStart(); |
| foreach (file; m.contentImportedFiles) |
| { |
| item(file.toDString); |
| } |
| arrayEnd(); |
| } |
| objectEnd(); |
| } |
| arrayEnd(); |
| objectEnd(); |
| } |
| } |
| |
| extern (C++) void json_generate(OutBuffer* buf, Modules* modules) |
| { |
| scope ToJsonVisitor json = new ToJsonVisitor(buf); |
| // write trailing newline |
| scope(exit) buf.writeByte('\n'); |
| |
| if (global.params.jsonFieldFlags == 0) |
| { |
| // Generate the original format, which is just an array |
| // of modules representing their syntax. |
| json.generateModules(modules); |
| json.removeComma(); |
| } |
| else |
| { |
| // Generate the new format which is an object where each |
| // output option is its own field. |
| |
| json.objectStart(); |
| if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo) |
| { |
| json.propertyStart("compilerInfo"); |
| json.generateCompilerInfo(); |
| } |
| if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo) |
| { |
| json.propertyStart("buildInfo"); |
| json.generateBuildInfo(); |
| } |
| if (global.params.jsonFieldFlags & JsonFieldFlags.modules) |
| { |
| json.propertyStart("modules"); |
| json.generateModules(modules); |
| } |
| if (global.params.jsonFieldFlags & JsonFieldFlags.semantics) |
| { |
| json.propertyStart("semantics"); |
| json.generateSemantics(); |
| } |
| json.objectEnd(); |
| } |
| } |
| |
| /** |
| A string listing the name of each JSON field. Useful for errors messages. |
| */ |
| enum jsonFieldNames = () { |
| string s; |
| string prefix = ""; |
| foreach (idx, enumName; __traits(allMembers, JsonFieldFlags)) |
| { |
| static if (idx > 0) |
| { |
| s ~= prefix ~ "`" ~ enumName ~ "`"; |
| prefix = ", "; |
| } |
| } |
| return s; |
| }(); |
| |
| /** |
| Parse the given `fieldName` and return its corresponding JsonFieldFlags value. |
| |
| Params: |
| fieldName = the field name to parse |
| |
| Returns: JsonFieldFlags.none on error, otherwise the JsonFieldFlags value |
| corresponding to the given fieldName. |
| */ |
| extern (C++) JsonFieldFlags tryParseJsonField(const(char)* fieldName) |
| { |
| auto fieldNameString = fieldName.toDString(); |
| foreach (idx, enumName; __traits(allMembers, JsonFieldFlags)) |
| { |
| static if (idx > 0) |
| { |
| if (fieldNameString == enumName) |
| return __traits(getMember, JsonFieldFlags, enumName); |
| } |
| } |
| return JsonFieldFlags.none; |
| } |
| |
| /** |
| Determines and returns the compiler interface which is one of `dmd`, `ldc`, |
| `gdc` or `sdc`. Returns `null` if no interface can be determined. |
| */ |
| private extern(D) string determineCompilerInterface() |
| { |
| if (global.vendor == "Digital Mars D") |
| return "dmd"; |
| if (global.vendor == "LDC") |
| return "ldc"; |
| if (global.vendor == "GNU D") |
| return "gdc"; |
| if (global.vendor == "SDC") |
| return "sdc"; |
| return null; |
| } |