// Compiler implementation of the D programming language // Copyright (c) 1999-2013 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. // This implements the JSON capability. #include #include #include #include "rmem.h" #include "dsymbol.h" #include "template.h" #include "aggregate.h" #include "declaration.h" #include "enum.h" #include "module.h" #include "json.h" #include "mtype.h" #include "attrib.h" #include "cond.h" #include "init.h" #include "import.h" #include "id.h" struct JsonOut { OutBuffer *buf; int indentLevel; const char *filename; JsonOut(OutBuffer *buf) {this->buf = buf; indentLevel = 0; filename = NULL;} void indent(); void removeComma(); void comma(); void stringStart(); void stringEnd(); void stringPart(const char* part); void value(const char* s); void value(int value); void valueBool(bool value); void item(const char*); void item(int); void itemBool(bool); void arrayStart(); void arrayEnd(); void objectStart(); void objectEnd(); void propertyStart(const char* name); void property(const char *name, const char* s); void property(const char *name, int value); void propertyBool(const char *name, bool value); void propertyStorageClass(const char *name, StorageClass stc); void property(const char *name, Loc* loc); void property(const char *name, Type* type); void property(const char *name, const char *deconame, Type* type); void property(const char *name, Parameters* parameters); void property(const char *name, enum TRUST trust); void property(const char *name, enum PURE purity); void property(const char *name, enum LINK linkage); }; void json_generate(OutBuffer *buf, Modules *modules) { JsonOut json(buf); json.arrayStart(); for (size_t i = 0; i < modules->dim; i++) { Module *m = (*modules)[i]; if (global.params.verbose) printf("json gen %s\n", m->toChars()); m->toJson(&json); } json.arrayEnd(); json.removeComma(); } void JsonOut::indent() { if (buf->offset >= 1 && buf->data[buf->offset - 1] == '\n') for (int i = 0; i < indentLevel; i++) buf->writeByte(' '); } void JsonOut::removeComma() { if (buf->offset >= 2 && buf->data[buf->offset - 2] == ',' && (buf->data[buf->offset - 1] == '\n' || buf->data[buf->offset - 1] == ' ')) buf->offset -= 2; } void JsonOut::comma() { if (indentLevel > 0) buf->writestring(",\n"); } void JsonOut::stringStart() { buf->writeByte('\"'); } void JsonOut::stringEnd() { buf->writeByte('\"'); } void JsonOut::stringPart(const char *s) { for (; *s; s++) { unsigned char c = (unsigned char) *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. */ void JsonOut::value(const char *s) { stringStart(); stringPart(s); stringEnd(); } void JsonOut::value(int value) { buf->printf("%d", value); } void JsonOut::valueBool(bool value) { buf->writestring(value? "true" : "false"); } /********************************* * Item is an intented value and a comma, for use in arrays */ void JsonOut::item(const char *s) { indent(); value(s); comma(); } void JsonOut::item(int i) { indent(); value(i); comma(); } void JsonOut::itemBool(bool b) { indent(); valueBool(b); comma(); } // Json array functions void JsonOut::arrayStart() { indent(); buf->writestring("[\n"); indentLevel++; } void JsonOut::arrayEnd() { indentLevel--; removeComma(); if (buf->offset >= 2 && buf->data[buf->offset - 2] == '[' && buf->data[buf->offset - 1] == '\n') buf->offset -= 1; else if (!(buf->offset >= 1 && buf->data[buf->offset - 1] == '[')) { buf->writestring("\n"); indent(); } buf->writestring("]"); comma(); } // Json object functions void JsonOut::objectStart() { indent(); buf->writestring("{\n"); indentLevel++; } void JsonOut::objectEnd() { indentLevel--; removeComma(); if (buf->offset >= 2 && buf->data[buf->offset - 2] == '{' && buf->data[buf->offset - 1] == '\n') buf->offset -= 1; else { buf->writestring("\n"); indent(); } buf->writestring("}"); comma(); } // Json object property functions void JsonOut::propertyStart(const char *name) { indent(); value(name); buf->writestring(" : "); } void JsonOut::property(const char *name, const char *s) { if (s == NULL) return; propertyStart(name); value(s); comma(); } void JsonOut::property(const char *name, int i) { propertyStart(name); value(i); comma(); } void JsonOut::propertyBool(const char *name, bool b) { propertyStart(name); valueBool(b); comma(); } void JsonOut::property(const char *name, enum TRUST trust) { switch (trust) { case TRUSTdefault: // Should not be printed //property(name, "default"); break; case TRUSTsystem: property(name, "system"); break; case TRUSTtrusted: property(name, "trusted"); break; case TRUSTsafe: property(name, "safe"); break; default: assert(false); } } void JsonOut::property(const char *name, enum PURE purity) { switch (purity) { case PUREimpure: // Should not be printed //property(name, "impure"); break; case PUREweak: property(name, "weak"); break; case PUREconst: property(name, "const"); break; case PUREstrong: property(name, "strong"); break; case PUREfwdref: property(name, "fwdref"); break; default: assert(false); } } void JsonOut::property(const char *name, enum LINK linkage) { switch (linkage) { case LINKdefault: // Should not be printed //property(name, "default"); break; case LINKd: // Should not be printed //property(name, "d"); break; case LINKc: property(name, "c"); break; case LINKcpp: property(name, "cpp"); break; case LINKwindows: property(name, "windows"); break; case LINKpascal: property(name, "pascal"); break; default: assert(false); } } void JsonOut::propertyStorageClass(const char *name, StorageClass stc) { stc &= STCStorageClass; if (stc) { propertyStart(name); arrayStart(); while (stc) { char tmp[20]; const char *p = StorageClassDeclaration::stcToChars(tmp, stc); assert(p); assert(strlen(p) < sizeof(tmp)); if (p[0] == '@') { indent(); stringStart(); buf->writestring(p); stringEnd(); comma(); } else item(p); } arrayEnd(); } } void JsonOut::property(const char *name, Loc *loc) { if (loc) { const char *filename = loc->filename; if (filename) { if (!this->filename || strcmp(filename, this->filename)) { this->filename = filename; property("file", filename); } } if (loc->linnum) property(name, loc->linnum); } } void JsonOut::property(const char *name, Type *type) { if (type) { property(name, type->toChars()); } } void JsonOut::property(const char *name, const char *deconame, Type *type) { if (type) { if (type->deco) property(deconame, type->deco); else property(name, type->toChars()); } } void JsonOut::property(const char *name, Parameters *parameters) { if (parameters == 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->toChars()); property("type", "deco", p->type); propertyStorageClass("storageClass", p->storageClass); if (p->defaultArg) property("default", p->defaultArg->toChars()); objectEnd(); } arrayEnd(); } /* ========================================================================== */ void Type::toJson(JsonOut *json) { } void TypeSArray::toJson(JsonOut *json) { json->property("elementType", next); json->property("dim", dim->toChars()); } void TypeDArray::toJson(JsonOut *json) { json->property("elementType", next); } void TypeAArray::toJson(JsonOut *json) { json->property("elementType", next); json->property("index", index); } void TypePointer::toJson(JsonOut *json) { json->property("targetType", next); } void TypeReference::toJson(JsonOut *json) { json->property("targetType", next); } void TypeFunction::toJson(JsonOut *json) { if (purity || isnothrow || isproperty || isref) { json->propertyStart("attributes"); json->arrayStart(); if (purity) json->item("pure"); if (isnothrow) json->item("nothrow"); if (isproperty) json->item("@property"); if (isref) json->item("ref"); json->arrayEnd(); } json->property("trust", trust); json->property("purity", purity); json->property("linkage", linkage); json->property("returnType", next); json->property("parameters", parameters); } void TypeDelegate::toJson(JsonOut *json) { next->toJson(json); // next is TypeFunction } void TypeQualified::toJson(JsonOut *json) // ident.ident.ident.etc { json->propertyStart("idents"); json->arrayStart(); for (size_t i = 0; i < idents.dim; i++) { Object *ident = idents[i]; json->item(ident->toChars()); } json->arrayEnd(); } void TypeIdentifier::toJson(JsonOut *json) { TypeQualified::toJson(json); json->property("rawIdentifier", ident->toChars()); json->property("identifier", ident->toHChars2()); } void TypeInstance::toJson(JsonOut *json) { TypeQualified::toJson(json); json->property("tempinst", tempinst->toChars()); } void TypeTypeof::toJson(JsonOut *json) { TypeQualified::toJson(json); json->property("exp", exp->toChars()); json->property("type", exp->type); } void TypeReturn::toJson(JsonOut *json) { TypeQualified::toJson(json); } void TypeStruct::toJson(JsonOut *json) { json->propertyStorageClass("storageClass", sym->storage_class); } void TypeEnum::toJson(JsonOut *json) { sym->jsonProperties(json); } void TypeTypedef::toJson(JsonOut *json) { sym->jsonProperties(json); } void TypeClass::toJson(JsonOut *json) { json->propertyStorageClass("storageClass", sym->storage_class); } void TypeTuple::toJson(JsonOut *json) { json->property("arguments", arguments); } void TypeSlice::toJson(JsonOut *json) { json->property("lower", lwr->toChars()); json->property("upper", upr->toChars()); } void TypeNull::toJson(JsonOut *json) { } void TypeVector::toJson(JsonOut *json) { json->property("basetype", basetype); } /* ========================================================================== */ void Dsymbol::toJson(JsonOut *json) { #if 0 json->objectStart(); jsonProperties(json); json->objectEnd(); #endif } void Dsymbol::jsonProperties(JsonOut *json) { if (!isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes { json->property("name", toChars()); json->property("kind", kind()); } if (prot() != PROTpublic) json->property("protection", Pprotectionnames[prot()]); json->property("comment", (const char *)comment); json->property("line", &loc); #if 0 if (!isModule()) { Module *module = getModule(); if (module) { json->propertyStart("module"); json->objectStart(); module->jsonProperties(json); json->objectEnd(); } Module *accessModule = getAccessModule(); if (accessModule && accessModule != module) { json->propertyStart("accessModule"); json->objectStart(); accessModule->jsonProperties(json); json->objectEnd(); } } #endif } void Module::toJson(JsonOut *json) { json->objectStart(); if (md) json->property("name", md->toChars()); json->property("kind", kind()); json->filename = srcfile->toChars(); json->property("file", json->filename); json->property("comment", (const char *)comment); json->propertyStart("members"); json->arrayStart(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->toJson(json); } json->arrayEnd(); json->objectEnd(); } void Module::jsonProperties(JsonOut *json) { #if 0 Dsymbol::jsonProperties(json); if (md && md->packages) { json->propertyStart("package"); json->arrayStart(); for (size_t i = 0; i < md->packages->dim; i++) { Identifier *pid = (*md->packages)[i]; json->item(pid->toChars()); } json->arrayEnd(); } json->property("prettyName", toPrettyChars()); #endif } void Import::toJson(JsonOut *json) { if (id == Id::object) return; json->objectStart(); json->propertyStart("name"); json->stringStart(); if (packages && packages->dim) { for (size_t i = 0; i < packages->dim; i++) { Identifier *pid = (*packages)[i]; json->stringPart(pid->toChars()); json->buf->writeByte('.'); } } json->stringPart(id->toChars()); json->stringEnd(); json->comma(); json->property("kind", kind()); json->property("comment", (const char *)comment); json->property("line", &loc); if (prot() != PROTpublic) json->property("protection", Pprotectionnames[prot()]); if (aliasId) json->property("alias", aliasId->toChars()); bool hasRenamed = false; bool hasSelective = false; for (size_t i = 0; i < aliases.dim; i++) { // avoid empty "renamed" and "selective" sections if (hasRenamed && hasSelective) break; else if (aliases[i]) hasRenamed = true; else hasSelective = true; } if (hasRenamed) { // import foo : alias1 = target1; json->propertyStart("renamed"); json->objectStart(); for (size_t i = 0; i < aliases.dim; i++) { Identifier *name = names[i]; Identifier *alias = aliases[i]; if (alias) json->property(alias->toChars(), name->toChars()); } json->objectEnd(); } if (hasSelective) { // import foo : target1; json->propertyStart("selective"); json->arrayStart(); for (size_t i = 0; i < names.dim; i++) { Identifier *name = names[i]; if (!aliases[i]) json->item(name->toChars()); } json->arrayEnd(); } json->objectEnd(); } void AttribDeclaration::toJson(JsonOut *json) { Dsymbols *d = include(NULL, NULL); if (d) { for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = (*d)[i]; s->toJson(json); } } } void ConditionalDeclaration::toJson(JsonOut *json) { if (condition->inc) { AttribDeclaration::toJson(json); } } void ClassInfoDeclaration::toJson(JsonOut *json) { } void ModuleInfoDeclaration::toJson(JsonOut *json) { } void TypeInfoDeclaration::toJson(JsonOut *json) { } #if DMDV2 void PostBlitDeclaration::toJson(JsonOut *json) { } #endif void Declaration::toJson(JsonOut *json) { json->objectStart(); //json->property("unknown", "declaration"); jsonProperties(json); json->objectEnd(); } void Declaration::jsonProperties(JsonOut *json) { Dsymbol::jsonProperties(json); json->propertyStorageClass("storageClass", storage_class); json->property("type", "deco", type); // Emit originalType if it differs from type if (type != originalType && originalType) { const char *ostr = originalType->toChars(); if (type) { const char *tstr = type->toChars(); if (strcmp(tstr, ostr)) { //printf("tstr = %s, ostr = %s\n", tstr, ostr); json->property("originalType", ostr); } } else json->property("originalType", ostr); } } void TemplateDeclaration::jsonProperties(JsonOut *json) { Dsymbol::jsonProperties(json); if (onemember && onemember->isCtorDeclaration()) json->property("name", "this"); // __ctor -> this else json->property("name", ident->toChars()); // Foo(T) -> Foo } void TypedefDeclaration::toJson(JsonOut *json) { json->objectStart(); jsonProperties(json); json->property("base", "baseDeco", basetype); json->objectEnd(); } void AggregateDeclaration::toJson(JsonOut *json) { json->objectStart(); jsonProperties(json); ClassDeclaration *cd = isClassDeclaration(); if (cd) { if (cd->baseClass && cd->baseClass->ident != Id::Object) { json->property("base", cd->baseClass->toChars()); } if (cd->interfaces_dim) { json->propertyStart("interfaces"); json->arrayStart(); for (size_t i = 0; i < cd->interfaces_dim; i++) { BaseClass *b = cd->interfaces[i]; json->item(b->base->toChars()); } json->arrayEnd(); } } if (members) { json->propertyStart("members"); json->arrayStart(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->toJson(json); } json->arrayEnd(); } json->objectEnd(); } void FuncDeclaration::toJson(JsonOut *json) { json->objectStart(); jsonProperties(json); TypeFunction *tf = (TypeFunction *)type; if (tf && tf->ty == Tfunction) json->property("parameters", tf->parameters); json->property("endline", &endloc); if (foverrides.dim) { json->propertyStart("overrides"); json->arrayStart(); for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fd = foverrides[i]; json->item(fd->toPrettyChars()); } json->arrayEnd(); } if (fdrequire) { json->propertyStart("in"); fdrequire->toJson(json); } if (fdensure) { json->propertyStart("out"); fdensure->toJson(json); } json->objectEnd(); } void TemplateDeclaration::toJson(JsonOut *json) { json->objectStart(); // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one json->property("kind", "template"); jsonProperties(json); json->propertyStart("parameters"); json->arrayStart(); for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *s = (*parameters)[i]; json->objectStart(); json->property("name", s->ident->toChars()); TemplateTypeParameter *type = s->isTemplateTypeParameter(); if (type) { #if DMDV2 if (s->isTemplateThisParameter()) json->property("kind", "this"); else #endif json->property("kind", "type"); json->property("type", "deco", type->specType); json->property("default", "defaultDeco", type->defaultType); } TemplateValueParameter *value = s->isTemplateValueParameter(); if (value) { json->property("kind", "value"); json->property("type", "deco", value->valType); if (value->specValue) json->property("specValue", value->specValue->toChars()); if (value->defaultValue) json->property("defaultValue", value->defaultValue->toChars()); } TemplateAliasParameter *alias = s->isTemplateAliasParameter(); if (alias) { json->property("kind", "alias"); json->property("type", "deco", alias->specType); if (alias->specAlias) json->property("specAlias", alias->specAlias->toChars()); if (alias->defaultAlias) json->property("defaultAlias", alias->defaultAlias->toChars()); } TemplateTupleParameter *tuple = s->isTemplateTupleParameter(); if (tuple) { json->property("kind", "tuple"); } json->objectEnd(); } json->arrayEnd(); json->propertyStart("members"); json->arrayStart(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->toJson(json); } json->arrayEnd(); json->objectEnd(); } void EnumDeclaration::toJson(JsonOut *json) { if (isAnonymous()) { if (members) { for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->toJson(json); } } return; } json->objectStart(); jsonProperties(json); json->property("base", "baseDeco", memtype); if (members) { json->propertyStart("members"); json->arrayStart(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->toJson(json); } json->arrayEnd(); } json->objectEnd(); } void EnumMember::toJson(JsonOut *json) { json->objectStart(); jsonProperties(json); json->property("type", "deco", type); json->objectEnd(); } void VarDeclaration::toJson(JsonOut *json) { json->objectStart(); jsonProperties(json); if (init) json->property("init", init->toChars()); if (isField()) json->property("offset", offset); if (alignment && alignment != STRUCTALIGN_DEFAULT) json->property("align", alignment); json->objectEnd(); } void TemplateMixin::toJson(JsonOut *json) { json->objectStart(); jsonProperties(json); json->objectEnd(); }