Changeset 44968 in webkit


Ignore:
Timestamp:
Jun 22, 2009 6:27:54 PM (15 years ago)
Author:
oliver@apple.com
Message:

Bug 26591: Support revivers in JSON.parse
<https://bugs.webkit.org/show_bug.cgi?id=26591>

Reviewed by Darin Adler.

Add reviver support to JSON.parse. This completes the JSON object.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/ChangeLog

    r44931 r44968  
     12009-06-22  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Darin Adler.
     4
     5        Bug 26591: Support revivers in JSON.parse
     6        <https://bugs.webkit.org/show_bug.cgi?id=26591>
     7
     8        Add reviver support to JSON.parse.  This completes the JSON object.
     9
     10        * runtime/JSONObject.cpp:
     11        (JSC::Walker::Walker):
     12        (JSC::Walker::callReviver):
     13        (JSC::Walker::walk):
     14        (JSC::JSONProtoFuncParse):
     15
    1162009-06-21  Oliver Hunt  <oliver@apple.com>
    217
  • trunk/JavaScriptCore/runtime/JSONObject.cpp

    r44923 r44968  
    588588}
    589589
    590 // ECMA-262 v5 15.12.3
     590class Walker {
     591public:
     592    Walker(ExecState* exec, JSObject* function, CallType callType, CallData callData)
     593        : m_exec(exec)
     594        , m_function(function)
     595        , m_callType(callType)
     596        , m_callData(callData)
     597    {
     598    }
     599    JSValue walk(JSValue unfiltered);
     600private:
     601    JSValue callReviver(JSValue property, JSValue unfiltered)
     602    {
     603        JSValue args[] = { property, unfiltered };
     604        ArgList argList(args, 2);
     605        return call(m_exec, m_function, m_callType, m_callData, jsNull(), argList);
     606    }
     607
     608    friend class Holder;
     609
     610    ExecState* m_exec;
     611    JSObject* m_function;
     612    CallType m_callType;
     613    CallData m_callData;
     614};
     615   
     616enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember,
     617                                 ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember };
     618NEVER_INLINE JSValue Walker::walk(JSValue unfiltered)
     619{
     620    Vector<PropertyNameArray, 16> propertyStack;
     621    Vector<uint32_t, 16> indexStack;
     622    Vector<JSObject*, 16> objectStack;
     623    Vector<JSArray*, 16> arrayStack;
     624   
     625    Vector<WalkerState, 16> stateStack;
     626    WalkerState state = StateUnknown;
     627    JSValue inValue = unfiltered;
     628    JSValue outValue = jsNull();
     629    while (1) {
     630        switch (state) {
     631            arrayStartState:
     632            case ArrayStartState: {
     633                ASSERT(inValue.isObject());
     634                ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue)));
     635                JSArray* array = asArray(inValue);
     636                arrayStack.append(array);
     637                indexStack.append(0);
     638                // fallthrough
     639            }
     640            arrayStartVisitMember:
     641            case ArrayStartVisitMember: {
     642                JSArray* array = arrayStack.last();
     643                uint32_t index = indexStack.last();
     644                if (index == array->length()) {
     645                    outValue = array;
     646                    arrayStack.removeLast();
     647                    indexStack.removeLast();
     648                    break;
     649                }
     650                inValue = array->getIndex(index);
     651                if (inValue.isObject()) {
     652                    stateStack.append(ArrayEndVisitMember);
     653                    goto stateUnknown;
     654                } else
     655                    outValue = inValue;
     656                // fallthrough
     657            }
     658            case ArrayEndVisitMember: {
     659                JSArray* array = arrayStack.last();
     660                array->setIndex(indexStack.last(), callReviver(jsString(m_exec, UString::from(indexStack.last())), outValue));
     661                if (m_exec->hadException())
     662                    return jsNull();
     663                indexStack.last()++;
     664                goto arrayStartVisitMember;
     665            }
     666            objectStartState:
     667            case ObjectStartState: {
     668                ASSERT(inValue.isObject());
     669                ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue)));
     670                JSObject* object = asObject(inValue);
     671                objectStack.append(object);
     672                indexStack.append(0);
     673                propertyStack.append(PropertyNameArray(m_exec));
     674                object->getPropertyNames(m_exec, propertyStack.last());
     675                // fallthrough
     676            }
     677            objectStartVisitMember:
     678            case ObjectStartVisitMember: {
     679                JSObject* object = objectStack.last();
     680                uint32_t index = indexStack.last();
     681                PropertyNameArray& properties = propertyStack.last();
     682                if (index == properties.size()) {
     683                    outValue = object;
     684                    objectStack.removeLast();
     685                    indexStack.removeLast();
     686                    propertyStack.removeLast();
     687                    break;
     688                }
     689                PropertySlot slot;
     690                object->getOwnPropertySlot(m_exec, properties[index], slot);
     691                inValue = slot.getValue(m_exec, properties[index]);
     692                ASSERT(!m_exec->hadException());
     693                if (inValue.isObject()) {
     694                    stateStack.append(ObjectEndVisitMember);
     695                    goto stateUnknown;
     696                } else
     697                    outValue = inValue;
     698                // fallthrough
     699            }
     700            case ObjectEndVisitMember: {
     701                JSObject* object = objectStack.last();
     702                Identifier prop = propertyStack.last()[indexStack.last()];
     703                PutPropertySlot slot;
     704                object->put(m_exec, prop, callReviver(jsString(m_exec, prop.ustring()), outValue), slot);
     705                if (m_exec->hadException())
     706                    return jsNull();
     707                indexStack.last()++;
     708                goto objectStartVisitMember;
     709            }
     710            stateUnknown:
     711            case StateUnknown:
     712                if (!inValue.isObject()) {
     713                    outValue = inValue;
     714                    break;
     715                }
     716                if (isJSArray(&m_exec->globalData(), asObject(inValue)))
     717                    goto arrayStartState;
     718                goto objectStartState;
     719        }
     720        if (stateStack.isEmpty())
     721            break;
     722        state = stateStack.last();
     723        stateStack.removeLast();
     724    }
     725    return callReviver(jsEmptyString(m_exec), outValue);
     726}
     727
     728// ECMA-262 v5 15.12.2
    591729JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec, JSObject*, JSValue, const ArgList& args)
    592730{
     
    599737   
    600738    LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON);
    601     JSValue parsedObject = jsonParser.tryLiteralParse();
    602     if (!parsedObject)
     739    JSValue unfiltered = jsonParser.tryLiteralParse();
     740    if (!unfiltered)
    603741        return throwError(exec, SyntaxError, "Unable to parse JSON string");
    604742   
    605     return parsedObject;
     743    if (args.size() < 2)
     744        return unfiltered;
     745   
     746    JSValue function = args.at(1);
     747    CallData callData;
     748    CallType callType = function.getCallData(callData);
     749    if (callType == CallTypeNone)
     750        return unfiltered;
     751    return Walker(exec, asObject(function), callType, callData).walk(unfiltered);
    606752}
    607753
  • trunk/LayoutTests/ChangeLog

    r44961 r44968  
     12009-06-22  Oliver Hunt  <oliver@apple.com>
     2
     3        Reviewed by Darin Adler.
     4
     5        Bug 26591: Support revivers in JSON.parse
     6        <https://bugs.webkit.org/show_bug.cgi?id=26591>
     7
     8        Test cases for JSON.parse with a reviver function.
     9
     10        * fast/js/JSON-parse-expected.txt:
     11        * fast/js/resources/JSON-parse.js:
     12        (createTests.log):
     13        (createTests.result):
     14        (createTests.logOrder):
     15        (createTests.var):
     16        (createTests.throwAfterFifthCall):
     17        (createTests):
     18
    1192009-06-22  Simon Fraser  <simon.fraser@apple.com>
    220
  • trunk/LayoutTests/fast/js/JSON-parse-expected.txt

    r44923 r44968  
    386386        return jsonObject.parse(JSON.stringify(complexObject,null,"\n"));
    387387    }
    388 PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
    389 function (jsonObject) {
    390         return jsonObject.parse(JSON.stringify(complexObject,null,"\n"));
    391     }
    392 PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
     388PASS JSON.stringify(tests[i](nativeJSON)) is tests[i].expected
     389function (jsonObject) {
     390        return jsonObject.parse("true", log);
     391    }
     392PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     393function (jsonObject) {
     394        return jsonObject.parse("false", log);
     395    }
     396PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     397function (jsonObject) {
     398        return jsonObject.parse("null", log);
     399    }
     400PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     401function (jsonObject) {
     402        return jsonObject.parse("1", log);
     403    }
     404PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     405function (jsonObject) {
     406        return jsonObject.parse("1.5", log);
     407    }
     408PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     409function (jsonObject) {
     410        return jsonObject.parse('"a string"', log);
     411    }
     412PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     413function (jsonObject) {
     414        return jsonObject.parse(JSON.stringify(simpleArray), log);
     415    }
     416PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     417function (jsonObject) {
     418        return jsonObject.parse(JSON.stringify(complexArray), log);
     419    }
     420PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     421function (jsonObject) {
     422        return jsonObject.parse(JSON.stringify(simpleObject), log);
     423    }
     424PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     425function (jsonObject) {
     426        return jsonObject.parse(JSON.stringify(complexObject), log);
     427    }
     428PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     429function (jsonObject) {
     430        logOrderString = "";
     431        return jsonObject.parse("true", logOrder);
     432    }
     433PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     434function (jsonObject) {
     435        logOrderString = "";
     436        return jsonObject.parse("false", logOrder);
     437    }
     438PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     439function (jsonObject) {
     440        logOrderString = "";
     441        return jsonObject.parse("null", logOrder);
     442    }
     443PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     444function (jsonObject) {
     445        logOrderString = "";
     446        return jsonObject.parse("1", logOrder);
     447    }
     448PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     449function (jsonObject) {
     450        logOrderString = "";
     451        return jsonObject.parse("1.5", logOrder);
     452    }
     453PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     454function (jsonObject) {
     455        logOrderString = "";
     456        return jsonObject.parse('"a string"', logOrder);
     457    }
     458PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     459function (jsonObject) {
     460        logOrderString = "";
     461        return jsonObject.parse(JSON.stringify(simpleArray), logOrder);
     462    }
     463PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     464function (jsonObject) {
     465        logOrderString = "";
     466        return jsonObject.parse(JSON.stringify(complexArray), logOrder);
     467    }
     468PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     469function (jsonObject) {
     470        logOrderString = "";
     471        return jsonObject.parse(JSON.stringify(simpleObject), logOrder);
     472    }
     473PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     474function (jsonObject) {
     475        logOrderString = "";
     476        return jsonObject.parse(JSON.stringify(complexObject), logOrder);
     477    }
     478PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     479function (jsonObject) {
     480        logOrderString = "";
     481        jsonObject.parse("true", logOrder);
     482        return logOrderString;
     483    }
     484PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     485function (jsonObject) {
     486        logOrderString = "";
     487        jsonObject.parse("false", logOrder);
     488        return logOrderString;
     489    }
     490PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     491function (jsonObject) {
     492        logOrderString = "";
     493        jsonObject.parse("null", logOrder);
     494        return logOrderString;
     495    }
     496PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     497function (jsonObject) {
     498        logOrderString = "";
     499        jsonObject.parse("1", logOrder);
     500        return logOrderString;
     501    }
     502PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     503function (jsonObject) {
     504        logOrderString = "";
     505        jsonObject.parse("1.5", logOrder);
     506        return logOrderString;
     507    }
     508PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     509function (jsonObject) {
     510        logOrderString = "";
     511        jsonObject.parse('"a string"', logOrder);
     512        return logOrderString;
     513    }
     514PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     515function (jsonObject) {
     516        logOrderString = "";
     517        jsonObject.parse(JSON.stringify(simpleArray), logOrder);
     518        return logOrderString;
     519    }
     520PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     521function (jsonObject) {
     522        logOrderString = "";
     523        jsonObject.parse(JSON.stringify(complexArray), logOrder);
     524        return logOrderString;
     525    }
     526PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     527function (jsonObject) {
     528        logOrderString = "";
     529        jsonObject.parse(JSON.stringify(simpleObject), logOrder);
     530        return logOrderString;
     531    }
     532PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     533function (jsonObject) {
     534        logOrderString = "";
     535        jsonObject.parse(JSON.stringify(complexObject), logOrder);
     536        return logOrderString;
     537    }
     538PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     539function (jsonObject) {
     540        callCount = 0;
     541        logOrderString = "";
     542        return jsonObject.parse(JSON.stringify(complexArray), throwAfterFifthCall);
     543    }
     544PASS tests[i](nativeJSON) threw exception from reviver.
     545function (jsonObject) {
     546        callCount = 0;
     547        logOrderString = "";
     548        return jsonObject.parse(JSON.stringify(simpleObject), throwAfterFifthCall);
     549    }
     550PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     551function (jsonObject) {
     552        callCount = 0;
     553        logOrderString = "";
     554        return jsonObject.parse(JSON.stringify(complexObject), throwAfterFifthCall);
     555    }
     556PASS tests[i](nativeJSON) threw exception from reviver.
     557function (jsonObject) {
     558        callCount = 0;
     559        logOrderString = "";
     560        try { jsonObject.parse(JSON.stringify(complexArray), throwAfterFifthCall); } catch (e) {}
     561        return logOrderString;
     562    }
     563PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     564function (jsonObject) {
     565        callCount = 0;
     566        logOrderString = "";
     567        try { jsonObject.parse(JSON.stringify(simpleObject), throwAfterFifthCall); } catch (e) {}
     568        return logOrderString;
     569    }
     570PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
     571function (jsonObject) {
     572        callCount = 0;
     573        logOrderString = "";
     574        try { jsonObject.parse(JSON.stringify(complexObject), throwAfterFifthCall); } catch (e) {}
     575        return logOrderString;
     576    }
     577PASS JSON.stringify(tests[i](nativeJSON)) is JSON.stringify(tests[i](JSON))
    393578PASS successfullyParsed is true
    394579
  • trunk/LayoutTests/fast/js/resources/JSON-parse.js

    r44923 r44968  
    351351        return jsonObject.parse(JSON.stringify(complexObject,null,"\n"));
    352352    });
    353     result.push(function(jsonObject){
    354         return jsonObject.parse(JSON.stringify(complexObject,null,"\n"));
    355     });
    356353    result[result.length - 1].expected = JSON.stringify(complexObject);
    357    
     354    function log(key, value) {
     355        var o = {};
     356        o[key] = value;
     357        o.keyType = typeof key;
     358        return o;
     359    }
     360    result.push(function(jsonObject){
     361        return jsonObject.parse("true", log);
     362    });
     363    result.push(function(jsonObject){
     364        return jsonObject.parse("false", log);
     365    });
     366    result.push(function(jsonObject){
     367        return jsonObject.parse("null", log);
     368    });
     369    result.push(function(jsonObject){
     370        return jsonObject.parse("1", log);
     371    });
     372    result.push(function(jsonObject){
     373        return jsonObject.parse("1.5", log);
     374    });
     375    result.push(function(jsonObject){
     376        return jsonObject.parse('"a string"', log);
     377    });
     378    result.push(function(jsonObject){
     379        return jsonObject.parse(JSON.stringify(simpleArray), log);
     380    });
     381    result.push(function(jsonObject){
     382        return jsonObject.parse(JSON.stringify(complexArray), log);
     383    });
     384    result.push(function(jsonObject){
     385        return jsonObject.parse(JSON.stringify(simpleObject), log);
     386    });
     387    result.push(function(jsonObject){
     388        return jsonObject.parse(JSON.stringify(complexObject), log);
     389    });
     390    var logOrderString;
     391    function logOrder(key, value) {
     392        logOrderString += key +":"+JSON.stringify(value);
     393        return null;
     394    }
     395    result.push(function(jsonObject){
     396        logOrderString = "";
     397        return jsonObject.parse("true", logOrder);
     398    });
     399    result.push(function(jsonObject){
     400        logOrderString = "";
     401        return jsonObject.parse("false", logOrder);
     402    });
     403    result.push(function(jsonObject){
     404        logOrderString = "";
     405        return jsonObject.parse("null", logOrder);
     406    });
     407    result.push(function(jsonObject){
     408        logOrderString = "";
     409        return jsonObject.parse("1", logOrder);
     410    });
     411    result.push(function(jsonObject){
     412        logOrderString = "";
     413        return jsonObject.parse("1.5", logOrder);
     414    });
     415    result.push(function(jsonObject){
     416        logOrderString = "";
     417        return jsonObject.parse('"a string"', logOrder);
     418    });
     419    result.push(function(jsonObject){
     420        logOrderString = "";
     421        return jsonObject.parse(JSON.stringify(simpleArray), logOrder);
     422    });
     423    result.push(function(jsonObject){
     424        logOrderString = "";
     425        return jsonObject.parse(JSON.stringify(complexArray), logOrder);
     426    });
     427    result.push(function(jsonObject){
     428        logOrderString = "";
     429        return jsonObject.parse(JSON.stringify(simpleObject), logOrder);
     430    });
     431    result.push(function(jsonObject){
     432        logOrderString = "";
     433        return jsonObject.parse(JSON.stringify(complexObject), logOrder);
     434    });
     435    result.push(function(jsonObject){
     436        logOrderString = "";
     437        jsonObject.parse("true", logOrder);
     438        return logOrderString;
     439    });
     440    result.push(function(jsonObject){
     441        logOrderString = "";
     442        jsonObject.parse("false", logOrder);
     443        return logOrderString;
     444    });
     445    result.push(function(jsonObject){
     446        logOrderString = "";
     447        jsonObject.parse("null", logOrder);
     448        return logOrderString;
     449    });
     450    result.push(function(jsonObject){
     451        logOrderString = "";
     452        jsonObject.parse("1", logOrder);
     453        return logOrderString;
     454    });
     455    result.push(function(jsonObject){
     456        logOrderString = "";
     457        jsonObject.parse("1.5", logOrder);
     458        return logOrderString;
     459    });
     460    result.push(function(jsonObject){
     461        logOrderString = "";
     462        jsonObject.parse('"a string"', logOrder);
     463        return logOrderString;
     464    });
     465    result.push(function(jsonObject){
     466        logOrderString = "";
     467        jsonObject.parse(JSON.stringify(simpleArray), logOrder);
     468        return logOrderString;
     469    });
     470    result.push(function(jsonObject){
     471        logOrderString = "";
     472        jsonObject.parse(JSON.stringify(complexArray), logOrder);
     473        return logOrderString;
     474    });
     475    result.push(function(jsonObject){
     476        logOrderString = "";
     477        jsonObject.parse(JSON.stringify(simpleObject), logOrder);
     478        return logOrderString;
     479    });
     480    result.push(function(jsonObject){
     481        logOrderString = "";
     482        jsonObject.parse(JSON.stringify(complexObject), logOrder);
     483        return logOrderString;
     484    });
     485    var callCount = 0;
     486    function throwAfterFifthCall(key, value) {
     487        logOrder(key, value);
     488        if (++callCount > 5)
     489            throw "from reviver";
     490        return null;
     491    }
     492    result.push(function(jsonObject){
     493        callCount = 0;
     494        logOrderString = "";
     495        return jsonObject.parse(JSON.stringify(complexArray), throwAfterFifthCall);
     496    });
     497    result[result.length - 1].throws = true;
     498    result.push(function(jsonObject){
     499        callCount = 0;
     500        logOrderString = "";
     501        return jsonObject.parse(JSON.stringify(simpleObject), throwAfterFifthCall);
     502    });
     503    result.push(function(jsonObject){
     504        callCount = 0;
     505        logOrderString = "";
     506        return jsonObject.parse(JSON.stringify(complexObject), throwAfterFifthCall);
     507    });
     508    result[result.length - 1].throws = true;
     509    result.push(function(jsonObject){
     510        callCount = 0;
     511        logOrderString = "";
     512        try { jsonObject.parse(JSON.stringify(complexArray), throwAfterFifthCall); } catch (e) {}
     513        return logOrderString;
     514    });
     515    result.push(function(jsonObject){
     516        callCount = 0;
     517        logOrderString = "";
     518        try { jsonObject.parse(JSON.stringify(simpleObject), throwAfterFifthCall); } catch (e) {}
     519        return logOrderString;
     520    });
     521    result.push(function(jsonObject){
     522        callCount = 0;
     523        logOrderString = "";
     524        try { jsonObject.parse(JSON.stringify(complexObject), throwAfterFifthCall); } catch (e) {}
     525        return logOrderString;
     526    });
     527
    358528    return result;
    359529}
Note: See TracChangeset for help on using the changeset viewer.