summaryrefslogtreecommitdiffstats
path: root/js/src/vm/JSONParser.h
blob: 797aaca5f1b93226132de3c83e9c29f8ece4eae5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
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 */