~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/3rdparty/javascriptcore/JavaScriptCore/runtime/JSONObject.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2009 Apple Inc. All rights reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions
 
6
 * are met:
 
7
 * 1. Redistributions of source code must retain the above copyright
 
8
 *    notice, this list of conditions and the following disclaimer.
 
9
 * 2. Redistributions in binary form must reproduce the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer in the
 
11
 *    documentation and/or other materials provided with the distribution.
 
12
 *
 
13
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 
14
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
15
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
16
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 
17
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
18
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
19
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
20
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 
21
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
23
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 
24
 */
 
25
 
 
26
#include "config.h"
 
27
#include "JSONObject.h"
 
28
 
 
29
#include "BooleanObject.h"
 
30
#include "Error.h"
 
31
#include "ExceptionHelpers.h"
 
32
#include "JSArray.h"
 
33
#include "LiteralParser.h"
 
34
#include "PropertyNameArray.h"
 
35
#include <wtf/MathExtras.h>
 
36
 
 
37
namespace JSC {
 
38
 
 
39
ASSERT_CLASS_FITS_IN_CELL(JSONObject);
 
40
 
 
41
static JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*, JSObject*, JSValue, const ArgList&);
 
42
static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSValue, const ArgList&);
 
43
 
 
44
}
 
45
 
 
46
#include "JSONObject.lut.h"
 
47
 
 
48
namespace JSC {
 
49
 
 
50
// PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked.
 
51
class PropertyNameForFunctionCall {
 
52
public:
 
53
    PropertyNameForFunctionCall(const Identifier&);
 
54
    PropertyNameForFunctionCall(unsigned);
 
55
 
 
56
    JSValue value(ExecState*) const;
 
57
 
 
58
private:
 
59
    const Identifier* m_identifier;
 
60
    unsigned m_number;
 
61
    mutable JSValue m_value;
 
62
};
 
63
 
 
64
class Stringifier : public Noncopyable {
 
65
public:
 
66
    Stringifier(ExecState*, JSValue replacer, JSValue space);
 
67
    ~Stringifier();
 
68
    JSValue stringify(JSValue);
 
69
 
 
70
    void markAggregate(MarkStack&);
 
71
 
 
72
private:
 
73
    typedef UString StringBuilder;
 
74
 
 
75
    class Holder {
 
76
    public:
 
77
        Holder(JSObject*);
 
78
 
 
79
        JSObject* object() const { return m_object; }
 
80
 
 
81
        bool appendNextProperty(Stringifier&, StringBuilder&);
 
82
 
 
83
    private:
 
84
        JSObject* const m_object;
 
85
        const bool m_isArray;
 
86
        bool m_isJSArray;
 
87
        unsigned m_index;
 
88
        unsigned m_size;
 
89
        RefPtr<PropertyNameArrayData> m_propertyNames;
 
90
    };
 
91
 
 
92
    friend class Holder;
 
93
 
 
94
    static void appendQuotedString(StringBuilder&, const UString&);
 
95
 
 
96
    JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
 
97
 
 
98
    enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedValue };
 
99
    StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&);
 
100
 
 
101
    bool willIndent() const;
 
102
    void indent();
 
103
    void unindent();
 
104
    void startNewLine(StringBuilder&) const;
 
105
 
 
106
    Stringifier* const m_nextStringifierToMark;
 
107
    ExecState* const m_exec;
 
108
    const JSValue m_replacer;
 
109
    bool m_usingArrayReplacer;
 
110
    PropertyNameArray m_arrayReplacerPropertyNames;
 
111
    CallType m_replacerCallType;
 
112
    CallData m_replacerCallData;
 
113
    const UString m_gap;
 
114
 
 
115
    HashSet<JSObject*> m_holderCycleDetector;
 
116
    Vector<Holder, 16> m_holderStack;
 
117
    UString m_repeatedGap;
 
118
    UString m_indent;
 
119
};
 
120
 
 
121
// ------------------------------ helper functions --------------------------------
 
122
 
 
123
static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value)
 
124
{
 
125
    if (!value.isObject())
 
126
        return value;
 
127
    JSObject* object = asObject(value);
 
128
    if (object->inherits(&NumberObject::info))
 
129
        return jsNumber(exec, object->toNumber(exec));
 
130
    if (object->inherits(&StringObject::info))
 
131
        return jsString(exec, object->toString(exec));
 
132
    if (object->inherits(&BooleanObject::info))
 
133
        return object->toPrimitive(exec);
 
134
    return value;
 
135
}
 
