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