2 * Code for generating .json descriptions of the module when passing the `-X` flag to dmd.
4 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 https://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
14 import core
.stdc
.stdio
;
15 import core
.stdc
.string
;
17 import dmd
.arraytypes
;
22 import dmd
.declaration
;
29 import dmd
.expression
;
34 import dmd
.identifier
;
37 import dmd
.common
.outbuffer
;
38 import dmd
.root
.rootobject
;
39 import dmd
.root
.string
;
44 extern (C
) char* getcwd(char* buffer
, size_t maxlen
);
46 import core
.sys
.posix
.unistd
: getcwd
;
49 private extern (C
++) final class ToJsonVisitor
: Visitor
51 alias visit
= Visitor
.visit
;
55 const(char)[] filename
;
57 extern (D
) this(OutBuffer
* buf
) scope @safe
65 if (buf
.length
>= 1 && (*buf
)[buf
.length
- 1] == '\n')
66 for (int i
= 0; i
< indentLevel
; i
++)
72 if (buf
.length
>= 2 && (*buf
)[buf
.length
- 2] == ',' && ((*buf
)[buf
.length
- 1] == '\n' ||
(*buf
)[buf
.length
- 1] == ' '))
73 buf
.setsize(buf
.length
- 2);
79 buf
.writestring(",\n");
92 extern(D
) void stringPart(const char[] s
)
99 buf
.writestring("\\n");
102 buf
.writestring("\\r");
105 buf
.writestring("\\t");
108 buf
.writestring("\\\"");
111 buf
.writestring("\\\\");
114 buf
.writestring("\\b");
117 buf
.writestring("\\f");
121 buf
.printf("\\u%04x", c
);
124 // Note that UTF-8 chars pass through here just fine
132 // Json value functions
133 /*********************************
134 * Encode string into buf, and wrap it in double quotes.
136 extern(D
) void value(const char[] s
)
143 void value(int value
)
153 void valueBool(bool value
)
155 buf
.writestring(value ?
"true" : "false");
158 /*********************************
159 * Item is an intented value and a comma, for use in arrays
161 extern(D
) void item(const char[] s
)
175 void itemBool(const bool b
)
182 // Json array functions
186 buf
.writestring("[\n");
194 if (buf
.length
>= 2 && (*buf
)[buf
.length
- 2] == '[' && (*buf
)[buf
.length
- 1] == '\n')
195 buf
.setsize(buf
.length
- 1);
196 else if (!(buf
.length
>= 1 && (*buf
)[buf
.length
- 1] == '['))
198 buf
.writestring("\n");
201 buf
.writestring("]");
205 // Json object functions
209 buf
.writestring("{\n");
217 if (buf
.length
>= 2 && (*buf
)[buf
.length
- 2] == '{' && (*buf
)[buf
.length
- 1] == '\n')
218 buf
.setsize(buf
.length
- 1);
221 buf
.writestring("\n");
224 buf
.writestring("}");
228 // Json object property functions
229 extern(D
) void propertyStart(const char[] name
)
233 buf
.writestring(" : ");
237 Write the given string object property only if `s` is not null.
240 name = the name of the object property
241 s = the string value of the object property
243 extern(D
) void property(const char[] name
, const char[] s
)
253 Write the given string object property.
256 name = the name of the object property
257 s = the string value of the object property
259 extern(D
) void requiredProperty(const char[] name
, const char[] s
)
263 buf
.writestring("null");
269 extern(D
) void property(const char[] name
, int i
)
276 extern(D
) void propertyBool(const char[] name
, const bool b
)
283 extern(D
) void property(const char[] name
, TRUST trust
)
288 // Should not be printed
289 //property(name, "default");
291 case TRUST
.system
: return property(name
, "system");
292 case TRUST
.trusted
: return property(name
, "trusted");
293 case TRUST
.safe
: return property(name
, "safe");
297 extern(D
) void property(const char[] name
, PURE purity
)
299 final switch (purity
)
302 // Should not be printed
303 //property(name, "impure");
305 case PURE
.weak
: return property(name
, "weak");
306 case PURE
.const_
: return property(name
, "strong");
307 case PURE
.fwdref
: return property(name
, "fwdref");
311 extern(D
) void property(const char[] name
, const LINK linkage
)
313 final switch (linkage
)
316 // Should not be printed
317 //property(name, "default");
320 // Should not be printed
321 //property(name, "d");
323 case LINK
.system
: return property(name
, "system");
324 case LINK
.c
: return property(name
, "c");
325 case LINK
.cpp
: return property(name
, "cpp");
326 case LINK
.windows
: return property(name
, "windows");
327 case LINK
.objc
: return property(name
, "objc");
331 extern(D
) void propertyStorageClass(const char[] name
, StorageClass
stc)
333 stc &= STC
.visibleStorageClasses
;
340 auto p
= stcToString(stc);
348 extern(D
) void property(const char[] linename
, const char[] charname
, const ref Loc loc
)
352 if (auto filename
= loc
.filename
.toDString
)
354 if (filename
!= this.filename
)
356 this.filename
= filename
;
357 property("file", filename
);
362 property(linename
, loc
.linnum
);
364 property(charname
, loc
.charnum
);
369 extern(D
) void property(const char[] name
, Type type
)
373 property(name
, type
.toString());
377 extern(D
) void property(const char[] name
, const char[] deconame
, Type type
)
382 property(deconame
, type
.deco
.toDString
);
384 property(name
, type
.toString());
388 extern(D
) void property(const char[] name
, Parameters
* parameters
)
390 if (parameters
is null || parameters
.length
== 0)
396 for (size_t i
= 0; i
< parameters
.length
; i
++)
398 Parameter p
= (*parameters
)[i
];
401 property("name", p
.ident
.toString());
402 property("type", "deco", p
.type
);
403 propertyStorageClass("storageClass", p
.storageClass
);
405 property("default", p
.defaultArg
.toString());
412 /* ========================================================================== */
413 void jsonProperties(Dsymbol s
)
417 if (!s
.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
419 property("name", s
.toString());
420 if (s
.isStaticCtorDeclaration())
422 property("kind", s
.isSharedStaticCtorDeclaration()
423 ?
"shared static constructor" : "static constructor");
425 else if (s
.isStaticDtorDeclaration())
427 property("kind", s
.isSharedStaticDtorDeclaration()
428 ?
"shared static destructor" : "static destructor");
431 property("kind", s
.kind
.toDString
);
433 // TODO: How about package(names)?
434 property("protection", visibilityToString(s
.visible().kind
));
435 if (EnumMember em
= s
.isEnumMember())
438 property("value", em
.origValue
.toString());
440 property("comment", s
.comment
.toDString
);
441 property("line", "char", s
.loc
);
444 void jsonProperties(Declaration d
)
446 if (d
.storage_class
& STC
.local
)
448 jsonProperties(cast(Dsymbol
)d
);
449 propertyStorageClass("storageClass", d
.storage_class
);
450 property("linkage", d
._linkage
);
451 property("type", "deco", d
.type
);
452 // Emit originalType if it differs from type
453 if (d
.type
!= d
.originalType
&& d
.originalType
)
455 auto ostr
= d
.originalType
.toString();
458 auto tstr
= d
.type
.toString();
461 //printf("tstr = %s, ostr = %s\n", tstr, ostr);
462 property("originalType", ostr
);
466 property("originalType", ostr
);
470 void jsonProperties(TemplateDeclaration td
)
472 jsonProperties(cast(Dsymbol
)td
);
473 if (td
.onemember
&& td
.onemember
.isCtorDeclaration())
474 property("name", "this"); // __ctor -> this
476 property("name", td
.ident
.toString()); // Foo(T) -> Foo
479 /* ========================================================================== */
480 override void visit(Dsymbol s
)
484 override void visit(Module s
)
488 property("name", s
.md
.toString());
489 property("kind", s
.kind
.toDString
);
490 filename
= s
.srcfile
.toString();
491 property("file", filename
);
492 property("comment", s
.comment
.toDString
);
493 propertyStart("members");
495 for (size_t i
= 0; i
< s
.members
.length
; i
++)
497 (*s
.members
)[i
].accept(this);
503 override void visit(Import s
)
505 if (s
.id
== Id
.object
)
508 propertyStart("name");
510 foreach (const pid
; s
.packages
){
511 stringPart(pid
.toString());
514 stringPart(s
.id
.toString());
517 property("kind", s
.kind
.toDString
);
518 property("comment", s
.comment
.toDString
);
519 property("line", "char", s
.loc
);
520 if (s
.visible().kind
!= Visibility
.Kind
.public_
)
521 property("protection", visibilityToString(s
.visible().kind
));
523 property("alias", s
.aliasId
.toString());
524 bool hasRenamed
= false;
525 bool hasSelective
= false;
526 for (size_t i
= 0; i
< s
.aliases
.length
; i
++)
528 // avoid empty "renamed" and "selective" sections
529 if (hasRenamed
&& hasSelective
)
531 else if (s
.aliases
[i
])
538 // import foo : alias1 = target1;
539 propertyStart("renamed");
541 for (size_t i
= 0; i
< s
.aliases
.length
; i
++)
543 const name
= s
.names
[i
];
544 const _alias
= s
.aliases
[i
];
546 property(_alias
.toString(), name
.toString());
552 // import foo : target1;
553 propertyStart("selective");
555 foreach (i
, name
; s
.names
)
558 item(name
.toString());
565 override void visit(AttribDeclaration d
)
567 Dsymbols
* ds = d
.include(null);
570 for (size_t i
= 0; i
< ds.length
; i
++)
572 Dsymbol s
= (*ds)[i
];
578 override void visit(ConditionalDeclaration d
)
580 if (d
.condition
.inc != Include
.notComputed
)
582 visit(cast(AttribDeclaration
)d
);
583 return; // Don't visit the if/else bodies again below
585 Dsymbols
* ds = d
.decl ? d
.decl
: d
.elsedecl
;
586 for (size_t i
= 0; i
< ds.length
; i
++)
588 Dsymbol s
= (*ds)[i
];
593 override void visit(TypeInfoDeclaration d
)
597 override void visit(PostBlitDeclaration d
)
601 override void visit(Declaration d
)
604 //property("unknown", "declaration");
609 override void visit(AggregateDeclaration d
)
613 ClassDeclaration cd
= d
.isClassDeclaration();
616 if (cd
.baseClass
&& cd
.baseClass
.ident
!= Id
.Object
)
618 property("base", cd
.baseClass
.toPrettyChars(true).toDString
);
620 if (cd
.interfaces
.length
)
622 propertyStart("interfaces");
624 foreach (b
; cd
.interfaces
)
626 item(b
.sym
.toPrettyChars(true).toDString
);
633 propertyStart("members");
635 for (size_t i
= 0; i
< d
.members
.length
; i
++)
637 Dsymbol s
= (*d
.members
)[i
];
645 override void visit(FuncDeclaration d
)
649 TypeFunction tf
= cast(TypeFunction
)d
.type
;
650 if (tf
&& tf
.ty
== Tfunction
)
651 property("parameters", tf
.parameterList
.parameters
);
652 property("endline", "endchar", d
.endloc
);
653 if (d
.foverrides
.length
)
655 propertyStart("overrides");
657 for (size_t i
= 0; i
< d
.foverrides
.length
; i
++)
659 FuncDeclaration fd
= d
.foverrides
[i
];
660 item(fd
.toPrettyChars().toDString
);
667 d
.fdrequire
.accept(this);
671 propertyStart("out");
672 d
.fdensure
.accept(this);
677 override void visit(TemplateDeclaration d
)
680 // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one
681 property("kind", "template");
683 propertyStart("parameters");
685 for (size_t i
= 0; i
< d
.parameters
.length
; i
++)
687 TemplateParameter s
= (*d
.parameters
)[i
];
689 property("name", s
.ident
.toString());
691 if (auto type
= s
.isTemplateTypeParameter())
693 if (s
.isTemplateThisParameter())
694 property("kind", "this");
696 property("kind", "type");
697 property("type", "deco", type
.specType
);
698 property("default", "defaultDeco", type
.defaultType
);
701 if (auto value
= s
.isTemplateValueParameter())
703 property("kind", "value");
704 property("type", "deco", value
.valType
);
706 property("specValue", value
.specValue
.toString());
707 if (value
.defaultValue
)
708 property("defaultValue", value
.defaultValue
.toString());
711 if (auto _alias
= s
.isTemplateAliasParameter())
713 property("kind", "alias");
714 property("type", "deco", _alias
.specType
);
715 if (_alias
.specAlias
)
716 property("specAlias", _alias
.specAlias
.toString());
717 if (_alias
.defaultAlias
)
718 property("defaultAlias", _alias
.defaultAlias
.toString());
721 if (auto tuple
= s
.isTemplateTupleParameter())
723 property("kind", "tuple");
729 Expression expression
= d
.constraint
;
732 property("constraint", expression
.toString());
734 propertyStart("members");
736 for (size_t i
= 0; i
< d
.members
.length
; i
++)
738 Dsymbol s
= (*d
.members
)[i
];
745 override void visit(EnumDeclaration d
)
751 for (size_t i
= 0; i
< d
.members
.length
; i
++)
753 Dsymbol s
= (*d
.members
)[i
];
761 property("base", "baseDeco", d
.memtype
);
764 propertyStart("members");
766 for (size_t i
= 0; i
< d
.members
.length
; i
++)
768 Dsymbol s
= (*d
.members
)[i
];
776 override void visit(EnumMember s
)
779 jsonProperties(cast(Dsymbol
)s
);
780 property("type", "deco", s
.origType
);
784 override void visit(VarDeclaration d
)
786 if (d
.storage_class
& STC
.local
)
791 property("init", toString(d
._init
));
793 property("offset", d
.offset
);
794 if (!d
.alignment
.isUnknown() && !d
.alignment
.isDefault())
795 property("align", d
.alignment
.get());
799 override void visit(TemplateMixin d
)
807 Generate an array of module objects that represent the syntax of each
811 modules = array of the "root modules"
813 private void generateModules(ref Modules modules
)
818 if (global
.params
.v
.verbose
)
819 message("json gen %s", m
.toChars());
826 Generate the "compilerInfo" object which contains information about the compiler
827 such as the filename, version, supported features, etc.
829 private void generateCompilerInfo()
831 import dmd
.target
: target
;
833 requiredProperty("vendor", global
.compileEnv
.vendor
);
834 requiredProperty("version", global
.versionString());
835 property("__VERSION__", global
.versionNumber());
836 requiredProperty("interface", determineCompilerInterface());
837 property("size_t", size_t
.sizeof
);
838 propertyStart("platforms");
840 if (target
.os
== Target
.OS
.Windows
)
847 if (target
.os
== Target
.OS
.linux
)
849 else if (target
.os
== Target
.OS
.OSX
)
851 else if (target
.os
== Target
.OS
.FreeBSD
)
856 else if (target
.os
== Target
.OS
.OpenBSD
)
861 else if (target
.os
== Target
.OS
.Solaris
)
869 propertyStart("architectures");
871 item(target
.architectureName
);
874 propertyStart("predefinedVersions");
876 if (global
.versionids
)
878 foreach (const versionid
; *global
.versionids
)
880 item(versionid
.toString());
885 propertyStart("supportedFeatures");
888 scope(exit
) objectEnd();
889 propertyBool("includeImports", true);
895 Generate the "buildInfo" object which contains information specific to the
896 current build such as CWD, importPaths, configFile, etc.
898 private void generateBuildInfo()
901 requiredProperty("cwd", getcwd(null, 0).toDString
);
902 requiredProperty("argv0", global
.params
.argv0
);
903 requiredProperty("config", global
.inifilename
);
904 requiredProperty("libName", global
.params
.libname
);
906 propertyStart("importPaths");
908 if (global
.params
.imppath
)
910 foreach (importPath
; *global
.params
.imppath
)
912 item(importPath
.toDString
);
917 propertyStart("objectFiles");
919 foreach (objfile
; global
.params
.objfiles
)
921 item(objfile
.toDString
);
925 propertyStart("libraryFiles");
927 foreach (lib
; global
.params
.libfiles
)
933 propertyStart("ddocFiles");
935 foreach (ddocFile
; global
.params
.ddoc
.files
)
937 item(ddocFile
.toDString
);
941 requiredProperty("mapFile", global
.params
.mapfile
);
942 requiredProperty("resourceFile", global
.params
.resfile
);
943 requiredProperty("defFile", global
.params
.deffile
);
949 Generate the "semantics" object which contains a 'modules' field representing
950 semantic information about all the modules used in the compilation such as
951 module name, isRoot, contentImportedFiles, etc.
953 private void generateSemantics()
956 propertyStart("modules");
958 foreach (m
; Module
.amodules
)
961 requiredProperty("name", m
.md ? m
.md
.toString() : null);
962 requiredProperty("file", m
.srcfile
.toString());
963 propertyBool("isRoot", m
.isRoot());
964 if(m
.contentImportedFiles
.length
> 0)
966 propertyStart("contentImports");
968 foreach (file
; m
.contentImportedFiles
)
970 item(file
.toDString
);
981 /***********************************
982 * Generate json for the modules.
984 * modules = array of Modules
985 * buf = write json output to buf
987 extern (C
++) void json_generate(ref Modules modules
, ref OutBuffer buf
)
989 scope ToJsonVisitor json
= new ToJsonVisitor(&buf
);
990 // write trailing newline
991 scope(exit
) buf
.writeByte('\n');
993 if (global
.params
.jsonFieldFlags
== 0)
995 // Generate the original format, which is just an array
996 // of modules representing their syntax.
997 json
.generateModules(modules
);
1002 // Generate the new format which is an object where each
1003 // output option is its own field.
1006 if (global
.params
.jsonFieldFlags
& JsonFieldFlags
.compilerInfo
)
1008 json
.propertyStart("compilerInfo");
1009 json
.generateCompilerInfo();
1011 if (global
.params
.jsonFieldFlags
& JsonFieldFlags
.buildInfo
)
1013 json
.propertyStart("buildInfo");
1014 json
.generateBuildInfo();
1016 if (global
.params
.jsonFieldFlags
& JsonFieldFlags
.modules
)
1018 json
.propertyStart("modules");
1019 json
.generateModules(modules
);
1021 if (global
.params
.jsonFieldFlags
& JsonFieldFlags
.semantics
)
1023 json
.propertyStart("semantics");
1024 json
.generateSemantics();
1031 A string listing the name of each JSON field. Useful for errors messages.
1033 enum jsonFieldNames
= () {
1036 foreach (idx
, enumName
; __traits(allMembers
, JsonFieldFlags
))
1040 s
~= prefix
~ "`" ~ enumName
~ "`";
1048 Parse the given `fieldName` and return its corresponding JsonFieldFlags value.
1051 fieldName = the field name to parse
1053 Returns: JsonFieldFlags.none on error, otherwise the JsonFieldFlags value
1054 corresponding to the given fieldName.
1056 extern (C
++) JsonFieldFlags
tryParseJsonField(const(char)* fieldName
)
1058 auto fieldNameString
= fieldName
.toDString();
1059 foreach (idx
, enumName
; __traits(allMembers
, JsonFieldFlags
))
1063 if (fieldNameString
== enumName
)
1064 return __traits(getMember
, JsonFieldFlags
, enumName
);
1067 return JsonFieldFlags
.none
;
1071 Determines and returns the compiler interface which is one of `dmd`, `ldc`,
1072 `gdc` or `sdc`. Returns `null` if no interface can be determined.
1074 private extern(D
) string
determineCompilerInterface()
1076 if (global
.compileEnv
.vendor
== "Digital Mars D")
1078 if (global
.compileEnv
.vendor
== "LDC")
1080 if (global
.compileEnv
.vendor
== "GNU D")
1082 if (global
.compileEnv
.vendor
== "SDC")