1 2 /* Compiler implementation of the D programming language 3 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved 4 * written by Walter Bright 5 * http://www.digitalmars.com 6 * Distributed under the Boost Software License, Version 1.0. 7 * http://www.boost.org/LICENSE_1_0.txt 8 * https://github.com/D-Programming-Language/dmd/blob/master/src/json.c 9 */ 10 11 // This implements the JSON capability. 12 13 #include "root/dsystem.h" 14 #include "root/rmem.h" 15 16 #include "mars.h" 17 #include "dsymbol.h" 18 #include "template.h" 19 #include "aggregate.h" 20 #include "declaration.h" 21 #include "enum.h" 22 #include "module.h" 23 #include "json.h" 24 #include "mtype.h" 25 #include "attrib.h" 26 #include "cond.h" 27 #include "init.h" 28 #include "import.h" 29 #include "id.h" 30 #include "hdrgen.h" 31 32 class ToJsonVisitor : public Visitor 33 { 34 public: 35 OutBuffer *buf; 36 int indentLevel; 37 const char *filename; 38 39 ToJsonVisitor(OutBuffer *buf) 40 : buf(buf), indentLevel(0), filename(NULL) 41 { 42 } 43 44 void indent() 45 { 46 if (buf->offset >= 1 && 47 buf->data[buf->offset - 1] == '\n') 48 for (int i = 0; i < indentLevel; i++) 49 buf->writeByte(' '); 50 } 51 52 void removeComma() 53 { 54 if (buf->offset >= 2 && 55 buf->data[buf->offset - 2] == ',' && 56 (buf->data[buf->offset - 1] == '\n' || buf->data[buf->offset - 1] == ' ')) 57 buf->offset -= 2; 58 } 59 60 void comma() 61 { 62 if (indentLevel > 0) 63 buf->writestring(",\n"); 64 } 65 66 void stringStart() 67 { 68 buf->writeByte('\"'); 69 } 70 71 void stringEnd() 72 { 73 buf->writeByte('\"'); 74 } 75 76 void stringPart(const char *s) 77 { 78 for (; *s; s++) 79 { 80 utf8_t c = (utf8_t) *s; 81 switch (c) 82 { 83 case '\n': 84 buf->writestring("\\n"); 85 break; 86 87 case '\r': 88 buf->writestring("\\r"); 89 break; 90 91 case '\t': 92 buf->writestring("\\t"); 93 break; 94 95 case '\"': 96 buf->writestring("\\\""); 97 break; 98 99 case '\\': 100 buf->writestring("\\\\"); 101 break; 102 103 case '\b': 104 buf->writestring("\\b"); 105 break; 106 107 case '\f': 108 buf->writestring("\\f"); 109 break; 110 111 default: 112 if (c < 0x20) 113 buf->printf("\\u%04x", c); 114 else 115 { 116 // Note that UTF-8 chars pass through here just fine 117 buf->writeByte(c); 118 } 119 break; 120 } 121 } 122 } 123 124 // Json value functions 125 126 /********************************* 127 * Encode string into buf, and wrap it in double quotes. 128 */ 129 void value(const char *s) 130 { 131 stringStart(); 132 stringPart(s); 133 stringEnd(); 134 } 135 136 void value(int value) 137 { 138 buf->printf("%d", value); 139 } 140 141 void valueBool(bool value) 142 { 143 buf->writestring(value ? "true" : "false"); 144 } 145 146 /********************************* 147 * Item is an intented value and a comma, for use in arrays 148 */ 149 void item(const char *s) 150 { 151 indent(); 152 value(s); 153 comma(); 154 } 155 156 void item(int i) 157 { 158 indent(); 159 value(i); 160 comma(); 161 } 162 163 void itemBool(bool b) 164 { 165 indent(); 166 valueBool(b); 167 comma(); 168 } 169 170 171 // Json array functions 172 173 void arrayStart() 174 { 175 indent(); 176 buf->writestring("[\n"); 177 indentLevel++; 178 } 179 180 void arrayEnd() 181 { 182 indentLevel--; 183 removeComma(); 184 if (buf->offset >= 2 && 185 buf->data[buf->offset - 2] == '[' && 186 buf->data[buf->offset - 1] == '\n') 187 buf->offset -= 1; 188 else if (!(buf->offset >= 1 && 189 buf->data[buf->offset - 1] == '[')) 190 { 191 buf->writestring("\n"); 192 indent(); 193 } 194 buf->writestring("]"); 195 comma(); 196 } 197 198 199 // Json object functions 200 201 void objectStart() 202 { 203 indent(); 204 buf->writestring("{\n"); 205 indentLevel++; 206 } 207 208 void objectEnd() 209 { 210 indentLevel--; 211 removeComma(); 212 if (buf->offset >= 2 && 213 buf->data[buf->offset - 2] == '{' && 214 buf->data[buf->offset - 1] == '\n') 215 buf->offset -= 1; 216 else 217 { 218 buf->writestring("\n"); 219 indent(); 220 } 221 buf->writestring("}"); 222 comma(); 223 } 224 225 // Json object property functions 226 227 void propertyStart(const char *name) 228 { 229 indent(); 230 value(name); 231 buf->writestring(" : "); 232 } 233 234 void property(const char *name, const char *s) 235 { 236 if (s == NULL) return; 237 238 propertyStart(name); 239 value(s); 240 comma(); 241 } 242 243 void property(const char *name, int i) 244 { 245 propertyStart(name); 246 value(i); 247 comma(); 248 } 249 250 void propertyBool(const char *name, bool b) 251 { 252 propertyStart(name); 253 valueBool(b); 254 comma(); 255 } 256 257 258 void property(const char *name, TRUST trust) 259 { 260 switch (trust) 261 { 262 case TRUSTdefault: 263 // Should not be printed 264 //property(name, "default"); 265 break; 266 case TRUSTsystem: 267 property(name, "system"); 268 break; 269 case TRUSTtrusted: 270 property(name, "trusted"); 271 break; 272 case TRUSTsafe: 273 property(name, "safe"); 274 break; 275 default: 276 assert(false); 277 } 278 } 279 280 void property(const char *name, PURE purity) 281 { 282 switch (purity) 283 { 284 case PUREimpure: 285 // Should not be printed 286 //property(name, "impure"); 287 break; 288 case PUREweak: 289 property(name, "weak"); 290 break; 291 case PUREconst: 292 property(name, "const"); 293 break; 294 case PUREstrong: 295 property(name, "strong"); 296 break; 297 case PUREfwdref: 298 property(name, "fwdref"); 299 break; 300 default: 301 assert(false); 302 } 303 } 304 305 void property(const char *name, LINK linkage) 306 { 307 switch (linkage) 308 { 309 case LINKdefault: 310 // Should not be printed 311 //property(name, "default"); 312 break; 313 case LINKd: 314 // Should not be printed 315 //property(name, "d"); 316 break; 317 case LINKc: 318 property(name, "c"); 319 break; 320 case LINKcpp: 321 property(name, "cpp"); 322 break; 323 case LINKwindows: 324 property(name, "windows"); 325 break; 326 case LINKpascal: 327 property(name, "pascal"); 328 break; 329 default: 330 assert(false); 331 } 332 } 333 334 void propertyStorageClass(const char *name, StorageClass stc) 335 { 336 stc &= STCStorageClass; 337 if (stc) 338 { 339 propertyStart(name); 340 arrayStart(); 341 342 while (stc) 343 { 344 const char *p = stcToChars(stc); 345 assert(p); 346 item(p); 347 } 348 349 arrayEnd(); 350 } 351 } 352 353 void property(const char *linename, const char *charname, Loc *loc) 354 { 355 if (loc) 356 { 357 const char *filename = loc->filename; 358 if (filename) 359 { 360 if (!this->filename || strcmp(filename, this->filename)) 361 { 362 this->filename = filename; 363 property("file", filename); 364 } 365 } 366 367 if (loc->linnum) 368 { 369 property(linename, loc->linnum); 370 if (loc->charnum) 371 property(charname, loc->charnum); 372 } 373 } 374 } 375 376 void property(const char *name, Type *type) 377 { 378 if (type) 379 { 380 property(name, type->toChars()); 381 } 382 } 383 384 void property(const char *name, const char *deconame, Type *type) 385 { 386 if (type) 387 { 388 if (type->deco) 389 property(deconame, type->deco); 390 else 391 property(name, type->toChars()); 392 } 393 } 394 395 void property(const char *name, Parameters *parameters) 396 { 397 if (parameters == NULL || parameters->dim == 0) 398 return; 399 400 propertyStart(name); 401 arrayStart(); 402 403 if (parameters) 404 { 405 for (size_t i = 0; i < parameters->dim; i++) 406 { 407 Parameter *p = (*parameters)[i]; 408 objectStart(); 409 410 if (p->ident) 411 property("name", p->ident->toChars()); 412 413 property("type", "deco", p->type); 414 415 propertyStorageClass("storageClass", p->storageClass); 416 417 if (p->defaultArg) 418 property("default", p->defaultArg->toChars()); 419 420 421 objectEnd(); 422 } 423 } 424 425 arrayEnd(); 426 } 427 428 /* ========================================================================== */ 429 430 void jsonProperties(Dsymbol *s) 431 { 432 if (s->isModule()) 433 return; 434 435 if (!s->isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes 436 { 437 property("name", s->toChars()); 438 property("kind", s->kind()); 439 } 440 441 if (s->prot().kind != PROTpublic) // TODO: How about package(names)? 442 property("protection", protectionToChars(s->prot().kind)); 443 444 if (EnumMember *em = s->isEnumMember()) 445 { 446 if (em->origValue) 447 property("value", em->origValue->toChars()); 448 } 449 450 property("comment", (const char *)s->comment); 451 452 property("line", "char", &s->loc); 453 } 454 455 void jsonProperties(Declaration *d) 456 { 457 if (d->storage_class & STClocal) 458 return; 459 jsonProperties((Dsymbol *)d); 460 461 propertyStorageClass("storageClass", d->storage_class); 462 463 property("type", "deco", d->type); 464 465 // Emit originalType if it differs from type 466 if (d->type != d->originalType && d->originalType) 467 { 468 const char *ostr = d->originalType->toChars(); 469 if (d->type) 470 { 471 const char *tstr = d->type->toChars(); 472 if (strcmp(tstr, ostr)) 473 { 474 //printf("tstr = %s, ostr = %s\n", tstr, ostr); 475 property("originalType", ostr); 476 } 477 } 478 else 479 property("originalType", ostr); 480 } 481 } 482 483 void jsonProperties(TemplateDeclaration *td) 484 { 485 jsonProperties((Dsymbol *)td); 486 487 if (td->onemember && td->onemember->isCtorDeclaration()) 488 property("name", "this"); // __ctor -> this 489 else 490 property("name", td->ident->toChars()); // Foo(T) -> Foo 491 } 492 493 /* ========================================================================== */ 494 495 void visit(Dsymbol *) 496 { 497 } 498 499 void visit(Module *s) 500 { 501 objectStart(); 502 503 if (s->md) 504 property("name", s->md->toChars()); 505 506 property("kind", s->kind()); 507 508 filename = s->srcfile->toChars(); 509 property("file", filename); 510 511 property("comment", (const char *)s->comment); 512 513 propertyStart("members"); 514 arrayStart(); 515 for (size_t i = 0; i < s->members->dim; i++) 516 { 517 (*s->members)[i]->accept(this); 518 } 519 arrayEnd(); 520 521 objectEnd(); 522 } 523 524 void visit(Import *s) 525 { 526 if (s->id == Id::object) 527 return; 528 529 objectStart(); 530 531 propertyStart("name"); 532 stringStart(); 533 if (s->packages && s->packages->dim) 534 { 535 for (size_t i = 0; i < s->packages->dim; i++) 536 { 537 Identifier *pid = (*s->packages)[i]; 538 stringPart(pid->toChars()); 539 buf->writeByte('.'); 540 } 541 } 542 stringPart(s->id->toChars()); 543 stringEnd(); 544 comma(); 545 546 property("kind", s->kind()); 547 property("comment", (const char *)s->comment); 548 property("line", "char", &s->loc); 549 if (s->prot().kind != PROTpublic) 550 property("protection", protectionToChars(s->prot().kind)); 551 if (s->aliasId) 552 property("alias", s->aliasId->toChars()); 553 554 bool hasRenamed = false; 555 bool hasSelective = false; 556 for (size_t i = 0; i < s->aliases.dim; i++) 557 { 558 // avoid empty "renamed" and "selective" sections 559 if (hasRenamed && hasSelective) 560 break; 561 else if (s->aliases[i]) 562 hasRenamed = true; 563 else 564 hasSelective = true; 565 } 566 567 if (hasRenamed) 568 { 569 // import foo : alias1 = target1; 570 propertyStart("renamed"); 571 objectStart(); 572 for (size_t i = 0; i < s->aliases.dim; i++) 573 { 574 Identifier *name = s->names[i]; 575 Identifier *alias = s->aliases[i]; 576 if (alias) property(alias->toChars(), name->toChars()); 577 } 578 objectEnd(); 579 } 580 581 if (hasSelective) 582 { 583 // import foo : target1; 584 propertyStart("selective"); 585 arrayStart(); 586 for (size_t i = 0; i < s->names.dim; i++) 587 { 588 Identifier *name = s->names[i]; 589 if (!s->aliases[i]) item(name->toChars()); 590 } 591 arrayEnd(); 592 } 593 594 objectEnd(); 595 } 596 597 void visit(AttribDeclaration *d) 598 { 599 Dsymbols *ds = d->include(NULL, NULL); 600 601 if (ds) 602 { 603 for (size_t i = 0; i < ds->dim; i++) 604 { 605 Dsymbol *s = (*ds)[i]; 606 s->accept(this); 607 } 608 } 609 } 610 611 void visit(ConditionalDeclaration *d) 612 { 613 if (d->condition->inc) 614 { 615 visit((AttribDeclaration *)d); 616 } 617 } 618 619 void visit(TypeInfoDeclaration *) {} 620 void visit(PostBlitDeclaration *) {} 621 622 void visit(Declaration *d) 623 { 624 objectStart(); 625 626 //property("unknown", "declaration"); 627 628 jsonProperties(d); 629 630 objectEnd(); 631 } 632 633 void visit(AggregateDeclaration *d) 634 { 635 objectStart(); 636 637 jsonProperties(d); 638 639 ClassDeclaration *cd = d->isClassDeclaration(); 640 if (cd) 641 { 642 if (cd->baseClass && cd->baseClass->ident != Id::Object) 643 { 644 property("base", cd->baseClass->toPrettyChars(true)); 645 } 646 if (cd->interfaces.length) 647 { 648 propertyStart("interfaces"); 649 arrayStart(); 650 for (size_t i = 0; i < cd->interfaces.length; i++) 651 { 652 BaseClass *b = cd->interfaces.ptr[i]; 653 item(b->sym->toPrettyChars(true)); 654 } 655 arrayEnd(); 656 } 657 } 658 659 if (d->members) 660 { 661 propertyStart("members"); 662 arrayStart(); 663 for (size_t i = 0; i < d->members->dim; i++) 664 { 665 Dsymbol *s = (*d->members)[i]; 666 s->accept(this); 667 } 668 arrayEnd(); 669 } 670 671 objectEnd(); 672 } 673 674 void visit(FuncDeclaration *d) 675 { 676 objectStart(); 677 678 jsonProperties(d); 679 680 TypeFunction *tf = (TypeFunction *)d->type; 681 if (tf && tf->ty == Tfunction) 682 property("parameters", tf->parameters); 683 684 property("endline", "endchar", &d->endloc); 685 686 if (d->foverrides.dim) 687 { 688 propertyStart("overrides"); 689 arrayStart(); 690 for (size_t i = 0; i < d->foverrides.dim; i++) 691 { 692 FuncDeclaration *fd = d->foverrides[i]; 693 item(fd->toPrettyChars()); 694 } 695 arrayEnd(); 696 } 697 698 if (d->fdrequire) 699 { 700 propertyStart("in"); 701 d->fdrequire->accept(this); 702 } 703 704 if (d->fdensure) 705 { 706 propertyStart("out"); 707 d->fdensure->accept(this); 708 } 709 710 objectEnd(); 711 } 712 713 void visit(TemplateDeclaration *d) 714 { 715 objectStart(); 716 717 // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one 718 property("kind", "template"); 719 720 jsonProperties(d); 721 722 propertyStart("parameters"); 723 arrayStart(); 724 for (size_t i = 0; i < d->parameters->dim; i++) 725 { 726 TemplateParameter *s = (*d->parameters)[i]; 727 objectStart(); 728 729 property("name", s->ident->toChars()); 730 731 TemplateTypeParameter *type = s->isTemplateTypeParameter(); 732 if (type) 733 { 734 if (s->isTemplateThisParameter()) 735 property("kind", "this"); 736 else 737 property("kind", "type"); 738 property("type", "deco", type->specType); 739 740 property("default", "defaultDeco", type->defaultType); 741 } 742 743 TemplateValueParameter *value = s->isTemplateValueParameter(); 744 if (value) 745 { 746 property("kind", "value"); 747 748 property("type", "deco", value->valType); 749 750 if (value->specValue) 751 property("specValue", value->specValue->toChars()); 752 753 if (value->defaultValue) 754 property("defaultValue", value->defaultValue->toChars()); 755 } 756 757 TemplateAliasParameter *alias = s->isTemplateAliasParameter(); 758 if (alias) 759 { 760 property("kind", "alias"); 761 762 property("type", "deco", alias->specType); 763 764 if (alias->specAlias) 765 property("specAlias", alias->specAlias->toChars()); 766 767 if (alias->defaultAlias) 768 property("defaultAlias", alias->defaultAlias->toChars()); 769 } 770 771 TemplateTupleParameter *tuple = s->isTemplateTupleParameter(); 772 if (tuple) 773 { 774 property("kind", "tuple"); 775 } 776 777 objectEnd(); 778 } 779 arrayEnd(); 780 781 Expression *expression = d->constraint; 782 if (expression) 783 { 784 property("constraint", expression->toChars()); 785 } 786 787 propertyStart("members"); 788 arrayStart(); 789 for (size_t i = 0; i < d->members->dim; i++) 790 { 791 Dsymbol *s = (*d->members)[i]; 792 s->accept(this); 793 } 794 arrayEnd(); 795 796 objectEnd(); 797 } 798 799 void visit(EnumDeclaration *d) 800 { 801 if (d->isAnonymous()) 802 { 803 if (d->members) 804 { 805 for (size_t i = 0; i < d->members->dim; i++) 806 { 807 Dsymbol *s = (*d->members)[i]; 808 s->accept(this); 809 } 810 } 811 return; 812 } 813 814 objectStart(); 815 816 jsonProperties(d); 817 818 property("base", "baseDeco", d->memtype); 819 820 if (d->members) 821 { 822 propertyStart("members"); 823 arrayStart(); 824 for (size_t i = 0; i < d->members->dim; i++) 825 { 826 Dsymbol *s = (*d->members)[i]; 827 s->accept(this); 828 } 829 arrayEnd(); 830 } 831 832 objectEnd(); 833 } 834 835 void visit(EnumMember *s) 836 { 837 objectStart(); 838 839 jsonProperties((Dsymbol*)s); 840 841 property("type", "deco", s->origType); 842 843 objectEnd(); 844 } 845 846 void visit(VarDeclaration *d) 847 { 848 if (d->storage_class & STClocal) 849 return; 850 objectStart(); 851 852 jsonProperties(d); 853 854 if (d->_init) 855 property("init", d->_init->toChars()); 856 857 if (d->isField()) 858 property("offset", d->offset); 859 860 if (d->alignment && d->alignment != STRUCTALIGN_DEFAULT) 861 property("align", d->alignment); 862 863 objectEnd(); 864 } 865 866 void visit(TemplateMixin *d) 867 { 868 objectStart(); 869 870 jsonProperties(d); 871 872 objectEnd(); 873 } 874 }; 875 876 877 void json_generate(OutBuffer *buf, Modules *modules) 878 { 879 ToJsonVisitor json(buf); 880 881 json.arrayStart(); 882 for (size_t i = 0; i < modules->dim; i++) 883 { 884 Module *m = (*modules)[i]; 885 if (global.params.verbose) 886 message("json gen %s", m->toChars()); 887 m->accept(&json); 888 } 889 json.arrayEnd(); 890 json.removeComma(); 891 } 892