1 /** 2 * Code for generating .json descriptions of the module when passing the `-X` flag to dmd. 3 * 4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://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 10 */ 11 12 module dmd.json; 13 14 import core.stdc.stdio; 15 import core.stdc.string; 16 import dmd.aggregate; 17 import dmd.arraytypes; 18 import dmd.attrib; 19 import dmd.cond; 20 import dmd.dclass; 21 import dmd.declaration; 22 import dmd.denum; 23 import dmd.dimport; 24 import dmd.dmodule; 25 import dmd.dsymbol; 26 import dmd.dtemplate; 27 import dmd.errors; 28 import dmd.expression; 29 import dmd.func; 30 import dmd.globals; 31 import dmd.hdrgen; 32 import dmd.id; 33 import dmd.identifier; 34 import dmd.mtype; 35 import dmd.root.outbuffer; 36 import dmd.root.rootobject; 37 import dmd.root.string; 38 import dmd.visitor; 39 40 version(Windows) { 41 extern (C) char* getcwd(char* buffer, size_t maxlen); 42 } else { 43 import core.sys.posix.unistd : getcwd; 44 } 45 46 private extern (C++) final class ToJsonVisitor : Visitor 47 { 48 alias visit = Visitor.visit; 49 public: 50 OutBuffer* buf; 51 int indentLevel; 52 const(char)[] filename; 53 54 extern (D) this(OutBuffer* buf) 55 { 56 this.buf = buf; 57 } 58 59 60 void indent() 61 { 62 if (buf.length >= 1 && (*buf)[buf.length - 1] == '\n') 63 for (int i = 0; i < indentLevel; i++) 64 buf.writeByte(' '); 65 } 66 67 void removeComma() 68 { 69 if (buf.length >= 2 && (*buf)[buf.length - 2] == ',' && ((*buf)[buf.length - 1] == '\n' || (*buf)[buf.length - 1] == ' ')) 70 buf.setsize(buf.length - 2); 71 } 72 73 void comma() 74 { 75 if (indentLevel > 0) 76 buf.writestring(",\n"); 77 } 78 79 void stringStart() 80 { 81 buf.writeByte('\"'); 82 } 83 84 void stringEnd() 85 { 86 buf.writeByte('\"'); 87 } 88 89 extern(D) void stringPart(const char[] s) 90 { 91 foreach (char c; s) 92 { 93 switch (c) 94 { 95 case '\n': 96 buf.writestring("\\n"); 97 break; 98 case '\r': 99 buf.writestring("\\r"); 100 break; 101 case '\t': 102 buf.writestring("\\t"); 103 break; 104 case '\"': 105 buf.writestring("\\\""); 106 break; 107 case '\\': 108 buf.writestring("\\\\"); 109 break; 110 case '\b': 111 buf.writestring("\\b"); 112 break; 113 case '\f': 114 buf.writestring("\\f"); 115 break; 116 default: 117 if (c < 0x20) 118 buf.printf("\\u%04x", c); 119 else 120 { 121 // Note that UTF-8 chars pass through here just fine 122 buf.writeByte(c); 123 } 124 break; 125 } 126 } 127 } 128 129 // Json value functions 130 /********************************* 131 * Encode string into buf, and wrap it in double quotes. 132 */ 133 extern(D) void value(const char[] s) 134 { 135 stringStart(); 136 stringPart(s); 137 stringEnd(); 138 } 139 140 void value(int value) 141 { 142 if (value < 0) 143 { 144 buf.writeByte('-'); 145 value = -value; 146 } 147 buf.print(value); 148 } 149 150 void valueBool(bool value) 151 { 152 buf.writestring(value ? "true" : "false"); 153 } 154 155 /********************************* 156 * Item is an intented value and a comma, for use in arrays 157 */ 158 extern(D) void item(const char[] s) 159 { 160 indent(); 161 value(s); 162 comma(); 163 } 164 165 void item(int i) 166 { 167 indent(); 168 value(i); 169 comma(); 170 } 171 172 void itemBool(const bool b) 173 { 174 indent(); 175 valueBool(b); 176 comma(); 177 } 178 179 // Json array functions 180 void arrayStart() 181 { 182 indent(); 183 buf.writestring("[\n"); 184 indentLevel++; 185 } 186 187 void arrayEnd() 188 { 189 indentLevel--; 190 removeComma(); 191 if (buf.length >= 2 && (*buf)[buf.length - 2] == '[' && (*buf)[buf.length - 1] == '\n') 192 buf.setsize(buf.length - 1); 193 else if (!(buf.length >= 1 && (*buf)[buf.length - 1] == '[')) 194 { 195 buf.writestring("\n"); 196 indent(); 197 } 198 buf.writestring("]"); 199 comma(); 200 } 201 202 // Json object functions 203 void objectStart() 204 { 205 indent(); 206 buf.writestring("{\n"); 207 indentLevel++; 208 } 209 210 void objectEnd() 211 { 212 indentLevel--; 213 removeComma(); 214 if (buf.length >= 2 && (*buf)[buf.length - 2] == '{' && (*buf)[buf.length - 1] == '\n') 215 buf.setsize(buf.length - 1); 216 else 217 { 218 buf.writestring("\n"); 219 indent(); 220 } 221 buf.writestring("}"); 222 comma(); 223 } 224 225 // Json object property functions 226 extern(D) void propertyStart(const char[] name) 227 { 228 indent(); 229 value(name); 230 buf.writestring(" : "); 231 } 232 233 /** 234 Write the given string object property only if `s` is not null. 235 236 Params: 237 name = the name of the object property 238 s = the string value of the object property 239 */ 240 extern(D) void property(const char[] name, const char[] s) 241 { 242 if (s is null) 243 return; 244 propertyStart(name); 245 value(s); 246 comma(); 247 } 248 249 /** 250 Write the given string object property. 251 252 Params: 253 name = the name of the object property 254 s = the string value of the object property 255 */ 256 extern(D) void requiredProperty(const char[] name, const char[] s) 257 { 258 propertyStart(name); 259 if (s is null) 260 buf.writestring("null"); 261 else 262 value(s); 263 comma(); 264 } 265 266 extern(D) void property(const char[] name, int i) 267 { 268 propertyStart(name); 269 value(i); 270 comma(); 271 } 272 273 extern(D) void propertyBool(const char[] name, const bool b) 274 { 275 propertyStart(name); 276 valueBool(b); 277 comma(); 278 } 279 280 extern(D) void property(const char[] name, TRUST trust) 281 { 282 final switch (trust) 283 { 284 case TRUST.default_: 285 // Should not be printed 286 //property(name, "default"); 287 break; 288 case TRUST.system: return property(name, "system"); 289 case TRUST.trusted: return property(name, "trusted"); 290 case TRUST.safe: return property(name, "safe"); 291 } 292 } 293 294 extern(D) void property(const char[] name, PURE purity) 295 { 296 final switch (purity) 297 { 298 case PURE.impure: 299 // Should not be printed 300 //property(name, "impure"); 301 break; 302 case PURE.weak: return property(name, "weak"); 303 case PURE.const_: return property(name, "const"); 304 case PURE.strong: return property(name, "strong"); 305 case PURE.fwdref: return property(name, "fwdref"); 306 } 307 } 308 309 extern(D) void property(const char[] name, const LINK linkage) 310 { 311 final switch (linkage) 312 { 313 case LINK.default_: 314 // Should not be printed 315 //property(name, "default"); 316 break; 317 case LINK.d: 318 // Should not be printed 319 //property(name, "d"); 320 break; 321 case LINK.system: 322 // Should not be printed 323 //property(name, "system"); 324 break; 325 case LINK.c: return property(name, "c"); 326 case LINK.cpp: return property(name, "cpp"); 327 case LINK.windows: return property(name, "windows"); 328 case LINK.objc: return property(name, "objc"); 329 } 330 } 331 332 extern(D) void propertyStorageClass(const char[] name, StorageClass stc) 333 { 334 stc &= STCStorageClass; 335 if (stc) 336 { 337 propertyStart(name); 338 arrayStart(); 339 while (stc) 340 { 341 auto p = stcToString(stc); 342 assert(p.length); 343 item(p); 344 } 345 arrayEnd(); 346 } 347 } 348 349 extern(D) void property(const char[] linename, const char[] charname, const ref Loc loc) 350 { 351 if (loc.isValid()) 352 { 353 if (auto filename = loc.filename.toDString) 354 { 355 if (filename != this.filename) 356 { 357 this.filename = filename; 358 property("file", filename); 359 } 360 } 361 if (loc.linnum) 362 { 363 property(linename, loc.linnum); 364 if (loc.charnum) 365 property(charname, loc.charnum); 366 } 367 } 368 } 369 370 extern(D) void property(const char[] name, Type type) 371 { 372 if (type) 373 { 374 property(name, type.toString()); 375 } 376 } 377 378 extern(D) void property(const char[] name, const char[] deconame, Type type) 379 { 380 if (type) 381 { 382 if (type.deco) 383 property(deconame, type.deco.toDString); 384 else 385 property(name, type.toString()); 386 } 387 } 388 389 extern(D) void property(const char[] name, Parameters* parameters) 390 { 391 if (parameters is null || parameters.dim == 0) 392 return; 393 propertyStart(name); 394 arrayStart(); 395 if (parameters) 396 { 397 for (size_t i = 0; i < parameters.dim; i++) 398 { 399 Parameter p = (*parameters)[i]; 400 objectStart(); 401 if (p.ident) 402 property("name", p.ident.toString()); 403 property("type", "deco", p.type); 404 propertyStorageClass("storageClass", p.storageClass); 405 if (p.defaultArg) 406 property("default", p.defaultArg.toString()); 407 objectEnd(); 408 } 409 } 410 arrayEnd(); 411 } 412 413 /* ========================================================================== */ 414 void jsonProperties(Dsymbol s) 415 { 416 if (s.isModule()) 417 return; 418 if (!s.isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes 419 { 420 property("name", s.toString()); 421 if (s.isStaticCtorDeclaration()) 422 { 423 property("kind", s.isSharedStaticCtorDeclaration() 424 ? "shared static constructor" : "static constructor"); 425 } 426 else if (s.isStaticDtorDeclaration()) 427 { 428 property("kind", s.isSharedStaticDtorDeclaration() 429 ? "shared static destructor" : "static destructor"); 430 } 431 else 432 property("kind", s.kind.toDString); 433 } 434 // TODO: How about package(names)? 435 property("protection", visibilityToString(s.visible().kind)); 436 if (EnumMember em = s.isEnumMember()) 437 { 438 if (em.origValue) 439 property("value", em.origValue.toString()); 440 } 441 property("comment", s.comment.toDString); 442 property("line", "char", s.loc); 443 } 444 445 void jsonProperties(Declaration d) 446 { 447 if (d.storage_class & STC.local) 448 return; 449 jsonProperties(cast(Dsymbol)d); 450 propertyStorageClass("storageClass", d.storage_class); 451 property("linkage", d.linkage); 452 property("type", "deco", d.type); 453 // Emit originalType if it differs from type 454 if (d.type != d.originalType && d.originalType) 455 { 456 auto ostr = d.originalType.toString(); 457 if (d.type) 458 { 459 auto tstr = d.type.toString(); 460 if (ostr != tstr) 461 { 462 //printf("tstr = %s, ostr = %s\n", tstr, ostr); 463 property("originalType", ostr); 464 } 465 } 466 else 467 property("originalType", ostr); 468 } 469 } 470 471 void jsonProperties(TemplateDeclaration td) 472 { 473 jsonProperties(cast(Dsymbol)td); 474 if (td.onemember && td.onemember.isCtorDeclaration()) 475 property("name", "this"); // __ctor -> this 476 else 477 property("name", td.ident.toString()); // Foo(T) -> Foo 478 } 479 480 /* ========================================================================== */ 481 override void visit(Dsymbol s) 482 { 483 } 484 485 override void visit(Module s) 486 { 487 objectStart(); 488 if (s.md) 489 property("name", s.md.toString()); 490 property("kind", s.kind.toDString); 491 filename = s.srcfile.toString(); 492 property("file", filename); 493 property("comment", s.comment.toDString); 494 propertyStart("members"); 495 arrayStart(); 496 for (size_t i = 0; i < s.members.dim; i++) 497 { 498 (*s.members)[i].accept(this); 499 } 500 arrayEnd(); 501 objectEnd(); 502 } 503 504 override void visit(Import s) 505 { 506 if (s.id == Id.object) 507 return; 508 objectStart(); 509 propertyStart("name"); 510 stringStart(); 511 foreach (const pid; s.packages){ 512 stringPart(pid.toString()); 513 buf.writeByte('.'); 514 } 515 stringPart(s.id.toString()); 516 stringEnd(); 517 comma(); 518 property("kind", s.kind.toDString); 519 property("comment", s.comment.toDString); 520 property("line", "char", s.loc); 521 if (s.visible().kind != Visibility.Kind.public_) 522 property("protection", visibilityToString(s.visible().kind)); 523 if (s.aliasId) 524 property("alias", s.aliasId.toString()); 525 bool hasRenamed = false; 526 bool hasSelective = false; 527 for (size_t i = 0; i < s.aliases.dim; i++) 528 { 529 // avoid empty "renamed" and "selective" sections 530 if (hasRenamed && hasSelective) 531 break; 532 else if (s.aliases[i]) 533 hasRenamed = true; 534 else 535 hasSelective = true; 536 } 537 if (hasRenamed) 538 { 539 // import foo : alias1 = target1; 540 propertyStart("renamed"); 541 objectStart(); 542 for (size_t i = 0; i < s.aliases.dim; i++) 543 { 544 const name = s.names[i]; 545 const _alias = s.aliases[i]; 546 if (_alias) 547 property(_alias.toString(), name.toString()); 548 } 549 objectEnd(); 550 } 551 if (hasSelective) 552 { 553 // import foo : target1; 554 propertyStart("selective"); 555 arrayStart(); 556 foreach (i, name; s.names) 557 { 558 if (!s.aliases[i]) 559 item(name.toString()); 560 } 561 arrayEnd(); 562 } 563 objectEnd(); 564 } 565 566 override void visit(AttribDeclaration d) 567 { 568 Dsymbols* ds = d.include(null); 569 if (ds) 570 { 571 for (size_t i = 0; i < ds.dim; i++) 572 { 573 Dsymbol s = (*ds)[i]; 574 s.accept(this); 575 } 576 } 577 } 578 579 override void visit(ConditionalDeclaration d) 580 { 581 if (d.condition.inc != Include.notComputed) 582 { 583 visit(cast(AttribDeclaration)d); 584 return; // Don't visit the if/else bodies again below 585 } 586 Dsymbols* ds = d.decl ? d.decl : d.elsedecl; 587 for (size_t i = 0; i < ds.dim; i++) 588 { 589 Dsymbol s = (*ds)[i]; 590 s.accept(this); 591 } 592 } 593 594 override void visit(TypeInfoDeclaration d) 595 { 596 } 597 598 override void visit(PostBlitDeclaration d) 599 { 600 } 601 602 override void visit(Declaration d) 603 { 604 objectStart(); 605 //property("unknown", "declaration"); 606 jsonProperties(d); 607 objectEnd(); 608 } 609 610 override void visit(AggregateDeclaration d) 611 { 612 objectStart(); 613 jsonProperties(d); 614 ClassDeclaration cd = d.isClassDeclaration(); 615 if (cd) 616 { 617 if (cd.baseClass && cd.baseClass.ident != Id.Object) 618 { 619 property("base", cd.baseClass.toPrettyChars(true).toDString); 620 } 621 if (cd.interfaces.length) 622 { 623 propertyStart("interfaces"); 624 arrayStart(); 625 foreach (b; cd.interfaces) 626 { 627 item(b.sym.toPrettyChars(true).toDString); 628 } 629 arrayEnd(); 630 } 631 } 632 if (d.members) 633 { 634 propertyStart("members"); 635 arrayStart(); 636 for (size_t i = 0; i < d.members.dim; i++) 637 { 638 Dsymbol s = (*d.members)[i]; 639 s.accept(this); 640 } 641 arrayEnd(); 642 } 643 objectEnd(); 644 } 645 646 override void visit(FuncDeclaration d) 647 { 648 objectStart(); 649 jsonProperties(d); 650 TypeFunction tf = cast(TypeFunction)d.type; 651 if (tf && tf.ty == Tfunction) 652 property("parameters", tf.parameterList.parameters); 653 property("endline", "endchar", d.endloc); 654 if (d.foverrides.dim) 655 { 656 propertyStart("overrides"); 657 arrayStart(); 658 for (size_t i = 0; i < d.foverrides.dim; i++) 659 { 660 FuncDeclaration fd = d.foverrides[i]; 661 item(fd.toPrettyChars().toDString); 662 } 663 arrayEnd(); 664 } 665 if (d.fdrequire) 666 { 667 propertyStart("in"); 668 d.fdrequire.accept(this); 669 } 670 if (d.fdensure) 671 { 672 propertyStart("out"); 673 d.fdensure.accept(this); 674 } 675 objectEnd(); 676 } 677 678 override void visit(TemplateDeclaration d) 679 { 680 objectStart(); 681 // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one 682 property("kind", "template"); 683 jsonProperties(d); 684 propertyStart("parameters"); 685 arrayStart(); 686 for (size_t i = 0; i < d.parameters.dim; i++) 687 { 688 TemplateParameter s = (*d.parameters)[i]; 689 objectStart(); 690 property("name", s.ident.toString()); 691 692 if (auto type = s.isTemplateTypeParameter()) 693 { 694 if (s.isTemplateThisParameter()) 695 property("kind", "this"); 696 else 697 property("kind", "type"); 698 property("type", "deco", type.specType); 699 property("default", "defaultDeco", type.defaultType); 700 } 701 702 if (auto value = s.isTemplateValueParameter()) 703 { 704 property("kind", "value"); 705 property("type", "deco", value.valType); 706 if (value.specValue) 707 property("specValue", value.specValue.toString()); 708 if (value.defaultValue) 709 property("defaultValue", value.defaultValue.toString()); 710 } 711 712 if (auto _alias = s.isTemplateAliasParameter()) 713 { 714 property("kind", "alias"); 715 property("type", "deco", _alias.specType); 716 if (_alias.specAlias) 717 property("specAlias", _alias.specAlias.toString()); 718 if (_alias.defaultAlias) 719 property("defaultAlias", _alias.defaultAlias.toString()); 720 } 721 722 if (auto tuple = s.isTemplateTupleParameter()) 723 { 724 property("kind", "tuple"); 725 } 726 727 objectEnd(); 728 } 729 arrayEnd(); 730 Expression expression = d.constraint; 731 if (expression) 732 { 733 property("constraint", expression.toString()); 734 } 735 propertyStart("members"); 736 arrayStart(); 737 for (size_t i = 0; i < d.members.dim; i++) 738 { 739 Dsymbol s = (*d.members)[i]; 740 s.accept(this); 741 } 742 arrayEnd(); 743 objectEnd(); 744 } 745 746 override void visit(EnumDeclaration d) 747 { 748 if (d.isAnonymous()) 749 { 750 if (d.members) 751 { 752 for (size_t i = 0; i < d.members.dim; i++) 753 { 754 Dsymbol s = (*d.members)[i]; 755 s.accept(this); 756 } 757 } 758 return; 759 } 760 objectStart(); 761 jsonProperties(d); 762 property("base", "baseDeco", d.memtype); 763 if (d.members) 764 { 765 propertyStart("members"); 766 arrayStart(); 767 for (size_t i = 0; i < d.members.dim; i++) 768 { 769 Dsymbol s = (*d.members)[i]; 770 s.accept(this); 771 } 772 arrayEnd(); 773 } 774 objectEnd(); 775 } 776 777 override void visit(EnumMember s) 778 { 779 objectStart(); 780 jsonProperties(cast(Dsymbol)s); 781 property("type", "deco", s.origType); 782 objectEnd(); 783 } 784 785 override void visit(VarDeclaration d) 786 { 787 if (d.storage_class & STC.local) 788 return; 789 objectStart(); 790 jsonProperties(d); 791 if (d._init) 792 property("init", d._init.toString()); 793 if (d.isField()) 794 property("offset", d.offset); 795 if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT) 796 property("align", d.alignment); 797 objectEnd(); 798 } 799 800 override void visit(TemplateMixin d) 801 { 802 objectStart(); 803 jsonProperties(d); 804 objectEnd(); 805 } 806 807 /** 808 Generate an array of module objects that represent the syntax of each 809 "root module". 810 811 Params: 812 modules = array of the "root modules" 813 */ 814 private void generateModules(Modules* modules) 815 { 816 arrayStart(); 817 if (modules) 818 { 819 foreach (m; *modules) 820 { 821 if (global.params.verbose) 822 message("json gen %s", m.toChars()); 823 m.accept(this); 824 } 825 } 826 arrayEnd(); 827 } 828 829 /** 830 Generate the "compilerInfo" object which contains information about the compiler 831 such as the filename, version, supported features, etc. 832 */ 833 private void generateCompilerInfo() 834 { 835 import dmd.target : target; 836 objectStart(); 837 requiredProperty("vendor", global.vendor); 838 requiredProperty("version", global.versionString()); 839 property("__VERSION__", global.versionNumber()); 840 requiredProperty("interface", determineCompilerInterface()); 841 property("size_t", size_t.sizeof); 842 propertyStart("platforms"); 843 arrayStart(); 844 if (global.params.targetOS == TargetOS.Windows) 845 { 846 item("windows"); 847 } 848 else 849 { 850 item("posix"); 851 if (global.params.targetOS == TargetOS.linux) 852 item("linux"); 853 else if (global.params.targetOS == TargetOS.OSX) 854 item("osx"); 855 else if (global.params.targetOS == TargetOS.FreeBSD) 856 { 857 item("freebsd"); 858 item("bsd"); 859 } 860 else if (global.params.targetOS == TargetOS.OpenBSD) 861 { 862 item("openbsd"); 863 item("bsd"); 864 } 865 else if (global.params.targetOS == TargetOS.Solaris) 866 { 867 item("solaris"); 868 item("bsd"); 869 } 870 } 871 arrayEnd(); 872 873 propertyStart("architectures"); 874 arrayStart(); 875 item(target.architectureName); 876 arrayEnd(); 877 878 propertyStart("predefinedVersions"); 879 arrayStart(); 880 if (global.versionids) 881 { 882 foreach (const versionid; *global.versionids) 883 { 884 item(versionid.toString()); 885 } 886 } 887 arrayEnd(); 888 889 propertyStart("supportedFeatures"); 890 { 891 objectStart(); 892 scope(exit) objectEnd(); 893 propertyBool("includeImports", true); 894 } 895 objectEnd(); 896 } 897 898 /** 899 Generate the "buildInfo" object which contains information specific to the 900 current build such as CWD, importPaths, configFile, etc. 901 */ 902 private void generateBuildInfo() 903 { 904 objectStart(); 905 requiredProperty("cwd", getcwd(null, 0).toDString); 906 requiredProperty("argv0", global.params.argv0); 907 requiredProperty("config", global.inifilename); 908 requiredProperty("libName", global.params.libname); 909 910 propertyStart("importPaths"); 911 arrayStart(); 912 if (global.params.imppath) 913 { 914 foreach (importPath; *global.params.imppath) 915 { 916 item(importPath.toDString); 917 } 918 } 919 arrayEnd(); 920 921 propertyStart("objectFiles"); 922 arrayStart(); 923 foreach (objfile; global.params.objfiles) 924 { 925 item(objfile.toDString); 926 } 927 arrayEnd(); 928 929 propertyStart("libraryFiles"); 930 arrayStart(); 931 foreach (lib; global.params.libfiles) 932 { 933 item(lib.toDString); 934 } 935 arrayEnd(); 936 937 propertyStart("ddocFiles"); 938 arrayStart(); 939 foreach (ddocFile; global.params.ddocfiles) 940 { 941 item(ddocFile.toDString); 942 } 943 arrayEnd(); 944 945 requiredProperty("mapFile", global.params.mapfile); 946 requiredProperty("resourceFile", global.params.resfile); 947 requiredProperty("defFile", global.params.deffile); 948 949 objectEnd(); 950 } 951 952 /** 953 Generate the "semantics" object which contains a 'modules' field representing 954 semantic information about all the modules used in the compilation such as 955 module name, isRoot, contentImportedFiles, etc. 956 */ 957 private void generateSemantics() 958 { 959 objectStart(); 960 propertyStart("modules"); 961 arrayStart(); 962 foreach (m; Module.amodules) 963 { 964 objectStart(); 965 requiredProperty("name", m.md ? m.md.toString() : null); 966 requiredProperty("file", m.srcfile.toString()); 967 propertyBool("isRoot", m.isRoot()); 968 if(m.contentImportedFiles.dim > 0) 969 { 970 propertyStart("contentImports"); 971 arrayStart(); 972 foreach (file; m.contentImportedFiles) 973 { 974 item(file.toDString); 975 } 976 arrayEnd(); 977 } 978 objectEnd(); 979 } 980 arrayEnd(); 981 objectEnd(); 982 } 983 } 984 985 extern (C++) void json_generate(OutBuffer* buf, Modules* modules) 986 { 987 scope ToJsonVisitor json = new ToJsonVisitor(buf); 988 // write trailing newline 989 scope(exit) buf.writeByte('\n'); 990 991 if (global.params.jsonFieldFlags == 0) 992 { 993 // Generate the original format, which is just an array 994 // of modules representing their syntax. 995 json.generateModules(modules); 996 json.removeComma(); 997 } 998 else 999 { 1000 // Generate the new format which is an object where each 1001 // output option is its own field. 1002 1003 json.objectStart(); 1004 if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo) 1005 { 1006 json.propertyStart("compilerInfo"); 1007 json.generateCompilerInfo(); 1008 } 1009 if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo) 1010 { 1011 json.propertyStart("buildInfo"); 1012 json.generateBuildInfo(); 1013 } 1014 if (global.params.jsonFieldFlags & JsonFieldFlags.modules) 1015 { 1016 json.propertyStart("modules"); 1017 json.generateModules(modules); 1018 } 1019 if (global.params.jsonFieldFlags & JsonFieldFlags.semantics) 1020 { 1021 json.propertyStart("semantics"); 1022 json.generateSemantics(); 1023 } 1024 json.objectEnd(); 1025 } 1026 } 1027 1028 /** 1029 A string listing the name of each JSON field. Useful for errors messages. 1030 */ 1031 enum jsonFieldNames = () { 1032 string s; 1033 string prefix = ""; 1034 foreach (idx, enumName; __traits(allMembers, JsonFieldFlags)) 1035 { 1036 static if (idx > 0) 1037 { 1038 s ~= prefix ~ "`" ~ enumName ~ "`"; 1039 prefix = ", "; 1040 } 1041 } 1042 return s; 1043 }(); 1044 1045 /** 1046 Parse the given `fieldName` and return its corresponding JsonFieldFlags value. 1047 1048 Params: 1049 fieldName = the field name to parse 1050 1051 Returns: JsonFieldFlags.none on error, otherwise the JsonFieldFlags value 1052 corresponding to the given fieldName. 1053 */ 1054 extern (C++) JsonFieldFlags tryParseJsonField(const(char)* fieldName) 1055 { 1056 auto fieldNameString = fieldName.toDString(); 1057 foreach (idx, enumName; __traits(allMembers, JsonFieldFlags)) 1058 { 1059 static if (idx > 0) 1060 { 1061 if (fieldNameString == enumName) 1062 return __traits(getMember, JsonFieldFlags, enumName); 1063 } 1064 } 1065 return JsonFieldFlags.none; 1066 } 1067 1068 /** 1069 Determines and returns the compiler interface which is one of `dmd`, `ldc`, 1070 `gdc` or `sdc`. Returns `null` if no interface can be determined. 1071 */ 1072 private extern(D) string determineCompilerInterface() 1073 { 1074 if (global.vendor == "Digital Mars D") 1075 return "dmd"; 1076 if (global.vendor == "LDC") 1077 return "ldc"; 1078 if (global.vendor == "GNU D") 1079 return "gdc"; 1080 if (global.vendor == "SDC") 1081 return "sdc"; 1082 return null; 1083 }