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