136
 
 
137
static inline UString gap(ExecState* exec, JSValue space)
 
138
{
 
139
    const int maxGapLength = 10;
 
140
    space = unwrapBoxedPrimitive(exec, space);
 
141
 
 
142
    // If the space value is a number, create a gap string with that number of spaces.
 
143
    double spaceCount;
 
144
    if (space.getNumber(spaceCount)) {
 
145
        int count;
 
146
        if (spaceCount > maxGapLength)
 
147
            count = maxGapLength;
 
148
        else if (!(spaceCount > 0))
 
149
            count = 0;
 
150
        else
 
151
            count = static_cast<int>(spaceCount);
 
152
        UChar spaces[maxGapLength];
 
153
        for (int i = 0; i < count; ++i)
 
154
            spaces[i] = ' ';
 
155
        return UString(spaces, count);
 
156
    }
 
157
 
 
158
    // If the space value is a string, use it as the gap string, otherwise use no gap string.
 
159
    UString spaces = space.getString();
 
160
    if (spaces.size() > maxGapLength) {
 
161
        spaces = spaces.substr(0, maxGapLength);
 
162
    }
 
163
    return spaces;
 
164
}
 
165
 
 
166
// ------------------------------ PropertyNameForFunctionCall --------------------------------
 
167
 
 
168
inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier)
 
169
    : m_identifier(&identifier)
 
170
{
 
171
}
 
172
 
 
173
inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number)
 
174
    : m_identifier(0)
 
175
    , m_number(number)
 
176
{
 
177
}
 
178
 
 
179
JSValue PropertyNameForFunctionCall::value(ExecState* exec) const
 
180
{
 
181
    if (!m_value) {
 
182
        if (m_identifier)
 
183
            m_value = jsString(exec, m_identifier->ustring());
 
184
        else
 
185
            m_value = jsNumber(exec, m_number);
 
186
    }
 
187
    return m_value;
 
188
}
 
189
 
 
190
// ------------------------------ Stringifier --------------------------------
 
191
 
 
192
Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space)
 
193
    : m_nextStringifierToMark(exec->globalData().firstStringifierToMark)
 
194
    , m_exec(exec)
 
195
    , m_replacer(replacer)
 
196
    , m_usingArrayReplacer(false)
 
197
    , m_arrayReplacerPropertyNames(exec)
 
198
    , m_replacerCallType(CallTypeNone)
 
199
    , m_gap(gap(exec, space))
 
200
{
 
201
    exec->globalData().firstStringifierToMark = this;
 
202
 
 
203
    if (!m_replacer.isObject())
 
204
        return;
 
205
 
 
206
    if (asObject(m_replacer)->inherits(&JSArray::info)) {
 
207
        m_usingArrayReplacer = true;
 
208
        JSObject* array = asObject(m_replacer);
 
209
        unsigned length = array->get(exec, exec->globalData().propertyNames->length).toUInt32(exec);
 
210
        for (unsigned i = 0; i < length; ++i) {
 
211
            JSValue name = array->get(exec, i);
 
212
            if (exec->hadException())
 
213
                break;
 
214
 
 
215
            UString propertyName;
 
216
            if (name.getString(propertyName)) {
 
217
                m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName));
 
218
                continue;
 
219
            }
 
220
 
 
221
            double value = 0;
 
222
            if (name.getNumber(value)) {
 
223
                m_arrayReplacerPropertyNames.add(Identifier::from(exec, value));
 
224
                continue;
 
225
            }
 
226
 
 
227
            if (name.isObject()) {
 
228
                if (!asObject(name)->inherits(&NumberObject::info) && !asObject(name)->inherits(&StringObject::info))
 
229
                    continue;
 
230
                propertyName = name.toString(exec);
 
231
                if (exec->hadException())
 
232
                    break;
 
233
                m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName));
 
234
            }
 
235
        }
 
236
        return;
 
237
    }
 
238
 
 
239
    m_replacerCallType = asObject(m_replacer)->getCallData(m_replacerCallData);
 
240
}
 
241
 
 
242
Stringifier::~Stringifier()
 
243
{
 
244
    ASSERT(m_exec->globalData().firstStringifierToMark == this);
 
245
    m_exec->globalData().firstStringifierToMark = m_nextStringifierToMark;
 
246
}
 
