Commit c46cfaf6 authored by darin@apple.com's avatar darin@apple.com

JavaScriptCore:

2009-06-17  Darin Adler  <darin@apple.com>

        Reviewed by Oliver Hunt.

        Bug 26429: Make JSON.stringify non-recursive so it can handle objects
        of arbitrary complexity
        https://bugs.webkit.org/show_bug.cgi?id=26429

        For marking I decided not to use gcProtect, because this is inside the engine
        so it's easy enough to just do marking. And that darned gcProtect does locking!
        Oliver tried to convince me to used MarkedArgumentBuffer, but the constructor
        for that class says "FIXME: Remove all clients of this API, then remove this API."

        * runtime/Collector.cpp:
        (JSC::Heap::collect): Add a call to JSONObject::markStringifiers.

        * runtime/CommonIdentifiers.cpp:
        (JSC::CommonIdentifiers::CommonIdentifiers): Added emptyIdentifier.
        * runtime/CommonIdentifiers.h: Ditto.

        * runtime/JSGlobalData.cpp:
        (JSC::JSGlobalData::JSGlobalData): Initialize firstStringifierToMark to 0.
        * runtime/JSGlobalData.h: Added firstStringifierToMark.

        * runtime/JSONObject.cpp: Cut down the includes to the needed ones only.
        (JSC::unwrapNumberOrString): Added. Helper for unwrapping number and string
        objects to get their number and string values.
        (JSC::ReplacerPropertyName::ReplacerPropertyName): Added. The class is used
        to wrap an identifier or integer so we don't have to do any work unless we
        actually call a replacer.
        (JSC::ReplacerPropertyName::value): Added.
        (JSC::gap): Added. Helper function for the Stringifier constructor.
        (JSC::PropertyNameForFunctionCall::PropertyNameForFunctionCall): Added.
        The class is used to wrap an identifier or integer so we don't have to
        allocate a number or string until we actually call toJSON or a replacer.
        (JSC::PropertyNameForFunctionCall::asJSValue): Added.
        (JSC::Stringifier::Stringifier): Updated and moved out of the class
        definition. Added code to hook this into a singly linked list for marking.
        (JSC::Stringifier::~Stringifier): Remove from the singly linked list.
        (JSC::Stringifier::mark): Mark all the objects in the holder stacks.
        (JSC::Stringifier::stringify): Updated.
        (JSC::Stringifier::appendQuotedString): Tweaked and streamlined a bit.
        (JSC::Stringifier::toJSON): Renamed from toJSONValue.
        (JSC::Stringifier::appendStringifiedValue): Renamed from stringify.
        Added code to use the m_holderStack to do non-recursive stringify of
        objects and arrays. This code also uses the timeout checker since in
        pathological cases it could be slow even without calling into the
        JavaScript virtual machine.
        (JSC::Stringifier::willIndent): Added.
        (JSC::Stringifier::indent): Added.
        (JSC::Stringifier::unindent): Added.
        (JSC::Stringifier::startNewLine): Added.
        (JSC::Stringifier::Holder::Holder): Added.
        (JSC::Stringifier::Holder::appendNextProperty): Added. This is the
        function that handles the format of arrays and objects.
        (JSC::JSONObject::getOwnPropertySlot): Moved this down to the bottom
        of the file so the JSONObject class is not interleaved with the
        Stringifier class.
        (JSC::JSONObject::markStringifiers): Added. Calls mark.
        (JSC::JSONProtoFuncStringify): Streamlined the code here. The code
        to compute the gap string is now a separate function.

        * runtime/JSONObject.h: Made everything private. Added markStringifiers.

LayoutTests:

