1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jsonparser.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,216 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef jsonparser_h 1.11 +#define jsonparser_h 1.12 + 1.13 +#include "mozilla/Attributes.h" 1.14 + 1.15 +#include "ds/IdValuePair.h" 1.16 +#include "vm/String.h" 1.17 + 1.18 +namespace js { 1.19 + 1.20 +class MOZ_STACK_CLASS JSONParser : private AutoGCRooter 1.21 +{ 1.22 + public: 1.23 + enum ErrorHandling { RaiseError, NoError }; 1.24 + 1.25 + private: 1.26 + /* Data members */ 1.27 + 1.28 + JSContext * const cx; 1.29 + JS::ConstTwoByteChars current; 1.30 + const JS::ConstTwoByteChars begin, end; 1.31 + 1.32 + Value v; 1.33 + 1.34 + const ErrorHandling errorHandling; 1.35 + 1.36 + enum Token { String, Number, True, False, Null, 1.37 + ArrayOpen, ArrayClose, 1.38 + ObjectOpen, ObjectClose, 1.39 + Colon, Comma, 1.40 + OOM, Error }; 1.41 + 1.42 + // State related to the parser's current position. At all points in the 1.43 + // parse this keeps track of the stack of arrays and objects which have 1.44 + // been started but not finished yet. The actual JS object is not 1.45 + // allocated until the literal is closed, so that the result can be sized 1.46 + // according to its contents and have its type and shape filled in using 1.47 + // caches. 1.48 + 1.49 + // State for an array that is currently being parsed. This includes all 1.50 + // elements that have been seen so far. 1.51 + typedef Vector<Value, 20> ElementVector; 1.52 + 1.53 + // State for an object that is currently being parsed. This includes all 1.54 + // the key/value pairs that have been seen so far. 1.55 + typedef Vector<IdValuePair, 10> PropertyVector; 1.56 + 1.57 + // Possible states the parser can be in between values. 1.58 + enum ParserState { 1.59 + // An array element has just being parsed. 1.60 + FinishArrayElement, 1.61 + 1.62 + // An object property has just been parsed. 1.63 + FinishObjectMember, 1.64 + 1.65 + // At the start of the parse, before any values have been processed. 1.66 + JSONValue 1.67 + }; 1.68 + 1.69 + // Stack element for an in progress array or object. 1.70 + struct StackEntry { 1.71 + ElementVector &elements() { 1.72 + JS_ASSERT(state == FinishArrayElement); 1.73 + return * static_cast<ElementVector *>(vector); 1.74 + } 1.75 + 1.76 + PropertyVector &properties() { 1.77 + JS_ASSERT(state == FinishObjectMember); 1.78 + return * static_cast<PropertyVector *>(vector); 1.79 + } 1.80 + 1.81 + StackEntry(ElementVector *elements) 1.82 + : state(FinishArrayElement), vector(elements) 1.83 + {} 1.84 + 1.85 + StackEntry(PropertyVector *properties) 1.86 + : state(FinishObjectMember), vector(properties) 1.87 + {} 1.88 + 1.89 + ParserState state; 1.90 + 1.91 + private: 1.92 + void *vector; 1.93 + }; 1.94 + 1.95 + // All in progress arrays and objects being parsed, in order from outermost 1.96 + // to innermost. 1.97 + Vector<StackEntry, 10> stack; 1.98 + 1.99 + // Unused element and property vectors for previous in progress arrays and 1.100 + // objects. These vectors are not freed until the end of the parse to avoid 1.101 + // unnecessary freeing and allocation. 1.102 + Vector<ElementVector*, 5> freeElements; 1.103 + Vector<PropertyVector*, 5> freeProperties; 1.104 + 1.105 +#ifdef DEBUG 1.106 + Token lastToken; 1.107 +#endif 1.108 + 1.109 + public: 1.110 + /* Public API */ 1.111 + 1.112 + /* Create a parser for the provided JSON data. */ 1.113 + JSONParser(JSContext *cx, JS::ConstTwoByteChars data, size_t length, 1.114 + ErrorHandling errorHandling = RaiseError) 1.115 + : AutoGCRooter(cx, JSONPARSER), 1.116 + cx(cx), 1.117 + current(data), 1.118 + begin(data), 1.119 + end((data + length).get(), data.get(), length), 1.120 + errorHandling(errorHandling), 1.121 + stack(cx), 1.122 + freeElements(cx), 1.123 + freeProperties(cx) 1.124 +#ifdef DEBUG 1.125 + , lastToken(Error) 1.126 +#endif 1.127 + { 1.128 + JS_ASSERT(current <= end); 1.129 + } 1.130 + 1.131 + ~JSONParser(); 1.132 + 1.133 + /* 1.134 + * Parse the JSON data specified at construction time. If it parses 1.135 + * successfully, store the prescribed value in *vp and return true. If an 1.136 + * internal error (e.g. OOM) occurs during parsing, return false. 1.137 + * Otherwise, if invalid input was specifed but no internal error occurred, 1.138 + * behavior depends upon the error handling specified at construction: if 1.139 + * error handling is RaiseError then throw a SyntaxError and return false, 1.140 + * otherwise return true and set *vp to |undefined|. (JSON syntax can't 1.141 + * represent |undefined|, so the JSON data couldn't have specified it.) 1.142 + */ 1.143 + bool parse(MutableHandleValue vp); 1.144 + 1.145 + private: 1.146 + Value numberValue() const { 1.147 + JS_ASSERT(lastToken == Number); 1.148 + JS_ASSERT(v.isNumber()); 1.149 + return v; 1.150 + } 1.151 + 1.152 + Value stringValue() const { 1.153 + JS_ASSERT(lastToken == String); 1.154 + JS_ASSERT(v.isString()); 1.155 + return v; 1.156 + } 1.157 + 1.158 + JSAtom *atomValue() const { 1.159 + Value strval = stringValue(); 1.160 + return &strval.toString()->asAtom(); 1.161 + } 1.162 + 1.163 + Token token(Token t) { 1.164 + JS_ASSERT(t != String); 1.165 + JS_ASSERT(t != Number); 1.166 +#ifdef DEBUG 1.167 + lastToken = t; 1.168 +#endif 1.169 + return t; 1.170 + } 1.171 + 1.172 + Token stringToken(JSString *str) { 1.173 + this->v = StringValue(str); 1.174 +#ifdef DEBUG 1.175 + lastToken = String; 1.176 +#endif 1.177 + return String; 1.178 + } 1.179 + 1.180 + Token numberToken(double d) { 1.181 + this->v = NumberValue(d); 1.182 +#ifdef DEBUG 1.183 + lastToken = Number; 1.184 +#endif 1.185 + return Number; 1.186 + } 1.187 + 1.188 + enum StringType { PropertyName, LiteralValue }; 1.189 + template<StringType ST> Token readString(); 1.190 + 1.191 + Token readNumber(); 1.192 + 1.193 + Token advance(); 1.194 + Token advancePropertyName(); 1.195 + Token advancePropertyColon(); 1.196 + Token advanceAfterProperty(); 1.197 + Token advanceAfterObjectOpen(); 1.198 + Token advanceAfterArrayElement(); 1.199 + 1.200 + void error(const char *msg); 1.201 + bool errorReturn(); 1.202 + 1.203 + JSObject *createFinishedObject(PropertyVector &properties); 1.204 + bool finishObject(MutableHandleValue vp, PropertyVector &properties); 1.205 + bool finishArray(MutableHandleValue vp, ElementVector &elements); 1.206 + 1.207 + void getTextPosition(uint32_t *column, uint32_t *line); 1.208 + 1.209 + friend void AutoGCRooter::trace(JSTracer *trc); 1.210 + void trace(JSTracer *trc); 1.211 + 1.212 + private: 1.213 + JSONParser(const JSONParser &other) MOZ_DELETE; 1.214 + void operator=(const JSONParser &other) MOZ_DELETE; 1.215 +}; 1.216 + 1.217 +} /* namespace js */ 1.218 + 1.219 +#endif /* jsonparser_h */