247
 
 
248
void Stringifier::markAggregate(MarkStack& markStack)
 
249
{
 
250
    for (Stringifier* stringifier = this; stringifier; stringifier = stringifier->m_nextStringifierToMark) {
 
251
        size_t size = m_holderStack.size();
 
252
        for (size_t i = 0; i < size; ++i)
 
253
            markStack.append(m_holderStack[i].object());
 
254
    }
 
255
}
 
256
 
 
257
JSValue Stringifier::stringify(JSValue value)
 
258
{
 
259
    JSObject* object = constructEmptyObject(m_exec);
 
260
    if (m_exec->hadException())
 
261
        return jsNull();
 
262
 
 
263
    PropertyNameForFunctionCall emptyPropertyName(m_exec->globalData().propertyNames->emptyIdentifier);
 
264
    object->putDirect(m_exec->globalData().propertyNames->emptyIdentifier, value);
 
265
 
 
266
    StringBuilder result;
 
267
    if (appendStringifiedValue(result, value, object, emptyPropertyName) != StringifySucceeded)
 
268
        return jsUndefined();
 
269
    if (m_exec->hadException())
 
270
        return jsNull();
 
271
 
 
272
    return jsString(m_exec, result);
 
273
}
 
274
 
 
275
void Stringifier::appendQuotedString(StringBuilder& builder, const UString& value)
 
276
{
 
277
    int length = value.size();
 
278
 
 
279
    // String length plus 2 for quote marks plus 8 so we can accomodate a few escaped characters.
 
280
    builder.reserveCapacity(builder.size() + length + 2 + 8);
 
281
 
 
282
    builder.append('"');
 
283
 
 
284
    const UChar* data = value.data();
 
285
    for (int i = 0; i < length; ++i) {
 
286
        int start = i;
 
287
        while (i < length && (data[i] > 0x1F && data[i] != '"' && data[i] != '\\'))
 
288
            ++i;
 
289
        builder.append(data + start, i - start);
 
290
        if (i >= length)
 
291
            break;
 
292
        switch (data[i]) {
 
293
            case '\t':
 
294
                builder.append('\\');
 
295
                builder.append('t');
 
296
                break;
 
297
            case '\r':
 
298
                builder.append('\\');
 
299
                builder.append('r');
 
300
                break;
 
301
            case '\n':
 
302
                builder.append('\\');
 
303
                builder.append('n');
 
304
                break;
 
305
            case '\f':
 
306
                builder.append('\\');
 
307
                builder.append('f');
 
308
                break;
 
309
            case '\b':
 
310
                builder.append('\\');
 
311
                builder.append('b');
 
312
                break;
 
313
            case '"':
 
314
                builder.append('\\');
 
315
                builder.append('"');
 
316
                break;
 
317
            case '\\':
 
318
                builder.append('\\');
 
319
                builder.append('\\');
 
320
                break;
 
321
            default:
 
322
                static const char hexDigits[] = "0123456789abcdef";
 
323
                UChar ch = data[i];
 
324
                UChar hex[] = { '\\', 'u', hexDigits[(ch >> 12) & 0xF], hexDigits[(ch >> 8) & 0xF], hexDigits[(ch >> 4) & 0xF], hexDigits[ch & 0xF] };
 
325
                builder.append(hex, sizeof(hex) / sizeof(UChar));
 
326
                break;
 
327
        }
 
328
    }
 
329
 
 
330
    builder.append('"');
 
331
}
 
332
 
 
333
inline JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName)
 
334
{
 
335
    ASSERT(!m_exec->hadException());
 
336
    if (!value.isObject() || !asObject(value)->hasProperty(m_exec, m_exec->globalData().propertyNames->toJSON))
 
337
        return value;
 
338
 
 
339
    JSValue toJSONFunction = asObject(value)->get(m_exec, m_exec->globalData().propertyNames->toJSON);
 
340
    if (m_exec->hadException())
 
341
        return jsNull();
 
342
 
 
343
    if (!toJSONFunction.isObject())
 
344
        return value;
 
345
 
 
346
    JSObject* object = asObject(toJSONFunction);
 
347
    CallData callData;
 
348
    CallType callType = object->getCallData(callData);
 
349
    if (callType == CallTypeNone)
 
350
        return value;
 
351
 
 
352
    JSValue list[] = { propertyName.value(m_exec) };
 
353
    ArgList args(list, sizeof(list) / sizeof(JSValue));
 
354
    return call(m_exec, object, callType, callData, value, args);
 
355
}
 