2009-06-17  Darin Adler  <darin@apple.com>

        Reviewed by Oliver Hunt.

        Bug 26429: Make JSON.stringify non-recursive so it can handle objects
        of arbitrary complexity
        https://bugs.webkit.org/show_bug.cgi?id=26429

        * fast/js/JSON-stringify-expected.txt: Updated.
        * fast/js/resources/JSON-stringify.js: Changed the infinite object and
        infinite array tests to instead just test something a fixed number of
        levels deep. Otherwise we end up with an infinite loop in the test,
        which would lead to the slow-script dialog in the production web browser.
        Also raised the number from 512 to 2048 since there's no fixed limit any more.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@44813 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 2928517a
2009-06-17 Darin Adler <darin@apple.com>
Reviewed by Oliver Hunt.
Bug 26429: Make JSON.stringify non-recursive so it can handle objects
of arbitrary complexity
https://bugs.webkit.org/show_bug.cgi?id=26429
For marking I decided not to use gcProtect, because this is inside the engine
so it's easy enough to just do marking. And that darned gcProtect does locking!
Oliver tried to convince me to used MarkedArgumentBuffer, but the constructor
for that class says "FIXME: Remove all clients of this API, then remove this API."
* runtime/Collector.cpp:
(JSC::Heap::collect): Add a call to JSONObject::markStringifiers.
* runtime/CommonIdentifiers.cpp:
(JSC::CommonIdentifiers::CommonIdentifiers): Added emptyIdentifier.
* runtime/CommonIdentifiers.h: Ditto.
* runtime/JSGlobalData.cpp:
(JSC::JSGlobalData::JSGlobalData): Initialize firstStringifierToMark to 0.
* runtime/JSGlobalData.h: Added firstStringifierToMark.
* runtime/JSONObject.cpp: Cut down the includes to the needed ones only.
(JSC::unwrapNumberOrString): Added. Helper for unwrapping number and string
objects to get their number and string values.
(JSC::ReplacerPropertyName::ReplacerPropertyName): Added. The class is used
to wrap an identifier or integer so we don't have to do any work unless we
actually call a replacer.
(JSC::ReplacerPropertyName::value): Added.
(JSC::gap): Added. Helper function for the Stringifier constructor.
(JSC::PropertyNameForFunctionCall::PropertyNameForFunctionCall): Added.
The class is used to wrap an identifier or integer so we don't have to
allocate a number or string until we actually call toJSON or a replacer.
(JSC::PropertyNameForFunctionCall::asJSValue): Added.
(JSC::Stringifier::Stringifier): Updated and moved out of the class
definition. Added code to hook this into a singly linked list for marking.
(JSC::Stringifier::~Stringifier): Remove from the singly linked list.
(JSC::Stringifier::mark): Mark all the objects in the holder stacks.
(JSC::Stringifier::stringify): Updated.
(JSC::Stringifier::appendQuotedString): Tweaked and streamlined a bit.
(JSC::Stringifier::toJSON): Renamed from toJSONValue.
(JSC::Stringifier::appendStringifiedValue): Renamed from stringify.
Added code to use the m_holderStack to do non-recursive stringify of
objects and arrays. This code also uses the timeout checker since in
pathological cases it could be slow even without calling into the
JavaScript virtual machine.
(JSC::Stringifier::willIndent): Added.
(JSC::Stringifier::indent): Added.
(JSC::Stringifier::unindent): Added.
(JSC::Stringifier::startNewLine): Added.
(JSC::Stringifier::Holder::Holder): Added.
(JSC::Stringifier::Holder::appendNextProperty): Added. This is the
function that handles the format of arrays and objects.
(JSC::JSONObject::getOwnPropertySlot): Moved this down to the bottom
of the file so the JSONObject class is not interleaved with the
Stringifier class.
(JSC::JSONObject::markStringifiers): Added. Calls mark.
(JSC::JSONProtoFuncStringify): Streamlined the code here. The code
to compute the gap string is now a separate function.
* runtime/JSONObject.h: Made everything private. Added markStringifiers.
2009-06-17 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
......
......@@ -27,6 +27,7 @@
#include "Interpreter.h"
#include "JSGlobalObject.h"
#include "JSLock.h"
#include "JSONObject.h"
#include "JSString.h"
#include "JSValue.h"
#include "Nodes.h"
......@@ -991,6 +992,8 @@ bool Heap::collect()
m_globalData->smallStrings.mark();
if (m_globalData->scopeNodeBeingReparsed)
m_globalData->scopeNodeBeingReparsed->mark();
if (m_globalData->firstStringifierToMark)
JSONObject::markStringifiers(m_globalData->firstStringifierToMark);
JAVASCRIPTCORE_GC_MARKED();
......
/*
* Copyright (C) 2003, 2007 Apple Inc. All rights reserved.
* Copyright (C) 2003, 2007, 2009 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -23,12 +23,13 @@
namespace JSC {
const char* const nullCString = 0;
static const char* const nullCString = 0;
#define INITIALIZE_PROPERTY_NAME(name) , name(globalData, #name)
CommonIdentifiers::CommonIdentifiers(JSGlobalData* globalData)
: nullIdentifier(globalData, nullCString)
, emptyIdentifier(globalData, "")
, underscoreProto(globalData, "__proto__")
, thisIdentifier(globalData, "this")
JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PROPERTY_NAME)
......
/*
* Copyright (C) 2003,2007 Apple Computer, Inc
* Copyright (C) 2003, 2007, 2009 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -76,6 +76,7 @@ namespace JSC {
public:
const Identifier nullIdentifier;
const Identifier emptyIdentifier;
const Identifier underscoreProto;
const Identifier thisIdentifier;
......
......@@ -139,6 +139,7 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet)
, head(0)
, dynamicGlobalObject(0)
, scopeNodeBeingReparsed(0)
, firstStringifierToMark(0)
{
#if PLATFORM(MAC)
startProfilerServerIfNeeded();
......
......@@ -54,8 +54,10 @@ namespace JSC {
class Lexer;
class Parser;
class ScopeNode;
class Stringifier;
class Structure;
class UString;
struct HashTable;
struct VPtrSet;
......@@ -146,6 +148,7 @@ namespace JSC {
HashSet<JSObject*> arrayVisitedElements;
ScopeNode* scopeNodeBeingReparsed;
Stringifier* firstStringifierToMark;
private:
JSGlobalData(bool isShared, const VPtrSet&);
......
This diff is collapsed.
......@@ -30,22 +30,27 @@
namespace JSC {
class Stringifier;
class JSONObject : public JSObject {
public:
JSONObject(PassRefPtr<Structure> structure)
:JSObject(structure)
: JSObject(structure)
{
}
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
virtual const ClassInfo* classInfo() const { return &info; }
static const ClassInfo info;
static PassRefPtr<Structure> createStructure(JSValue prototype)
{
return Structure::create(prototype, TypeInfo(ObjectType));
}
static void markStringifiers(Stringifier*);
private:
virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&);
virtual const ClassInfo* classInfo() const { return &info; }
static const ClassInfo info;
};
} // namespace JSC
......
2009-06-17 Darin Adler <darin@apple.com>
Reviewed by Oliver Hunt.
Bug 26429: Make JSON.stringify non-recursive so it can handle objects
of arbitrary complexity
https://bugs.webkit.org/show_bug.cgi?id=26429
* fast/js/JSON-stringify-expected.txt: Updated.
* fast/js/resources/JSON-stringify.js: Changed the infinite object and
infinite array tests to instead just test something a fixed number of
levels deep. Otherwise we end up with an infinite loop in the test,
which would lead to the slow-script dialog in the production web browser.
Also raised the number from 512 to 2048 since there's no fixed limit any more.
2009-06-17 Erik Arvidsson <arv@chromium.org>
Reviewed by Adele Peterson.
......
......@@ -257,36 +257,41 @@ function (jsonObject) {
PASS tests[i](nativeJSON) is tests[i](JSON)
function (jsonObject) {
var deepObject = {};
for (var i = 0; i < 512; i++)
for (var i = 0; i < 2048; i++)
deepObject = {next:deepObject};
return jsonObject.stringify(deepObject);
}
PASS tests[i](nativeJSON) is tests[i](JSON)
function (jsonObject) {
var deepArray = [];
for (var i = 0; i < 512; i++)
for (var i = 0; i < 2048; i++)
deepArray = [deepArray];
return jsonObject.stringify(deepArray);
}
PASS tests[i](nativeJSON) is tests[i](JSON)
function (jsonObject) {
function toInfiniteJSONObject() {
var depth = 0;
function toDeepVirtualJSONObject() {
if (++depth >= 2048)
return {};
var r = {};
r.toJSON = toInfiniteJSONObject;
r.toJSON = toDeepVirtualJSONObject;
return {recurse: r};
}
return jsonObject.stringify(toInfiniteJSONObject());
return jsonObject.stringify(toDeepVirtualJSONObject());
}
PASS tests[i](nativeJSON) threw exception RangeError: Maximum call stack size exceeded..
PASS tests[i](nativeJSON) is tests[i](JSON)
function (jsonObject) {
function toInfiniteJSONArray() {
var depth = 0;
function toDeepVirtualJSONArray() {
if (++depth >= 2048)
return [];
var r = [];
r.toJSON = toInfiniteJSONArray;
r.toJSON = toDeepJSONArray;
return [r];
}
return jsonObject.stringify(toInfiniteJSONArray());
return jsonObject.stringify(toDeepVirtualJSONArray());
}
PASS tests[i](nativeJSON) threw exception RangeError: Maximum call stack size exceeded..
function (jsonObject) {
return jsonObject.stringify(fullCharsetString);
}
......
......@@ -240,34 +240,38 @@ function createTests() {
});
result.push(function(jsonObject){
var deepObject = {};
for (var i = 0; i < 512; i++)
for (var i = 0; i < 2048; i++)
deepObject = {next:deepObject};
return jsonObject.stringify(deepObject);
});
result.push(function(jsonObject){
var deepArray = [];
for (var i = 0; i < 512; i++)
for (var i = 0; i < 2048; i++)
deepArray = [deepArray];
return jsonObject.stringify(deepArray);
});
result.push(function(jsonObject){
function toInfiniteJSONObject() {
var depth = 0;
function toDeepVirtualJSONObject() {
if (++depth >= 2048)
return {};
var r = {};
r.toJSON = toInfiniteJSONObject;
r.toJSON = toDeepVirtualJSONObject;
return {recurse: r};
}
return jsonObject.stringify(toInfiniteJSONObject());
return jsonObject.stringify(toDeepVirtualJSONObject());
});
result[result.length - 1].throws = true;
result.push(function(jsonObject){
function toInfiniteJSONArray() {
var depth = 0;
function toDeepVirtualJSONArray() {
if (++depth >= 2048)
return [];
var r = [];
r.toJSON = toInfiniteJSONArray;
r.toJSON = toDeepJSONArray;
return [r];
}
return jsonObject.stringify(toInfiniteJSONArray());
return jsonObject.stringify(toDeepVirtualJSONArray());
});
result[result.length - 1].throws = true;
var fullCharsetString = "";
for (var i = 0; i < 65536; i++)
fullCharsetString += String.fromCharCode(i);
......
......@@ -70,9 +70,9 @@ static CFArrayCallBacks NonRetainingArrayCallbacks = {
CFRange arrayRange = CFRangeMake(0, CFArrayGetCount(openWindowsRef));
CFIndex i = CFArrayGetFirstIndexOfValue(openWindowsRef, arrayRange, self);
assert(i != -1);
CFArrayRemoveValueAtIndex(openWindowsRef, i);
if (i != kCFNotFound)
CFArrayRemoveValueAtIndex(openWindowsRef, i);
[super close];
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment