ldc/ddmd/json.d
David Nadlinger 9f998a398d Initial merge of upstream v2.071.0-b2
Notably, the glue layer side of the changed multiple interface
inheritance layout (DMD a54e89d) has not been implemented yet.

This corresponds to DMD commit 3f6a763c0589dd03c1c206eafd434b593702564e.
2016-04-03 15:15:14 +01:00

786 lines
20 KiB
D

// Compiler implementation of the D programming language
// Copyright (c) 1999-2015 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
module ddmd.json;
import core.stdc.stdio;
import core.stdc.string;
import ddmd.aggregate;
import ddmd.arraytypes;
import ddmd.attrib;
import ddmd.dclass;
import ddmd.declaration;
import ddmd.denum;
import ddmd.dimport;
import ddmd.dmodule;
import ddmd.dsymbol;
import ddmd.dtemplate;
import ddmd.expression;
import ddmd.func;
import ddmd.globals;
import ddmd.hdrgen;
import ddmd.id;
import ddmd.identifier;
import ddmd.mtype;
import ddmd.root.outbuffer;
import ddmd.visitor;
extern (C++) final class ToJsonVisitor : Visitor
{
alias visit = super.visit;
public:
OutBuffer* buf;
int indentLevel;
const(char)* filename;
extern (D) this(OutBuffer* buf)
{
this.buf = buf;
}
void indent()
{
if (buf.offset >= 1 && buf.data[buf.offset - 1] == '\n')
for (int i = 0; i < indentLevel; i++)
buf.writeByte(' ');
}
void 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 comma()
{
if (indentLevel > 0)
buf.writestring(",\n");
}
void stringStart()
{
buf.writeByte('\"');
}
void stringEnd()
{
buf.writeByte('\"');
}
void stringPart(const(char)* s)
{
for (; *s; s++)
{
char c = cast(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 value(const(char)* s)
{
stringStart();
stringPart(s);
stringEnd();
}
void value(int value)
{
buf.printf("%d", value);
}
void valueBool(bool value)
{
buf.writestring(value ? "true" : "false");
}
/*********************************
* Item is an intented value and a comma, for use in arrays
*/
void item(const(char)* s)
{
indent();
value(s);
comma();
}
void item(int i)
{
indent();
value(i);
comma();
}
void itemBool(bool b)
{
indent();
valueBool(b);
comma();
}
// Json array functions
void arrayStart()
{
indent();
buf.writestring("[\n");
indentLevel++;
}
void 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 objectStart()
{
indent();
buf.writestring("{\n");
indentLevel++;
}
void 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 propertyStart(const(char)* name)
{
indent();
value(name);
buf.writestring(" : ");
}
void property(const(char)* name, const(char)* s)
{
if (s is null)
return;
propertyStart(name);
value(s);
comma();
}
void property(const(char)* name, int i)
{
propertyStart(name);
value(i);
comma();
}
void propertyBool(const(char)* name, bool b)
{
propertyStart(name);
valueBool(b);
comma();
}
void property(const(char)* name, 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 property(const(char)* name, 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 property(const(char)* name, 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 propertyStorageClass(const(char)* name, StorageClass stc)
{
stc &= STCStorageClass;
if (stc)
{
propertyStart(name);
arrayStart();
while (stc)
{
const(char)* p = stcToChars(stc);
assert(p);
item(p);
}
arrayEnd();
}
}
void property(const(char)* linename, const(char)* charname, 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(linename, loc.linnum);
if (loc.charnum)
property(charname, loc.charnum);
}
}
}
void property(const(char)* name, Type type)
{
if (type)
{
property(name, type.toChars());
}
}
void property(const(char)* name, const(char)* deconame, Type type)
{
if (type)
{
if (type.deco)
property(deconame, type.deco);
else
property(name, type.toChars());
}
}
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.toChars());
property("type", "deco", p.type);
propertyStorageClass("storageClass", p.storageClass);
if (p.defaultArg)
property("default", p.defaultArg.toChars());
objectEnd();
}
}
arrayEnd();
}
/* ========================================================================== */
void jsonProperties(Dsymbol s)
{
if (s.isModule())
return;
if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
{
property("name", s.toChars());
property("kind", s.kind());
}
if (s.prot().kind != PROTpublic) // TODO: How about package(names)?
property("protection", protectionToChars(s.prot().kind));
if (EnumMember em = s.isEnumMember())
{
if (em.origValue)
property("value", em.origValue.toChars());
}
property("comment", s.comment);
property("line", "char", &s.loc);
}
void jsonProperties(Declaration d)
{
jsonProperties(cast(Dsymbol)d);
propertyStorageClass("storageClass", d.storage_class);
property("type", "deco", d.type);
// Emit originalType if it differs from type
if (d.type != d.originalType && d.originalType)
{
const(char)* ostr = d.originalType.toChars();
if (d.type)
{
const(char)* tstr = d.type.toChars();
if (strcmp(tstr, ostr))
{
//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.toChars()); // Foo(T) -> Foo
}
/* ========================================================================== */
override void visit(Dsymbol s)
{
}
override void visit(Module s)
{
objectStart();
if (s.md)
property("name", s.md.toChars());
property("kind", s.kind());
filename = s.srcfile.toChars();
property("file", filename);
property("comment", s.comment);
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();
if (s.packages && s.packages.dim)
{
for (size_t i = 0; i < s.packages.dim; i++)
{
Identifier pid = (*s.packages)[i];
stringPart(pid.toChars());
buf.writeByte('.');
}
}
stringPart(s.id.toChars());
stringEnd();
comma();
property("kind", s.kind());
property("comment", s.comment);
property("line", "char", &s.loc);
if (s.prot().kind != PROTpublic)
property("protection", protectionToChars(s.prot().kind));
if (s.aliasId)
property("alias", s.aliasId.toChars());
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++)
{
Identifier name = s.names[i];
Identifier _alias = s.aliases[i];
if (_alias)
property(_alias.toChars(), name.toChars());
}
objectEnd();
}
if (hasSelective)
{
// import foo : target1;
propertyStart("selective");
arrayStart();
for (size_t i = 0; i < s.names.dim; i++)
{
Identifier name = s.names[i];
if (!s.aliases[i])
item(name.toChars());
}
arrayEnd();
}
objectEnd();
}
override void visit(AttribDeclaration d)
{
Dsymbols* ds = d.include(null, 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)
{
visit(cast(AttribDeclaration)d);
}
}
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));
}
if (cd.interfaces.length)
{
propertyStart("interfaces");
arrayStart();
foreach (b; cd.interfaces)
{
item(b.sym.toPrettyChars(true));
}
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.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());
}
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.toChars());
TemplateTypeParameter type = s.isTemplateTypeParameter();
if (type)
{
if (s.isTemplateThisParameter())
property("kind", "this");
else
property("kind", "type");
property("type", "deco", type.specType);
property("default", "defaultDeco", type.defaultType);
}
TemplateValueParameter value = s.isTemplateValueParameter();
if (value)
{
property("kind", "value");
property("type", "deco", value.valType);
if (value.specValue)
property("specValue", value.specValue.toChars());
if (value.defaultValue)
property("defaultValue", value.defaultValue.toChars());
}
TemplateAliasParameter _alias = s.isTemplateAliasParameter();
if (_alias)
{
property("kind", "alias");
property("type", "deco", _alias.specType);
if (_alias.specAlias)
property("specAlias", _alias.specAlias.toChars());
if (_alias.defaultAlias)
property("defaultAlias", _alias.defaultAlias.toChars());
}
TemplateTupleParameter tuple = s.isTemplateTupleParameter();
if (tuple)
{
property("kind", "tuple");
}
objectEnd();
}
arrayEnd();
Expression expression = d.constraint;
if (expression)
{
property("constraint", expression.toChars());
}
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)
{
objectStart();
jsonProperties(d);
if (d._init)
property("init", d._init.toChars());
if (d.isField())
property("offset", d.offset);
if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT)
property("align", d.alignment);
objectEnd();
}
override void visit(TemplateMixin d)
{
objectStart();
jsonProperties(d);
objectEnd();
}
}
extern (C++) void json_generate(OutBuffer* buf, Modules* modules)
{
scope ToJsonVisitor json = new ToJsonVisitor(buf);
json.arrayStart();
for (size_t i = 0; i < modules.dim; i++)
{
Module m = (*modules)[i];
if (global.params.verbose)
fprintf(global.stdmsg, "json gen %s\n", m.toChars());
m.accept(json);
}
json.arrayEnd();
json.removeComma();
}