/* -*- 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 jsonparser_h #define jsonparser_h #include "mozilla/Attributes.h" #include "ds/IdValuePair.h" #include "vm/String.h" namespace js { class MOZ_STACK_CLASS JSONParser : private AutoGCRooter { public: enum ErrorHandling { RaiseError, NoError }; private: /* Data members */ JSContext * const cx; JS::ConstTwoByteChars current; const JS::ConstTwoByteChars begin, end; Value v; 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 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 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() { JS_ASSERT(state == FinishArrayElement); return * static_cast(vector); } PropertyVector &properties() { JS_ASSERT(state == FinishObjectMember); return * static_cast(vector); } StackEntry(ElementVector *elements) : state(FinishArrayElement), vector(elements) {} 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 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 freeElements; Vector freeProperties; #ifdef DEBUG Token lastToken; #endif public: /* Public API */ /* Create a parser for the provided JSON data. */ JSONParser(JSContext *cx, JS::ConstTwoByteChars data, size_t length, ErrorHandling errorHandling = RaiseError) : AutoGCRooter(cx, JSONPARSER), cx(cx), current(data), begin(data), end((data + length).get(), data.get(), length), errorHandling(errorHandling), stack(cx), freeElements(cx), freeProperties(cx) #ifdef DEBUG , lastToken(Error) #endif { JS_ASSERT(current <= end); } ~JSONParser(); /* * 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); private: Value numberValue() const { JS_ASSERT(lastToken == Number); JS_ASSERT(v.isNumber()); return v; } Value stringValue() const { JS_ASSERT(lastToken == String); JS_ASSERT(v.isString()); return v; } JSAtom *atomValue() const { Value strval = stringValue(); return &strval.toString()->asAtom(); } Token token(Token t) { JS_ASSERT(t != String); JS_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 }; template Token readString(); Token readNumber(); Token advance(); Token advancePropertyName(); Token advancePropertyColon(); Token advanceAfterProperty(); Token advanceAfterObjectOpen(); Token advanceAfterArrayElement(); void error(const char *msg); bool errorReturn(); JSObject *createFinishedObject(PropertyVector &properties); bool finishObject(MutableHandleValue vp, PropertyVector &properties); bool finishArray(MutableHandleValue vp, ElementVector &elements); void getTextPosition(uint32_t *column, uint32_t *line); friend void AutoGCRooter::trace(JSTracer *trc); void trace(JSTracer *trc); private: JSONParser(const JSONParser &other) MOZ_DELETE; void operator=(const JSONParser &other) MOZ_DELETE; }; } /* namespace js */ #endif /* jsonparser_h */