356
 
 
357
Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName)
 
358
{
 
359
    // Call the toJSON function.
 
360
    value = toJSON(value, propertyName);
 
361
    if (m_exec->hadException())
 
362
        return StringifyFailed;
 
363
 
 
364
    // Call the replacer function.
 
365
    if (m_replacerCallType != CallTypeNone) {
 
366
        JSValue list[] = { propertyName.value(m_exec), value };
 
367
        ArgList args(list, sizeof(list) / sizeof(JSValue));
 
368
        value = call(m_exec, m_replacer, m_replacerCallType, m_replacerCallData, holder, args);
 
369
        if (m_exec->hadException())
 
370
            return StringifyFailed;
 
371
    }
 
372
 
 
373
    if (value.isUndefined() && !holder->inherits(&JSArray::info))
 
374
        return StringifyFailedDueToUndefinedValue;
 
375
 
 
376
    if (value.isNull()) {
 
377
        builder.append("null");
 
378
        return StringifySucceeded;
 
379
    }
 
380
 
 
381
    value = unwrapBoxedPrimitive(m_exec, value);
 
382
 
 
383
    if (m_exec->hadException())
 
384
        return StringifyFailed;
 
385
 
 
386
    if (value.isBoolean()) {
 
387
        builder.append(value.getBoolean() ? "true" : "false");
 
388
        return StringifySucceeded;
 
389
    }
 
390
 
 
391
    UString stringValue;
 
392
    if (value.getString(stringValue)) {
 
393
        appendQuotedString(builder, stringValue);
 
394
        return StringifySucceeded;
 
395
    }
 
396
 
 
397
    double numericValue;
 
398
    if (value.getNumber(numericValue)) {
 
399
        if (!isfinite(numericValue))
 
400
            builder.append("null");
 
401
        else
 
402
            builder.append(UString::from(numericValue));
 
403
        return StringifySucceeded;
 
404
    }
 
405
 
 
406
    if (!value.isObject())
 
407
        return StringifyFailed;
 
408
 
 
409
    JSObject* object = asObject(value);
 
410
 
 
411
    CallData callData;
 
412
    if (object->getCallData(callData) != CallTypeNone) {
 
413
        if (holder->inherits(&JSArray::info)) {
 
414
            builder.append("null");
 
415
            return StringifySucceeded;
 
416
        }
 
417
        return StringifyFailedDueToUndefinedValue;
 
418
    }
 
419
 
 
420
    // Handle cycle detection, and put the holder on the stack.
 
421
    if (!m_holderCycleDetector.add(object).second) {
 
422
        throwError(m_exec, TypeError, "JSON.stringify cannot serialize cyclic structures.");
 
423
        return StringifyFailed;
 
424
    }
 
425
    bool holderStackWasEmpty = m_holderStack.isEmpty();
 
426
    m_holderStack.append(object);
 
427
    if (!holderStackWasEmpty)
 
428
        return StringifySucceeded;
 
429
 
 
430
    // If this is the outermost call, then loop to handle everything on the holder stack.
 
431
    //TimeoutChecker localTimeoutChecker(*m_exec->globalData().timeoutChecker);
 
432
    TimeoutChecker localTimeoutChecker;
 
433
    localTimeoutChecker.copyTimeoutValues(m_exec->globalData().timeoutChecker);
 
434
    localTimeoutChecker.reset();
 
435
    unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck();
 
436
    do {
 
437
        while (m_holderStack.last().appendNextProperty(*this, builder)) {
 
438
            if (m_exec->hadException())
 
439
                return StringifyFailed;
 
440
            if (!--tickCount) {
 
441
                if (localTimeoutChecker.didTimeOut(m_exec)) {
 
442
                    m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
 
443
                    return StringifyFailed;
 
444
                }
 
445
                tickCount = localTimeoutChecker.ticksUntilNextCheck();
 
446
            }
 
447
        }
 
448
        m_holderCycleDetector.remove(m_holderStack.last().object());
 
449
        m_holderStack.removeLast();
 
450
    } while (!m_holderStack.isEmpty());
 
451
    return StringifySucceeded;
 
452
}
 
453
 
 
454
inline bool Stringifier::willIndent() const
 
