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