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 #ifndef vm_JSONParser_h
8 #define vm_JSONParser_h
9 
10 #include "mozilla/Attributes.h"
11 #include "mozilla/Range.h"
12 
13 #include "jspubtd.h"
14 
15 #include "ds/IdValuePair.h"
16 #include "vm/StringType.h"
17 
18 namespace js {
19 
20 // JSONParser base class. JSONParser is templatized to work on either Latin1
21 // or TwoByte input strings, JSONParserBase holds all state and methods that
22 // can be shared between the two encodings.
23 class MOZ_STACK_CLASS JSONParserBase {
24  public:
25   enum class ParseType {
26     // Parsing a string as if by JSON.parse.
27     JSONParse,
28     // Parsing what may or may not be JSON in a string of eval code.
29     // In this case, a failure to parse indicates either syntax that isn't JSON,
30     // or syntax that has different semantics in eval code than in JSON.
31     AttemptForEval,
32   };
33 
34  private:
35   /* Data members */
36   Value v;
37 
38  protected:
39   JSContext* const cx;
40 
41   const ParseType parseType;
42 
43   enum Token {
44     String,
45     Number,
46     True,
47     False,
48     Null,
49     ArrayOpen,
50     ArrayClose,
51     ObjectOpen,
52     ObjectClose,
53     Colon,
54     Comma,
55     OOM,
56     Error
57   };
58 
59   // State related to the parser's current position. At all points in the
60   // parse this keeps track of the stack of arrays and objects which have
61   // been started but not finished yet. The actual JS object is not
62   // allocated until the literal is closed, so that the result can be sized
63   // according to its contents and have its type and shape filled in using
64   // caches.
65 
66   // State for an array that is currently being parsed. This includes all
67   // elements that have been seen so far.
68   typedef GCVector<Value, 20> ElementVector;
69 
70   // State for an object that is currently being parsed. This includes all
71   // the key/value pairs that have been seen so far.
72   typedef GCVector<IdValuePair, 10> PropertyVector;
73 
74   // Possible states the parser can be in between values.
75   enum ParserState {
76     // An array element has just being parsed.
77     FinishArrayElement,
78 
79     // An object property has just been parsed.
80     FinishObjectMember,
81 
82     // At the start of the parse, before any values have been processed.
83     JSONValue
84   };
85 
86   // Stack element for an in progress array or object.
87   struct StackEntry {
elementsStackEntry88     ElementVector& elements() {
89       MOZ_ASSERT(state == FinishArrayElement);
90       return *static_cast<ElementVector*>(vector);
91     }
92 
propertiesStackEntry93     PropertyVector& properties() {
94       MOZ_ASSERT(state == FinishObjectMember);
95       return *static_cast<PropertyVector*>(vector);
96     }
97 
StackEntryStackEntry98     explicit StackEntry(ElementVector* elements)
99         : state(FinishArrayElement), vector(elements) {}
100 
StackEntryStackEntry101     explicit StackEntry(PropertyVector* properties)
102         : state(FinishObjectMember), vector(properties) {}
103 
104     ParserState state;
105 
106    private:
107     void* vector;
108   };
109 
110   // All in progress arrays and objects being parsed, in order from outermost
111   // to innermost.
112   Vector<StackEntry, 10> stack;
113 
114   // Unused element and property vectors for previous in progress arrays and
115   // objects. These vectors are not freed until the end of the parse to avoid
116   // unnecessary freeing and allocation.
117   Vector<ElementVector*, 5> freeElements;
118   Vector<PropertyVector*, 5> freeProperties;
119 
120 #ifdef DEBUG
121   Token lastToken;
122 #endif
123 
JSONParserBase(JSContext * cx,ParseType parseType)124   JSONParserBase(JSContext* cx, ParseType parseType)
125       : cx(cx),
126         parseType(parseType),
127         stack(cx),
128         freeElements(cx),
129         freeProperties(cx)
130 #ifdef DEBUG
131         ,
132         lastToken(Error)
133 #endif
134   {
135   }
136   ~JSONParserBase();
137 
138   // Allow move construction for use with Rooted.
JSONParserBase(JSONParserBase && other)139   JSONParserBase(JSONParserBase&& other)
140       : v(other.v),
141         cx(other.cx),
142         parseType(other.parseType),
143         stack(std::move(other.stack)),
144         freeElements(std::move(other.freeElements)),
145         freeProperties(std::move(other.freeProperties))
146 #ifdef DEBUG
147         ,
148         lastToken(std::move(other.lastToken))
149 #endif
150   {
151   }
152 
numberValue()153   Value numberValue() const {
154     MOZ_ASSERT(lastToken == Number);
155     MOZ_ASSERT(v.isNumber());
156     return v;
157   }
158 
stringValue()159   Value stringValue() const {
160     MOZ_ASSERT(lastToken == String);
161     MOZ_ASSERT(v.isString());
162     return v;
163   }
164 
atomValue()165   JSAtom* atomValue() const {
166     Value strval = stringValue();
167     return &strval.toString()->asAtom();
168   }
169 
token(Token t)170   Token token(Token t) {
171     MOZ_ASSERT(t != String);
172     MOZ_ASSERT(t != Number);
173 #ifdef DEBUG
174     lastToken = t;
175 #endif
176     return t;
177   }
178 
stringToken(JSString * str)179   Token stringToken(JSString* str) {
180     this->v = StringValue(str);
181 #ifdef DEBUG
182     lastToken = String;
183 #endif
184     return String;
185   }
186 
numberToken(double d)187   Token numberToken(double d) {
188     this->v = NumberValue(d);
189 #ifdef DEBUG
190     lastToken = Number;
191 #endif
192     return Number;
193   }
194 
195   enum StringType { PropertyName, LiteralValue };
196 
197   bool errorReturn();
198 
199   bool finishObject(MutableHandleValue vp, PropertyVector& properties);
200   bool finishArray(MutableHandleValue vp, ElementVector& elements);
201 
202   void trace(JSTracer* trc);
203 
204  private:
205   JSONParserBase(const JSONParserBase& other) = delete;
206   void operator=(const JSONParserBase& other) = delete;
207 };
208 
209 template <typename CharT>
210 class MOZ_STACK_CLASS JSONParser : public JSONParserBase {
211  private:
212   using CharPtr = mozilla::RangedPtr<const CharT>;
213 
214   CharPtr current;
215   const CharPtr begin, end;
216 
217  public:
218   /* Public API */
219 
220   /* Create a parser for the provided JSON data. */
JSONParser(JSContext * cx,mozilla::Range<const CharT> data,ParseType parseType)221   JSONParser(JSContext* cx, mozilla::Range<const CharT> data,
222              ParseType parseType)
223       : JSONParserBase(cx, parseType),
224         current(data.begin()),
225         begin(current),
226         end(data.end()) {
227     MOZ_ASSERT(current <= end);
228   }
229 
230   /* Allow move construction for use with Rooted. */
JSONParser(JSONParser && other)231   JSONParser(JSONParser&& other)
232       : JSONParserBase(std::move(other)),
233         current(other.current),
234         begin(other.begin),
235         end(other.end) {}
236 
237   /*
238    * Parse the JSON data specified at construction time.  If it parses
239    * successfully, store the prescribed value in *vp and return true.  If an
240    * internal error (e.g. OOM) occurs during parsing, return false.
241    * Otherwise, if invalid input was specifed but no internal error occurred,
242    * behavior depends upon the error handling specified at construction: if
243    * error handling is RaiseError then throw a SyntaxError and return false,
244    * otherwise return true and set *vp to |undefined|.  (JSON syntax can't
245    * represent |undefined|, so the JSON data couldn't have specified it.)
246    */
247   bool parse(MutableHandleValue vp);
248 
trace(JSTracer * trc)249   void trace(JSTracer* trc) { JSONParserBase::trace(trc); }
250 
251  private:
252   template <StringType ST>
253   Token readString();
254 
255   Token readNumber();
256 
257   Token advance();
258   Token advancePropertyName();
259   Token advancePropertyColon();
260   Token advanceAfterProperty();
261   Token advanceAfterObjectOpen();
262   Token advanceAfterArrayElement();
263 
264   void error(const char* msg);
265 
266   void getTextPosition(uint32_t* column, uint32_t* line);
267 
268  private:
269   JSONParser(const JSONParser& other) = delete;
270   void operator=(const JSONParser& other) = delete;
271 };
272 
273 template <typename CharT, typename Wrapper>
274 class MutableWrappedPtrOperations<JSONParser<CharT>, Wrapper>
275     : public WrappedPtrOperations<JSONParser<CharT>, Wrapper> {
276  public:
parse(MutableHandleValue vp)277   bool parse(MutableHandleValue vp) {
278     return static_cast<Wrapper*>(this)->get().parse(vp);
279   }
280 };
281 
282 } /* namespace js */
283 
284 #endif /* vm_JSONParser_h */
285