455
{
 
456
    return !m_gap.isEmpty();
 
457
}
 
458
 
 
459
inline void Stringifier::indent()
 
460
{
 
461
    // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
 
462
    int newSize = m_indent.size() + m_gap.size();
 
463
    if (newSize > m_repeatedGap.size())
 
464
        m_repeatedGap.append(m_gap);
 
465
    ASSERT(newSize <= m_repeatedGap.size());
 
466
    m_indent = m_repeatedGap.substr(0, newSize);
 
467
}
 
468
 
 
469
inline void Stringifier::unindent()
 
470
{
 
471
    ASSERT(m_indent.size() >= m_gap.size());
 
472
    m_indent = m_repeatedGap.substr(0, m_indent.size() - m_gap.size());
 
473
}
 
474
 
 
475
inline void Stringifier::startNewLine(StringBuilder& builder) const
 
476
{
 
477
    if (m_gap.isEmpty())
 
478
        return;
 
479
    builder.append('\n');
 
480
    builder.append(m_indent);
 
481
}
 
482
 
 
483
inline Stringifier::Holder::Holder(JSObject* object)
 
484
    : m_object(object)
 
485
    , m_isArray(object->inherits(&JSArray::info))
 
486
    , m_index(0)
 
487
{
 
488
}
 
489
 
 
490
bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder)
 
491
{
 
492
    ASSERT(m_index <= m_size);
 
493
 
 
494
    ExecState* exec = stringifier.m_exec;
 
495
 
 
496
    // First time through, initialize.
 
497
    if (!m_index) {
 
498
        if (m_isArray) {
 
499
            m_isJSArray = isJSArray(&exec->globalData(), m_object);
 
500
            m_size = m_object->get(exec, exec->globalData().propertyNames->length).toUInt32(exec);
 
501
            builder.append('[');
 
502
        } else {
 
503
            if (stringifier.m_usingArrayReplacer)
 
504
                m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data();
 
505
            else {
 
506
                PropertyNameArray objectPropertyNames(exec);
 
507
                m_object->getPropertyNames(exec, objectPropertyNames);
 
508
                m_propertyNames = objectPropertyNames.releaseData();
 
509
            }
 
510
            m_size = m_propertyNames->propertyNameVector().size();
 
511
            builder.append('{');
 
512
        }
 
513
        stringifier.indent();
 
514
    }
 
515
 
 
516
    // Last time through, finish up and return false.
 
517
    if (m_index == m_size) {
 
518
        stringifier.unindent();
 
519
        if (m_size && builder[builder.size() - 1] != '{')
 
520
            stringifier.startNewLine(builder);
 
521
        builder.append(m_isArray ? ']' : '}');
 
522
        return false;
 
523
    }
 
524
 
 
525
    // Handle a single element of the array or object.
 
526
    unsigned index = m_index++;
 
527
    unsigned rollBackPoint = 0;
 
528
    StringifyResult stringifyResult;
 
529
    if (m_isArray) {
 
530
        // Get the value.
 
531
        JSValue value;
 
532
        if (m_isJSArray && asArray(m_object)->canGetIndex(index))
 
533
            value = asArray(m_object)->getIndex(index);
 
534
        else {
 
535
            PropertySlot slot(m_object);
 
536
            if (!m_object->getOwnPropertySlot(exec, index, slot))
 
537
                slot.setUndefined();
 
538
            if (exec->hadException())
 
539
                return false;
 
540
            value = slot.getValue(exec, index);
 
541
        }
 
542
 
 
543
        // Append the separator string.
 
544
        if (index)
 
545
            builder.append(',');
 
546
        stringifier.startNewLine(builder);
 
547
 
 
548
        // Append the stringified value.
 
549
        stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, index);
 
550
    } else {
 
551
        // Get the value.
 
552
        PropertySlot slot(m_object);
 
553
        Identifier& propertyName = m_propertyNames->propertyNameVector()[index];
 
554
        if (!m_object->getOwnPropertySlot(exec, propertyName, slot))
 
555
            return true;
 
556
        JSValue value = slot.getValue(exec, propertyName);
 
557
        if (exec->hadException())
 
558
            return false;
 
559
 
 
560
        rollBackPoint = builder.size();
 
561
 
 
562
        // Append the separator string.
 
563
        if (builder[rollBackPoint - 1] != '{')
 
564
            builder.append(',');
 
565
        stringifier.startNewLine(builder);
 
566
 
 
567
        // Append the property name.
 
568
        appendQuotedString(builder, propertyName.ustring());
 
569
        builder.append(':');
 
570
        if (stringifier.willIndent())
 
571
            builder.append(' ');
 
572
 
 
573
        // Append the stringified value.
 
574
        stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, propertyName);
 
