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
ToJsonVisitor(OutBuffer * buf)39 ToJsonVisitor(OutBuffer *buf)
40 : buf(buf), indentLevel(0), filename(NULL)
41 {
42 }
43
indent()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
removeComma()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
comma()60 void comma()
61 {
62 if (indentLevel > 0)
63 buf->writestring(",\n");
64 }
65
stringStart()66 void stringStart()
67 {
68 buf->writeByte('\"');
69 }
70
stringEnd()71 void stringEnd()
72 {
73 buf->writeByte('\"');
74 }
75
stringPart(const char * s)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 */
value(const char * s)129 void value(const char *s)
130 {
131 stringStart();
132 stringPart(s);
133 stringEnd();
134 }
135
value(int value)136 void value(int value)
137 {
138 buf->printf("%d", value);
139 }
140
valueBool(bool value)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 */
item(const char * s)149 void item(const char *s)
150 {
151 indent();
152 value(s);
153 comma();
154 }
155
item(int i)156 void item(int i)
157 {
158 indent();
159 value(i);
160 comma();
161 }
162
itemBool(bool b)163 void itemBool(bool b)
164 {
165 indent();
166 valueBool(b);
167 comma();
168 }
169
170
171 // Json array functions
172
arrayStart()173 void arrayStart()
174 {
175 indent();
176 buf->writestring("[\n");
177 indentLevel++;
178 }
179
arrayEnd()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
objectStart()201 void objectStart()
202 {
203 indent();
204 buf->writestring("{\n");
205 indentLevel++;
206 }
207
objectEnd()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
propertyStart(const char * name)227 void propertyStart(const char *name)
228 {
229 indent();
230 value(name);
231 buf->writestring(" : ");
232 }
233
property(const char * name,const char * s)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
property(const char * name,int i)243 void property(const char *name, int i)
244 {
245 propertyStart(name);
246 value(i);
247 comma();
248 }
249
propertyBool(const char * name,bool b)250 void propertyBool(const char *name, bool b)
251 {
252 propertyStart(name);
253 valueBool(b);
254 comma();
255 }
256
257
property(const char * name,TRUST trust)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
property(const char * name,PURE purity)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
property(const char * name,LINK linkage)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
propertyStorageClass(const char * name,StorageClass stc)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
property(const char * linename,const char * charname,Loc * loc)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
property(const char * name,Type * type)376 void property(const char *name, Type *type)
377 {
378 if (type)
379 {
380 property(name, type->toChars());
381 }
382 }
383
property(const char * name,const char * deconame,Type * type)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
property(const char * name,Parameters * parameters)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
jsonProperties(Dsymbol * s)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
jsonProperties(Declaration * d)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
jsonProperties(TemplateDeclaration * td)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
visit(Dsymbol *)495 void visit(Dsymbol *)
496 {
497 }
498
visit(Module * s)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
visit(Import * s)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
visit(AttribDeclaration * d)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
visit(ConditionalDeclaration * d)611 void visit(ConditionalDeclaration *d)
612 {
613 if (d->condition->inc)
614 {
615 visit((AttribDeclaration *)d);
616 }
617 }
618
visit(TypeInfoDeclaration *)619 void visit(TypeInfoDeclaration *) {}
visit(PostBlitDeclaration *)620 void visit(PostBlitDeclaration *) {}
621
visit(Declaration * d)622 void visit(Declaration *d)
623 {
624 objectStart();
625
626 //property("unknown", "declaration");
627
628 jsonProperties(d);
629
630 objectEnd();
631 }
632
visit(AggregateDeclaration * d)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
visit(FuncDeclaration * d)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
visit(TemplateDeclaration * d)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
visit(EnumDeclaration * d)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
visit(EnumMember * s)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
visit(VarDeclaration * d)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
visit(TemplateMixin * d)866 void visit(TemplateMixin *d)
867 {
868 objectStart();
869
870 jsonProperties(d);
871
872 objectEnd();
873 }
874 };
875
876
json_generate(OutBuffer * buf,Modules * modules)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