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 jsonProperties((Dsymbol *)d); 458 459 propertyStorageClass("storageClass", d->storage_class); 460 461 property("type", "deco", d->type); 462 463 // Emit originalType if it differs from type 464 if (d->type != d->originalType && d->originalType) 465 { 466 const char *ostr = d->originalType->toChars(); 467 if (d->type) 468 { 469 const char *tstr = d->type->toChars(); 470 if (strcmp(tstr, ostr)) 471 { 472 //printf("tstr = %s, ostr = %s\n", tstr, ostr); 473 property("originalType", ostr); 474 } 475 } 476 else 477 property("originalType", ostr); 478 } 479 } 480 481 void jsonProperties(TemplateDeclaration *td) 482 { 483 jsonProperties((Dsymbol *)td); 484 485 if (td->onemember && td->onemember->isCtorDeclaration()) 486 property("name", "this"); // __ctor -> this 487 else 488 property("name", td->ident->toChars()); // Foo(T) -> Foo 489 } 490 491 /* ========================================================================== */ 492 493 void visit(Dsymbol *) 494 { 495 } 496 497 void visit(Module *s) 498 { 499 objectStart(); 500 501 if (s->md) 502 property("name", s->md->toChars()); 503 504 property("kind", s->kind()); 505 506 filename = s->srcfile->toChars(); 507 property("file", filename); 508 509 property("comment", (const char *)s->comment); 510 511 propertyStart("members"); 512 arrayStart(); 513 for (size_t i = 0; i < s->members->dim; i++) 514 { 515 (*s->members)[i]->accept(this); 516 } 517 arrayEnd(); 518 519 objectEnd(); 520 } 521 522 void visit(Import *s) 523 { 524 if (s->id == Id::object) 525 return; 526 527 objectStart(); 528 529 propertyStart("name"); 530 stringStart(); 531 if (s->packages && s->packages->dim) 532 { 533 for (size_t i = 0; i < s->packages->dim; i++) 534 { 535 Identifier *pid = (*s->packages)[i]; 536 stringPart(pid->toChars()); 537 buf->writeByte('.'); 538 } 539 } 540 stringPart(s->id->toChars()); 541 stringEnd(); 542 comma(); 543 544 property("kind", s->kind()); 545 property("comment", (const char *)s->comment); 546 property("line", "char", &s->loc); 547 if (s->prot().kind != PROTpublic) 548 property("protection", protectionToChars(s->prot().kind)); 549 if (s->aliasId) 550 property("alias", s->aliasId->toChars()); 551 552 bool hasRenamed = false; 553 bool hasSelective = false; 554 for (size_t i = 0; i < s->aliases.dim; i++) 555 { 556 // avoid empty "renamed" and "selective" sections 557 if (hasRenamed && hasSelective) 558 break; 559 else if (s->aliases[i]) 560 hasRenamed = true; 561 else 562 hasSelective = true; 563 } 564 565 if (hasRenamed) 566 { 567 // import foo : alias1 = target1; 568 propertyStart("renamed"); 569 objectStart(); 570 for (size_t i = 0; i < s->aliases.dim; i++) 571 { 572 Identifier *name = s->names[i]; 573 Identifier *alias = s->aliases[i]; 574 if (alias) property(alias->toChars(), name->toChars()); 575 } 576 objectEnd(); 577 } 578 579 if (hasSelective) 580 { 581 // import foo : target1; 582 propertyStart("selective"); 583 arrayStart(); 584 for (size_t i = 0; i < s->names.dim; i++) 585 { 586 Identifier *name = s->names[i]; 587 if (!s->aliases[i]) item(name->toChars()); 588 } 589 arrayEnd(); 590 } 591 592 objectEnd(); 593 } 594 595 void visit(AttribDeclaration *d) 596 { 597 Dsymbols *ds = d->include(NULL, NULL); 598 599 if (ds) 600 { 601 for (size_t i = 0; i < ds->dim; i++) 602 { 603 Dsymbol *s = (*ds)[i]; 604 s->accept(this); 605 } 606 } 607 } 608 609 void visit(ConditionalDeclaration *d) 610 { 611 if (d->condition->inc) 612 { 613 visit((AttribDeclaration *)d); 614 } 615 } 616 617 void visit(TypeInfoDeclaration *) {} 618 void visit(PostBlitDeclaration *) {} 619 620 void visit(Declaration *d) 621 { 622 objectStart(); 623 624 //property("unknown", "declaration"); 625 626 jsonProperties(d); 627 628 objectEnd(); 629 } 630 631 void visit(AggregateDeclaration *d) 632 { 633 objectStart(); 634 635 jsonProperties(d); 636 637 ClassDeclaration *cd = d->isClassDeclaration(); 638 if (cd) 639 { 640 if (cd->baseClass && cd->baseClass->ident != Id::Object) 641 { 642 property("base", cd->baseClass->toPrettyChars(true)); 643 } 644 if (cd->interfaces.length) 645 { 646 propertyStart("interfaces"); 647 arrayStart(); 648 for (size_t i = 0; i < cd->interfaces.length; i++) 649 { 650 BaseClass *b = cd->interfaces.ptr[i]; 651 item(b->sym->toPrettyChars(true)); 652 } 653 arrayEnd(); 654 } 655 } 656 657 if (d->members) 658 { 659 propertyStart("members"); 660 arrayStart(); 661 for (size_t i = 0; i < d->members->dim; i++) 662 { 663 Dsymbol *s = (*d->members)[i]; 664 s->accept(this); 665 } 666 arrayEnd(); 667 } 668 669 objectEnd(); 670 } 671 672 void visit(FuncDeclaration *d) 673 { 674 objectStart(); 675 676 jsonProperties(d); 677 678 TypeFunction *tf = (TypeFunction *)d->type; 679 if (tf && tf->ty == Tfunction) 680 property("parameters", tf->parameters); 681 682 property("endline", "endchar", &d->endloc); 683 684 if (d->foverrides.dim) 685 { 686 propertyStart("overrides"); 687 arrayStart(); 688 for (size_t i = 0; i < d->foverrides.dim; i++) 689 { 690 FuncDeclaration *fd = d->foverrides[i]; 691 item(fd->toPrettyChars()); 692 } 693 arrayEnd(); 694 } 695 696 if (d->fdrequire) 697 { 698 propertyStart("in"); 699 d->fdrequire->accept(this); 700 } 701 702 if (d->fdensure) 703 { 704 propertyStart("out"); 705 d->fdensure->accept(this); 706 } 707 708 objectEnd(); 709 } 710 711 void visit(TemplateDeclaration *d) 712 { 713 objectStart(); 714 715 // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one 716 property("kind", "template"); 717 718 jsonProperties(d); 719 720 propertyStart("parameters"); 721 arrayStart(); 722 for (size_t i = 0; i < d->parameters->dim; i++) 723 { 724 TemplateParameter *s = (*d->parameters)[i]; 725 objectStart(); 726 727 property("name", s->ident->toChars()); 728 729 TemplateTypeParameter *type = s->isTemplateTypeParameter(); 730 if (type) 731 { 732 if (s->isTemplateThisParameter()) 733 property("kind", "this"); 734 else 735 property("kind", "type"); 736 property("type", "deco", type->specType); 737 738 property("default", "defaultDeco", type->defaultType); 739 } 740 741 TemplateValueParameter *value = s->isTemplateValueParameter(); 742 if (value) 743 { 744 property("kind", "value"); 745 746 property("type", "deco", value->valType); 747 748 if (value->specValue) 749 property("specValue", value->specValue->toChars()); 750 751 if (value->defaultValue) 752 property("defaultValue", value->defaultValue->toChars()); 753 } 754 755 TemplateAliasParameter *alias = s->isTemplateAliasParameter(); 756 if (alias) 757 { 758 property("kind", "alias"); 759 760 property("type", "deco", alias->specType); 761 762 if (alias->specAlias) 763 property("specAlias", alias->specAlias->toChars()); 764 765 if (alias->defaultAlias) 766 property("defaultAlias", alias->defaultAlias->toChars()); 767 } 768 769 TemplateTupleParameter *tuple = s->isTemplateTupleParameter(); 770 if (tuple) 771 { 772 property("kind", "tuple"); 773 } 774 775 objectEnd(); 776 } 777 arrayEnd(); 778 779 Expression *expression = d->constraint; 780 if (expression) 781 { 782 property("constraint", expression->toChars()); 783 } 784 785 propertyStart("members"); 786 arrayStart(); 787 for (size_t i = 0; i < d->members->dim; i++) 788 { 789 Dsymbol *s = (*d->members)[i]; 790 s->accept(this); 791 } 792 arrayEnd(); 793 794 objectEnd(); 795 } 796 797 void visit(EnumDeclaration *d) 798 { 799 if (d->isAnonymous()) 800 { 801 if (d->members) 802 { 803 for (size_t i = 0; i < d->members->dim; i++) 804 { 805 Dsymbol *s = (*d->members)[i]; 806 s->accept(this); 807 } 808 } 809 return; 810 } 811 812 objectStart(); 813 814 jsonProperties(d); 815 816 property("base", "baseDeco", d->memtype); 817 818 if (d->members) 819 { 820 propertyStart("members"); 821 arrayStart(); 822 for (size_t i = 0; i < d->members->dim; i++) 823 { 824 Dsymbol *s = (*d->members)[i]; 825 s->accept(this); 826 } 827 arrayEnd(); 828 } 829 830 objectEnd(); 831 } 832 833 void visit(EnumMember *s) 834 { 835 objectStart(); 836 837 jsonProperties((Dsymbol*)s); 838 839 property("type", "deco", s->origType); 840 841 objectEnd(); 842 } 843 844 void visit(VarDeclaration *d) 845 { 846 objectStart(); 847 848 jsonProperties(d); 849 850 if (d->_init) 851 property("init", d->_init->toChars()); 852 853 if (d->isField()) 854 property("offset", d->offset); 855 856 if (d->alignment && d->alignment != STRUCTALIGN_DEFAULT) 857 property("align", d->alignment); 858 859 objectEnd(); 860 } 861 862 void visit(TemplateMixin *d) 863 { 864 objectStart(); 865 866 jsonProperties(d); 867 868 objectEnd(); 869 } 870 }; 871 872 873 void json_generate(OutBuffer *buf, Modules *modules) 874 { 875 ToJsonVisitor json(buf); 876 877 json.arrayStart(); 878 for (size_t i = 0; i < modules->dim; i++) 879 { 880 Module *m = (*modules)[i]; 881 if (global.params.verbose) 882 message("json gen %s", m->toChars()); 883 m->accept(&json); 884 } 885 json.arrayEnd(); 886 json.removeComma(); 887 } 888