575
    }
 
576
 
 
577
    // From this point on, no access to the this pointer or to any members, because the
 
578
    // Holder object may have moved if the call to stringify pushed a new Holder onto
 
579
    // m_holderStack.
 
580
 
 
581
    switch (stringifyResult) {
 
582
        case StringifyFailed:
 
583
            builder.append("null");
 
584
            break;
 
585
        case StringifySucceeded:
 
586
            break;
 
587
        case StringifyFailedDueToUndefinedValue:
 
588
            // This only occurs when get an undefined value for an object property.
 
589
            // In this case we don't want the separator and property name that we
 
590
            // already appended, so roll back.
 
591
            builder = builder.substr(0, rollBackPoint);
 
592
            break;
 
593
    }
 
594
 
 
595
    return true;
 
596
}
 
597
 
 
598
// ------------------------------ JSONObject --------------------------------
 
599
 
 
600
const ClassInfo JSONObject::info = { "JSON", 0, 0, ExecState::jsonTable };
 
601
 
 
602
/* Source for JSONObject.lut.h
 
603
@begin jsonTable
 
604
  parse         JSONProtoFuncParse             DontEnum|Function 1
 
605
  stringify     JSONProtoFuncStringify         DontEnum|Function 1
 
606
@end
 
607
*/
 
608
 
 
609
// ECMA 15.8
 
610
 
 
611
bool JSONObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
 
612
{
 
613
    return getStaticFunctionSlot<JSObject>(exec, ExecState::jsonTable(exec), this, propertyName, slot);
 
614
}
 
615
 
 
616
bool JSONObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
 
617
{
 
618
    return getStaticFunctionDescriptor<JSObject>(exec, ExecState::jsonTable(exec), this, propertyName, descriptor);
 
619
}
 
620
 
 
621
void JSONObject::markStringifiers(MarkStack& markStack, Stringifier* stringifier)
 
622
{
 
623
    stringifier->markAggregate(markStack);
 
624
}
 
625
 
 
626
class Walker {
 
627
public:
 
628
    Walker(ExecState* exec, JSObject* function, CallType callType, CallData callData)
 
629
        : m_exec(exec)
 
630
        , m_function(function)
 
631
        , m_callType(callType)
 
632
        , m_callData(callData)
 
633
    {
 
634
    }
 
635
    JSValue walk(JSValue unfiltered);
 
636
private:
 
637
    JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered)
 
638
    {
 
639
        JSValue args[] = { property, unfiltered };
 
640
        ArgList argList(args, 2);
 
641
        return call(m_exec, m_function, m_callType, m_callData, thisObj, argList);
 
642
    }
 
643
 
 
644
    friend class Holder;
 
645
 
 
646
    ExecState* m_exec;
 
647
    JSObject* m_function;
 
648
    CallType m_callType;
 
649
    CallData m_callData;
 
650
};
 
651
 
 
652
// We clamp recursion well beyond anything reasonable, but we also have a timeout check
 
653
// to guard against "infinite" execution by inserting arbitrarily large objects.
 
654
static const unsigned maximumFilterRecursion = 40000;
 
655
enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember, 
 
656
                                 ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember };
 
657
NEVER_INLINE JSValue Walker::walk(JSValue unfiltered)
 
658
{
 
659
    Vector<PropertyNameArray, 16> propertyStack;
 
660
    Vector<uint32_t, 16> indexStack;
 
661
    Vector<JSObject*, 16> objectStack;
 
662
    Vector<JSArray*, 16> arrayStack;
 
663
    
 
664
    Vector<WalkerState, 16> stateStack;
 
665
    WalkerState state = StateUnknown;
 
666
    JSValue inValue = unfiltered;
 
667
    JSValue outValue = jsNull();
 
668
    
 
669
    TimeoutChecker localTimeoutChecker;
 
670
    localTimeoutChecker.copyTimeoutValues(m_exec->globalData().timeoutChecker);
 
671
    localTimeoutChecker.reset();
 
672
    unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck();
 
673
    while (1) {
 
674
        switch (state) {
 
675
            arrayStartState:
 
676
            case ArrayStartState: {
 
677
                ASSERT(inValue.isObject());
 
678
                ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue)) || asObject(inValue)->inherits(&JSArray::info));
 
679
                if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) {
 
680
                    m_exec->setException(createStackOverflowError(m_exec));
 
681
                    return jsUndefined();
 
682
                }
 
