| /** | |
| * 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; | |
| } |