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