683
 
 
684
                JSArray* array = asArray(inValue);
 
685
                arrayStack.append(array);
 
686
                indexStack.append(0);
 
687
                // fallthrough
 
688
            }
 
689
            arrayStartVisitMember:
 
690
            case ArrayStartVisitMember: {
 
691
                if (!--tickCount) {
 
692
                    if (localTimeoutChecker.didTimeOut(m_exec)) {
 
693
                        m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
 
694
                        return jsUndefined();
 
695
                    }
 
696
                    tickCount = localTimeoutChecker.ticksUntilNextCheck();
 
697
                }
 
698
 
 
699
                JSArray* array = arrayStack.last();
 
700
                uint32_t index = indexStack.last();
 
701
                if (index == array->length()) {
 
702
                    outValue = array;
 
703
                    arrayStack.removeLast();
 
704
                    indexStack.removeLast();
 
705
                    break;
 
706
                }
 
707
                if (isJSArray(&m_exec->globalData(), array) && array->canGetIndex(index))
 
708
                    inValue = array->getIndex(index);
 
709
                else {
 
710
                    PropertySlot slot;
 
711
                    if (array->getOwnPropertySlot(m_exec, index, slot))
 
712
                        inValue = slot.getValue(m_exec, index);
 
713
                    else
 
714
                        inValue = jsUndefined();
 
715
                }
 
716
                    
 
717
                if (inValue.isObject()) {
 
718
                    stateStack.append(ArrayEndVisitMember);
 
719
                    goto stateUnknown;
 
720
                } else
 
721
                    outValue = inValue;
 
722
                // fallthrough
 
723
            }
 
724
            case ArrayEndVisitMember: {
 
725
                JSArray* array = arrayStack.last();
 
726
                JSValue filteredValue = callReviver(array, jsString(m_exec, UString::from(indexStack.last())), outValue);
 
727
                if (filteredValue.isUndefined())
 
728
                    array->deleteProperty(m_exec, indexStack.last());
 
729
                else {
 
730
                    if (isJSArray(&m_exec->globalData(), array) && array->canSetIndex(indexStack.last()))
 
731
                        array->setIndex(indexStack.last(), filteredValue);
 
732
                    else
 
733
                        array->put(m_exec, indexStack.last(), filteredValue);
 
734
                }
 
735
                if (m_exec->hadException())
 
736
                    return jsNull();
 
737
                indexStack.last()++;
 
738
                goto arrayStartVisitMember;
 
739
            }
 
740
            objectStartState:
 
741
            case ObjectStartState: {
 
742
                ASSERT(inValue.isObject());
 
743
                ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue)) && !asObject(inValue)->inherits(&JSArray::info));
 
744
                if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) {
 
745
                    m_exec->setException(createStackOverflowError(m_exec));
 
746
                    return jsUndefined();
 
747
                }
 
748
 
 
749
                JSObject* object = asObject(inValue);
 
750
                objectStack.append(object);
 
751
                indexStack.append(0);
 
752
                propertyStack.append(PropertyNameArray(m_exec));
 
753
                object->getPropertyNames(m_exec, propertyStack.last());
 
754
                // fallthrough
 
755
            }
 
756
            objectStartVisitMember:
 
