1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "vm/JSONPrinter.h"
8 
9 #include "mozilla/Assertions.h"
10 #include "mozilla/FloatingPoint.h"
11 #include "mozilla/IntegerPrintfMacros.h"
12 
13 #include <stdarg.h>
14 
15 #include "util/DoubleToString.h"
16 
17 using namespace js;
18 
~JSONPrinter()19 JSONPrinter::~JSONPrinter() {
20   if (dtoaState_) {
21     DestroyDtoaState(dtoaState_);
22   }
23 }
24 
indent()25 void JSONPrinter::indent() {
26   MOZ_ASSERT(indentLevel_ >= 0);
27   if (indent_) {
28     out_.putChar('\n');
29     for (int i = 0; i < indentLevel_; i++) {
30       out_.put("  ");
31     }
32   }
33 }
34 
propertyName(const char * name)35 void JSONPrinter::propertyName(const char* name) {
36   if (!first_) {
37     out_.putChar(',');
38   }
39   indent();
40   out_.printf("\"%s\":", name);
41   first_ = false;
42 }
43 
beginObject()44 void JSONPrinter::beginObject() {
45   if (!first_) {
46     out_.putChar(',');
47     indent();
48   }
49   out_.putChar('{');
50   indentLevel_++;
51   first_ = true;
52 }
53 
beginList()54 void JSONPrinter::beginList() {
55   if (!first_) {
56     out_.putChar(',');
57   }
58   out_.putChar('[');
59   first_ = true;
60 }
61 
beginObjectProperty(const char * name)62 void JSONPrinter::beginObjectProperty(const char* name) {
63   propertyName(name);
64   out_.putChar('{');
65   indentLevel_++;
66   first_ = true;
67 }
68 
beginListProperty(const char * name)69 void JSONPrinter::beginListProperty(const char* name) {
70   propertyName(name);
71   out_.putChar('[');
72   first_ = true;
73 }
74 
beginStringProperty(const char * name)75 void JSONPrinter::beginStringProperty(const char* name) {
76   propertyName(name);
77   out_.putChar('"');
78 }
79 
endStringProperty()80 void JSONPrinter::endStringProperty() { out_.putChar('"'); }
81 
property(const char * name,const char * value)82 void JSONPrinter::property(const char* name, const char* value) {
83   beginStringProperty(name);
84   out_.put(value);
85   endStringProperty();
86 }
87 
formatProperty(const char * name,const char * format,...)88 void JSONPrinter::formatProperty(const char* name, const char* format, ...) {
89   va_list ap;
90   va_start(ap, format);
91 
92   beginStringProperty(name);
93   out_.vprintf(format, ap);
94   endStringProperty();
95 
96   va_end(ap);
97 }
98 
formatProperty(const char * name,const char * format,va_list ap)99 void JSONPrinter::formatProperty(const char* name, const char* format,
100                                  va_list ap) {
101   beginStringProperty(name);
102   out_.vprintf(format, ap);
103   endStringProperty();
104 }
105 
value(const char * format,...)106 void JSONPrinter::value(const char* format, ...) {
107   va_list ap;
108   va_start(ap, format);
109 
110   if (!first_) {
111     out_.putChar(',');
112   }
113   out_.putChar('"');
114   out_.vprintf(format, ap);
115   out_.putChar('"');
116 
117   va_end(ap);
118   first_ = false;
119 }
120 
property(const char * name,int32_t value)121 void JSONPrinter::property(const char* name, int32_t value) {
122   propertyName(name);
123   out_.printf("%" PRId32, value);
124 }
125 
value(int val)126 void JSONPrinter::value(int val) {
127   if (!first_) {
128     out_.putChar(',');
129   }
130   out_.printf("%d", val);
131   first_ = false;
132 }
133 
property(const char * name,uint32_t value)134 void JSONPrinter::property(const char* name, uint32_t value) {
135   propertyName(name);
136   out_.printf("%" PRIu32, value);
137 }
138 
property(const char * name,int64_t value)139 void JSONPrinter::property(const char* name, int64_t value) {
140   propertyName(name);
141   out_.printf("%" PRId64, value);
142 }
143 
property(const char * name,uint64_t value)144 void JSONPrinter::property(const char* name, uint64_t value) {
145   propertyName(name);
146   out_.printf("%" PRIu64, value);
147 }
148 
149 #if defined(XP_DARWIN) || defined(__OpenBSD__)
property(const char * name,size_t value)150 void JSONPrinter::property(const char* name, size_t value) {
151   propertyName(name);
152   out_.printf("%zu", value);
153 }
154 #endif
155 
floatProperty(const char * name,double value,size_t precision)156 void JSONPrinter::floatProperty(const char* name, double value,
157                                 size_t precision) {
158   if (!mozilla::IsFinite(value)) {
159     propertyName(name);
160     out_.put("null");
161     return;
162   }
163 
164   if (!dtoaState_) {
165     dtoaState_ = NewDtoaState();
166     if (!dtoaState_) {
167       out_.reportOutOfMemory();
168       return;
169     }
170   }
171 
172   char buffer[DTOSTR_STANDARD_BUFFER_SIZE];
173   char* str = js_dtostr(dtoaState_, buffer, sizeof(buffer), DTOSTR_STANDARD,
174                         precision, value);
175   if (!str) {
176     out_.reportOutOfMemory();
177     return;
178   }
179 
180   property(name, str);
181 }
182 
property(const char * name,const mozilla::TimeDuration & dur,TimePrecision precision)183 void JSONPrinter::property(const char* name, const mozilla::TimeDuration& dur,
184                            TimePrecision precision) {
185   if (precision == MICROSECONDS) {
186     property(name, static_cast<int64_t>(dur.ToMicroseconds()));
187     return;
188   }
189 
190   propertyName(name);
191   lldiv_t split;
192   switch (precision) {
193     case SECONDS:
194       split = lldiv(static_cast<int64_t>(dur.ToMilliseconds()), 1000);
195       break;
196     case MILLISECONDS:
197       split = lldiv(static_cast<int64_t>(dur.ToMicroseconds()), 1000);
198       break;
199     case MICROSECONDS:
200       MOZ_ASSERT_UNREACHABLE("");
201   };
202   out_.printf("%lld.%03lld", split.quot, split.rem);
203 }
204 
endObject()205 void JSONPrinter::endObject() {
206   indentLevel_--;
207   indent();
208   out_.putChar('}');
209   first_ = false;
210 }
211 
endList()212 void JSONPrinter::endList() {
213   out_.putChar(']');
214   first_ = false;
215 }
216