1
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2021 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->length() >= 1 &&
47 buf->slice().ptr[buf->length() - 1] == '\n')
48 for (int i = 0; i < indentLevel; i++)
49 buf->writeByte(' ');
50 }
51
removeComma()52 void removeComma()
53 {
54 if (buf->length() >= 2 &&
55 buf->slice().ptr[buf->length() - 2] == ',' &&
56 (buf->slice().ptr[buf->length() - 1] == '\n' || buf->slice().ptr[buf->length() - 1] == ' '))
57 buf->setsize(buf->length() - 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->length() >= 2 &&
185 buf->slice().ptr[buf->length() - 2] == '[' &&
186 buf->slice().ptr[buf->length() - 1] == '\n')
187 buf->setsize(buf->length() - 1);
188 else if (!(buf->length() >= 1 &&
189 buf->slice().ptr[buf->length() - 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->length() >= 2 &&
213 buf->slice().ptr[buf->length() - 2] == '{' &&
214 buf->slice().ptr[buf->length() - 1] == '\n')
215 buf->setsize(buf->length() - 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 default:
327 assert(false);
328 }
329 }
330
propertyStorageClass(const char * name,StorageClass stc)331 void propertyStorageClass(const char *name, StorageClass stc)
332 {
333 stc &= STCStorageClass;
334 if (stc)
335 {
336 propertyStart(name);
337 arrayStart();
338
339 while (stc)
340 {
341 const char *p = stcToChars(stc);
342 assert(p);
343 item(p);
344 }
345
346 arrayEnd();
347 }
348 }
349
property(const char * linename,const char * charname,Loc * loc)350 void property(const char *linename, const char *charname, Loc *loc)
351 {
352 if (loc)
353 {
354 const char *filename = loc->filename;
355 if (filename)
356 {
357 if (!this->filename || strcmp(filename, this->filename))
358 {
359 this->filename = filename;
360 property("file", filename);
361 }
362 }
363
364 if (loc->linnum)
365 {
366 property(linename, loc->linnum);
367 if (loc->charnum)
368 property(charname, loc->charnum);
369 }
370 }
371 }
372
property(const char * name,Type * type)373 void property(const char *name, Type *type)
374 {
375 if (type)
376 {
377 property(name, type->toChars());
378 }
379 }
380
property(const char * name,const char * deconame,Type * type)381 void property(const char *name, const char *deconame, Type *type)
382 {
383 if (type)
384 {
385 if (type->deco)
386 property(deconame, type->deco);
387 else
388 property(name, type->toChars());
389 }
390 }
391
property(const char * name,Parameters * parameters)392 void property(const char *name, Parameters *parameters)
393 {
394 if (parameters == NULL || parameters->length == 0)
395 return;
396
397 propertyStart(name);
398 arrayStart();
399
400 if (parameters)
401 {
402 for (size_t i = 0; i < parameters->length; i++)
403 {
404 Parameter *p = (*parameters)[i];
405 objectStart();
406
407 if (p->ident)
408 property("name", p->ident->toChars());
409
410 property("type", "deco", p->type);
411
412 propertyStorageClass("storageClass", p->storageClass);
413
414 if (p->defaultArg)
415 property("default", p->defaultArg->toChars());
416
417
418 objectEnd();
419 }
420 }
421
422 arrayEnd();
423 }
424
425 /* ========================================================================== */
426
jsonProperties(Dsymbol * s)427 void jsonProperties(Dsymbol *s)
428 {
429 if (s->isModule())
430 return;
431
432 if (!s->isTemplateDeclaration()) // TemplateDeclaration::kind() acts weird sometimes
433 {
434 property("name", s->toChars());
435 property("kind", s->kind());
436 }
437
438 if (s->prot().kind != Prot::public_) // TODO: How about package(names)?
439 property("protection", protectionToChars(s->prot().kind));
440
441 if (EnumMember *em = s->isEnumMember())
442 {
443 if (em->origValue)
444 property("value", em->origValue->toChars());
445 }
446
447 property("comment", (const char *)s->comment);
448
449 property("line", "char", &s->loc);
450 }
451
jsonProperties(Declaration * d)452 void jsonProperties(Declaration *d)
453 {
454 if (d->storage_class & STClocal)
455 return;
456 jsonProperties((Dsymbol *)d);
457
458 propertyStorageClass("storageClass", d->storage_class);
459
460 property("type", "deco", d->type);
461
462 // Emit originalType if it differs from type
463 if (d->type != d->originalType && d->originalType)
464 {
465 const char *ostr = d->originalType->toChars();
466 if (d->type)
467 {
468 const char *tstr = d->type->toChars();
469 if (strcmp(tstr, ostr))
470 {
471 //printf("tstr = %s, ostr = %s\n", tstr, ostr);
472 property("originalType", ostr);
473 }
474 }
475 else
476 property("originalType", ostr);
477 }
478 }
479
jsonProperties(TemplateDeclaration * td)480 void jsonProperties(TemplateDeclaration *td)
481 {
482 jsonProperties((Dsymbol *)td);
483
484 if (td->onemember && td->onemember->isCtorDeclaration())
485 property("name", "this"); // __ctor -> this
486 else
487 property("name", td->ident->toChars()); // Foo(T) -> Foo
488 }
489
490 /* ========================================================================== */
491
visit(Dsymbol *)492 void visit(Dsymbol *)
493 {
494 }
495
visit(Module * s)496 void visit(Module *s)
497 {
498 objectStart();
499
500 if (s->md)
501 property("name", s->md->toChars());
502
503 property("kind", s->kind());
504
505 filename = s->srcfile->toChars();
506 property("file", filename);
507
508 property("comment", (const char *)s->comment);
509
510 propertyStart("members");
511 arrayStart();
512 for (size_t i = 0; i < s->members->length; i++)
513 {
514 (*s->members)[i]->accept(this);
515 }
516 arrayEnd();
517
518 objectEnd();
519 }
520
visit(Import * s)521 void visit(Import *s)
522 {
523 if (s->id == Id::object)
524 return;
525
526 objectStart();
527
528 propertyStart("name");
529 stringStart();
530 if (s->packages && s->packages->length)
531 {
532 for (size_t i = 0; i < s->packages->length; i++)
533 {
534 Identifier *pid = (*s->packages)[i];
535 stringPart(pid->toChars());
536 buf->writeByte('.');
537 }
538 }
539 stringPart(s->id->toChars());
540 stringEnd();
541 comma();
542
543 property("kind", s->kind());
544 property("comment", (const char *)s->comment);
545 property("line", "char", &s->loc);
546 if (s->prot().kind != Prot::public_)
547 property("protection", protectionToChars(s->prot().kind));
548 if (s->aliasId)
549 property("alias", s->aliasId->toChars());
550
551 bool hasRenamed = false;
552 bool hasSelective = false;
553 for (size_t i = 0; i < s->aliases.length; i++)
554 {
555 // avoid empty "renamed" and "selective" sections
556 if (hasRenamed && hasSelective)
557 break;
558 else if (s->aliases[i])
559 hasRenamed = true;
560 else
561 hasSelective = true;
562 }
563
564 if (hasRenamed)
565 {
566 // import foo : alias1 = target1;
567 propertyStart("renamed");
568 objectStart();
569 for (size_t i = 0; i < s->aliases.length; i++)
570 {
571 Identifier *name = s->names[i];
572 Identifier *alias = s->aliases[i];
573 if (alias) property(alias->toChars(), name->toChars());
574 }
575 objectEnd();
576 }
577
578 if (hasSelective)
579 {
580 // import foo : target1;
581 propertyStart("selective");
582 arrayStart();
583 for (size_t i = 0; i < s->names.length; i++)
584 {
585 Identifier *name = s->names[i];
586 if (!s->aliases[i]) item(name->toChars());
587 }
588 arrayEnd();
589 }
590
591 objectEnd();
592 }
593
visit(AttribDeclaration * d)594 void visit(AttribDeclaration *d)
595 {
596 Dsymbols *ds = d->include(NULL);
597
598 if (ds)
599 {
600 for (size_t i = 0; i < ds->length; i++)
601 {
602 Dsymbol *s = (*ds)[i];
603 s->accept(this);
604 }
605 }
606 }
607
visit(ConditionalDeclaration * d)608 void visit(ConditionalDeclaration *d)
609 {
610 if (d->condition->inc)
611 {
612 visit((AttribDeclaration *)d);
613 }
614 }
615
visit(TypeInfoDeclaration *)616 void visit(TypeInfoDeclaration *) {}
visit(PostBlitDeclaration *)617 void visit(PostBlitDeclaration *) {}
618
visit(Declaration * d)619 void visit(Declaration *d)
620 {
621 objectStart();
622
623 //property("unknown", "declaration");
624
625 jsonProperties(d);
626
627 objectEnd();
628 }
629
visit(AggregateDeclaration * d)630 void visit(AggregateDeclaration *d)
631 {
632 objectStart();
633
634 jsonProperties(d);
635
636 ClassDeclaration *cd = d->isClassDeclaration();
637 if (cd)
638 {
639 if (cd->baseClass && cd->baseClass->ident != Id::Object)
640 {
641 property("base", cd->baseClass->toPrettyChars(true));
642 }
643 if (cd->interfaces.length)
644 {
645 propertyStart("interfaces");
646 arrayStart();
647 for (size_t i = 0; i < cd->interfaces.length; i++)
648 {
649 BaseClass *b = cd->interfaces.ptr[i];
650 item(b->sym->toPrettyChars(true));
651 }
652 arrayEnd();
653 }
654 }
655
656 if (d->members)
657 {
658 propertyStart("members");
659 arrayStart();
660 for (size_t i = 0; i < d->members->length; i++)
661 {
662 Dsymbol *s = (*d->members)[i];
663 s->accept(this);
664 }
665 arrayEnd();
666 }
667
668 objectEnd();
669 }
670
visit(FuncDeclaration * d)671 void visit(FuncDeclaration *d)
672 {
673 objectStart();
674
675 jsonProperties(d);
676
677 TypeFunction *tf = (TypeFunction *)d->type;
678 if (tf && tf->ty == Tfunction)
679 property("parameters", tf->parameterList.parameters);
680
681 property("endline", "endchar", &d->endloc);
682
683 if (d->foverrides.length)
684 {
685 propertyStart("overrides");
686 arrayStart();
687 for (size_t i = 0; i < d->foverrides.length; i++)
688 {
689 FuncDeclaration *fd = d->foverrides[i];
690 item(fd->toPrettyChars());
691 }
692 arrayEnd();
693 }
694
695 if (d->fdrequire)
696 {
697 propertyStart("in");
698 d->fdrequire->accept(this);
699 }
700
701 if (d->fdensure)
702 {
703 propertyStart("out");
704 d->fdensure->accept(this);
705 }
706
707 objectEnd();
708 }
709
visit(TemplateDeclaration * d)710 void visit(TemplateDeclaration *d)
711 {
712 objectStart();
713
714 // TemplateDeclaration::kind returns the kind of its Aggregate onemember, if it is one
715 property("kind", "template");
716
717 jsonProperties(d);
718
719 propertyStart("parameters");
720 arrayStart();
721 for (size_t i = 0; i < d->parameters->length; i++)
722 {
723 TemplateParameter *s = (*d->parameters)[i];
724 objectStart();
725
726 property("name", s->ident->toChars());
727
728 TemplateTypeParameter *type = s->isTemplateTypeParameter();
729 if (type)
730 {
731 if (s->isTemplateThisParameter())
732 property("kind", "this");
733 else
734 property("kind", "type");
735 property("type", "deco", type->specType);
736
737 property("default", "defaultDeco", type->defaultType);
738 }
739
740 TemplateValueParameter *value = s->isTemplateValueParameter();
741 if (value)
742 {
743 property("kind", "value");
744
745 property("type", "deco", value->valType);
746
747 if (value->specValue)
748 property("specValue", value->specValue->toChars());
749
750 if (value->defaultValue)
751 property("defaultValue", value->defaultValue->toChars());
752 }
753
754 TemplateAliasParameter *alias = s->isTemplateAliasParameter();
755 if (alias)
756 {
757 property("kind", "alias");
758
759 property("type", "deco", alias->specType);
760
761 if (alias->specAlias)
762 property("specAlias", alias->specAlias->toChars());
763
764 if (alias->defaultAlias)
765 property("defaultAlias", alias->defaultAlias->toChars());
766 }
767
768 TemplateTupleParameter *tuple = s->isTemplateTupleParameter();
769 if (tuple)
770 {
771 property("kind", "tuple");
772 }
773
774 objectEnd();
775 }
776 arrayEnd();
777
778 Expression *expression = d->constraint;
779 if (expression)
780 {
781 property("constraint", expression->toChars());
782 }
783
784 propertyStart("members");
785 arrayStart();
786 for (size_t i = 0; i < d->members->length; i++)
787 {
788 Dsymbol *s = (*d->members)[i];
789 s->accept(this);
790 }
791 arrayEnd();
792
793 objectEnd();
794 }
795
visit(EnumDeclaration * d)796 void visit(EnumDeclaration *d)
797 {
798 if (d->isAnonymous())
799 {
800 if (d->members)
801 {
802 for (size_t i = 0; i < d->members->length; i++)
803 {
804 Dsymbol *s = (*d->members)[i];
805 s->accept(this);
806 }
807 }
808 return;
809 }
810
811 objectStart();
812
813 jsonProperties(d);
814
815 property("base", "baseDeco", d->memtype);
816
817 if (d->members)
818 {
819 propertyStart("members");
820 arrayStart();
821 for (size_t i = 0; i < d->members->length; i++)
822 {
823 Dsymbol *s = (*d->members)[i];
824 s->accept(this);
825 }
826 arrayEnd();
827 }
828
829 objectEnd();
830 }
831
visit(EnumMember * s)832 void visit(EnumMember *s)
833 {
834 objectStart();
835
836 jsonProperties((Dsymbol*)s);
837
838 property("type", "deco", s->origType);
839
840 objectEnd();
841 }
842
visit(VarDeclaration * d)843 void visit(VarDeclaration *d)
844 {
845 if (d->storage_class & STClocal)
846 return;
847 objectStart();
848
849 jsonProperties(d);
850
851 if (d->_init)
852 property("init", d->_init->toChars());
853
854 if (d->isField())
855 property("offset", d->offset);
856
857 if (d->alignment && d->alignment != STRUCTALIGN_DEFAULT)
858 property("align", d->alignment);
859
860 objectEnd();
861 }
862
visit(TemplateMixin * d)863 void visit(TemplateMixin *d)
864 {
865 objectStart();
866
867 jsonProperties(d);
868
869 objectEnd();
870 }
871 };
872
873
json_generate(OutBuffer * buf,Modules * modules)874 void json_generate(OutBuffer *buf, Modules *modules)
875 {
876 ToJsonVisitor json(buf);
877
878 json.arrayStart();
879 for (size_t i = 0; i < modules->length; i++)
880 {
881 Module *m = (*modules)[i];
882 if (global.params.verbose)
883 message("json gen %s", m->toChars());
884 m->accept(&json);
885 }
886 json.arrayEnd();
887 json.removeComma();
888 }
889