757
            case ObjectStartVisitMember: {
 
758
                if (!--tickCount) {
 
759
                    if (localTimeoutChecker.didTimeOut(m_exec)) {
 
760
                        m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
 
761
                        return jsUndefined();
 
762
                    }
 
763
                    tickCount = localTimeoutChecker.ticksUntilNextCheck();
 
764
                }
 
765
 
 
766
                JSObject* object = objectStack.last();
 
767
                uint32_t index = indexStack.last();
 
768
                PropertyNameArray& properties = propertyStack.last();
 
769
                if (index == properties.size()) {
 
770
                    outValue = object;
 
771
                    objectStack.removeLast();
 
772
                    indexStack.removeLast();
 
773
                    propertyStack.removeLast();
 
774
                    break;
 
775
                }
 
776
                PropertySlot slot;
 
777
                if (object->getOwnPropertySlot(m_exec, properties[index], slot))
 
778
                    inValue = slot.getValue(m_exec, properties[index]);
 
779
                else
 
780
                    inValue = jsUndefined();
 
781
 
 
782
                // The holder may be modified by the reviver function so any lookup may throw
 
783
                if (m_exec->hadException())
 
784
                    return jsNull();
 
785
 
 
786
                if (inValue.isObject()) {
 
787
                    stateStack.append(ObjectEndVisitMember);
 
788
                    goto stateUnknown;
 
789
                } else
 
790
                    outValue = inValue;
 
791
                // fallthrough
 
792
            }
 
793
            case ObjectEndVisitMember: {
 
794
                JSObject* object = objectStack.last();
 
795
                Identifier prop = propertyStack.last()[indexStack.last()];
 
796
                PutPropertySlot slot;
 
797
                JSValue filteredValue = callReviver(object, jsString(m_exec, prop.ustring()), outValue);
 
798
                if (filteredValue.isUndefined())
 
799
                    object->deleteProperty(m_exec, prop);
 
800
                else
 
801
                    object->put(m_exec, prop, filteredValue, slot);
 
802
                if (m_exec->hadException())
 
803
                    return jsNull();
 
804
                indexStack.last()++;
 
805
                goto objectStartVisitMember;
 
806
            }
 
807
            stateUnknown:
 
808
            case StateUnknown:
 
809
                if (!inValue.isObject()) {
 
810
                    outValue = inValue;
 
811
                    break;
 
812
                }
 
813
                JSObject* object = asObject(inValue);
 
814
                if (isJSArray(&m_exec->globalData(), object) || object->inherits(&JSArray::info))
 
815
                    goto arrayStartState;
 
816
                goto objectStartState;
 
817
        }
 
818
        if (stateStack.isEmpty())
 
819
            break;
 
820
 
 
821
        state = stateStack.last();
 
822
        stateStack.removeLast();
 
823
 
 
824
        if (!--tickCount) {
 
825
            if (localTimeoutChecker.didTimeOut(m_exec)) {
 
826
                m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
 
827
                return jsUndefined();
 
828
            }
 
829
            tickCount = localTimeoutChecker.ticksUntilNextCheck();
 
830
        }
 
831
    }
 
832
    JSObject* finalHolder = constructEmptyObject(m_exec);
 
833
    PutPropertySlot slot;
 
834
    finalHolder->put(m_exec, m_exec->globalData().propertyNames->emptyIdentifier, outValue, slot);
 
835
    return callReviver(finalHolder, jsEmptyString(m_exec), outValue);
 
836
}
 
837
 
 
838
// ECMA-262 v5 15.12.2
 
839
JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec, JSObject*, JSValue, const ArgList& args)
 
840
{
 
841
    if (args.isEmpty())
 
842
        return throwError(exec, GeneralError, "JSON.parse requires at least one parameter");
 
843
    JSValue value = args.at(0);
 
844
    UString source = value.toString(exec);
 
845
    if (exec->hadException())
 
846
        return jsNull();
 
847
    
 
848
    LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON);
 
849
    JSValue unfiltered = jsonParser.tryLiteralParse();
 
850
    if (!unfiltered)
 
851
        return throwError(exec, SyntaxError, "Unable to parse JSON string");
 
852
    
 
853
    if (args.size() < 2)
 
854
        return unfiltered;
 
855
    
 
856
    JSValue function = args.at(1);
 
857
    CallData callData;
 
858
    CallType callType = function.getCallData(callData);
 
859
    if (callType == CallTypeNone)
 
860
        return unfiltered;
 
861
    return Walker(exec, asObject(function), callType, callData).walk(unfiltered);
 
862
}
 
863
 
 
864
// ECMA-262 v5 15.12.3
 
865
JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec, JSObject*, JSValue, const ArgList& args)
 
866
{
 
867
    if (args.isEmpty())
 
868
        return throwError(exec, GeneralError, "No input to stringify");
 
869
    JSValue value = args.at(0);
 
870
    JSValue replacer = args.at(1);
 
871
    JSValue space = args.at(2);
 
872
    return Stringifier(exec, replacer, space).stringify(value);
 
873
}
 
874
 
 
875
} // namespace JSC