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