diff options
Diffstat (limited to 'js/src/vm/JSONParser.h')
-rw-r--r-- | js/src/vm/JSONParser.h | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/js/src/vm/JSONParser.h b/js/src/vm/JSONParser.h new file mode 100644 index 000000000..797aaca5f --- /dev/null +++ b/js/src/vm/JSONParser.h @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_JSONParser_h +#define vm_JSONParser_h + +#include "mozilla/Attributes.h" +#include "mozilla/Range.h" + +#include "jspubtd.h" + +#include "ds/IdValuePair.h" +#include "vm/String.h" + +namespace js { + +// JSONParser base class. JSONParser is templatized to work on either Latin1 +// or TwoByte input strings, JSONParserBase holds all state and methods that +// can be shared between the two encodings. +class MOZ_STACK_CLASS JSONParserBase +{ + public: + enum ErrorHandling { RaiseError, NoError }; + + private: + /* Data members */ + Value v; + + protected: + JSContext * const cx; + + const ErrorHandling errorHandling; + + enum Token { String, Number, True, False, Null, + ArrayOpen, ArrayClose, + ObjectOpen, ObjectClose, + Colon, Comma, + OOM, Error }; + + // State related to the parser's current position. At all points in the + // parse this keeps track of the stack of arrays and objects which have + // been started but not finished yet. The actual JS object is not + // allocated until the literal is closed, so that the result can be sized + // according to its contents and have its type and shape filled in using + // caches. + + // State for an array that is currently being parsed. This includes all + // elements that have been seen so far. + typedef Vector<Value, 20> ElementVector; + + // State for an object that is currently being parsed. This includes all + // the key/value pairs that have been seen so far. + typedef Vector<IdValuePair, 10> PropertyVector; + + // Possible states the parser can be in between values. + enum ParserState { + // An array element has just being parsed. + FinishArrayElement, + + // An object property has just been parsed. + FinishObjectMember, + + // At the start of the parse, before any values have been processed. + JSONValue + }; + + // Stack element for an in progress array or object. + struct StackEntry { + ElementVector& elements() { + MOZ_ASSERT(state == FinishArrayElement); + return * static_cast<ElementVector*>(vector); + } + + PropertyVector& properties() { + MOZ_ASSERT(state == FinishObjectMember); + return * static_cast<PropertyVector*>(vector); + } + + explicit StackEntry(ElementVector* elements) + : state(FinishArrayElement), vector(elements) + {} + + explicit StackEntry(PropertyVector* properties) + : state(FinishObjectMember), vector(properties) + {} + + ParserState state; + + private: + void* vector; + }; + + // All in progress arrays and objects being parsed, in order from outermost + // to innermost. + Vector<StackEntry, 10> stack; + + // Unused element and property vectors for previous in progress arrays and + // objects. These vectors are not freed until the end of the parse to avoid + // unnecessary freeing and allocation. + Vector<ElementVector*, 5> freeElements; + Vector<PropertyVector*, 5> freeProperties; + +#ifdef DEBUG + Token lastToken; +#endif + + JSONParserBase(JSContext* cx, ErrorHandling errorHandling) + : cx(cx), + errorHandling(errorHandling), + stack(cx), + freeElements(cx), + freeProperties(cx) +#ifdef DEBUG + , lastToken(Error) +#endif + {} + ~JSONParserBase(); + + // Allow move construction for use with Rooted. + JSONParserBase(JSONParserBase&& other) + : v(other.v), + cx(other.cx), + errorHandling(other.errorHandling), + stack(mozilla::Move(other.stack)), + freeElements(mozilla::Move(other.freeElements)), + freeProperties(mozilla::Move(other.freeProperties)) +#ifdef DEBUG + , lastToken(mozilla::Move(other.lastToken)) +#endif + {} + + + Value numberValue() const { + MOZ_ASSERT(lastToken == Number); + MOZ_ASSERT(v.isNumber()); + return v; + } + + Value stringValue() const { + MOZ_ASSERT(lastToken == String); + MOZ_ASSERT(v.isString()); + return v; + } + + JSAtom* atomValue() const { + Value strval = stringValue(); + return &strval.toString()->asAtom(); + } + + Token token(Token t) { + MOZ_ASSERT(t != String); + MOZ_ASSERT(t != Number); +#ifdef DEBUG + lastToken = t; +#endif + return t; + } + + Token stringToken(JSString* str) { + this->v = StringValue(str); +#ifdef DEBUG + lastToken = String; +#endif + return String; + } + + Token numberToken(double d) { + this->v = NumberValue(d); +#ifdef DEBUG + lastToken = Number; +#endif + return Number; + } + + enum StringType { PropertyName, LiteralValue }; + + bool errorReturn(); + + bool finishObject(MutableHandleValue vp, PropertyVector& properties); + bool finishArray(MutableHandleValue vp, ElementVector& elements); + + void trace(JSTracer* trc); + + private: + JSONParserBase(const JSONParserBase& other) = delete; + void operator=(const JSONParserBase& other) = delete; +}; + +template <typename CharT> +class MOZ_STACK_CLASS JSONParser : public JSONParserBase +{ + private: + typedef mozilla::RangedPtr<const CharT> CharPtr; + + CharPtr current; + const CharPtr begin, end; + + public: + /* Public API */ + + /* Create a parser for the provided JSON data. */ + JSONParser(JSContext* cx, mozilla::Range<const CharT> data, + ErrorHandling errorHandling = RaiseError) + : JSONParserBase(cx, errorHandling), + current(data.begin()), + begin(current), + end(data.end()) + { + MOZ_ASSERT(current <= end); + } + + /* Allow move construction for use with Rooted. */ + JSONParser(JSONParser&& other) + : JSONParserBase(mozilla::Move(other)), + current(other.current), + begin(other.begin), + end(other.end) + {} + + /* + * Parse the JSON data specified at construction time. If it parses + * successfully, store the prescribed value in *vp and return true. If an + * internal error (e.g. OOM) occurs during parsing, return false. + * Otherwise, if invalid input was specifed but no internal error occurred, + * behavior depends upon the error handling specified at construction: if + * error handling is RaiseError then throw a SyntaxError and return false, + * otherwise return true and set *vp to |undefined|. (JSON syntax can't + * represent |undefined|, so the JSON data couldn't have specified it.) + */ + bool parse(MutableHandleValue vp); + + static void trace(JSONParser<CharT>* parser, JSTracer* trc) { parser->trace(trc); } + void trace(JSTracer* trc) { JSONParserBase::trace(trc); } + + private: + template<StringType ST> Token readString(); + + Token readNumber(); + + Token advance(); + Token advancePropertyName(); + Token advancePropertyColon(); + Token advanceAfterProperty(); + Token advanceAfterObjectOpen(); + Token advanceAfterArrayElement(); + + void error(const char* msg); + + void getTextPosition(uint32_t* column, uint32_t* line); + + private: + JSONParser(const JSONParser& other) = delete; + void operator=(const JSONParser& other) = delete; +}; + +template <typename CharT> +struct RootedBase<JSONParser<CharT>> { + bool parse(MutableHandleValue vp) { + return static_cast<Rooted<JSONParser<CharT>>*>(this)->get().parse(vp); + } +}; + +} /* namespace js */ + +#endif /* vm_JSONParser_h */ |