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