Diff
Modified: trunk/JSTests/ChangeLog (250859 => 250860)
--- trunk/JSTests/ChangeLog 2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/JSTests/ChangeLog 2019-10-08 21:23:14 UTC (rev 250860)
@@ -1,3 +1,18 @@
+2019-10-08 Alexey Shvayka <shvaikal...@gmail.com>
+
+ JSON.parse incorrectly handles array proxies
+ https://bugs.webkit.org/show_bug.cgi?id=199292
+
+ Reviewed by Saam Barati.
+
+ * microbenchmarks/json-parse-array-reviver-same-value.js: Added.
+ * microbenchmarks/json-parse-array-reviver.js: Added.
+ * microbenchmarks/json-parse-object-reviver-same-value.js: Added.
+ * microbenchmarks/json-parse-object-reviver.js: Added.
+ * stress/json-parse-reviver-array-proxy.js: Added.
+ * stress/json-parse-reviver-revoked-proxy.js: Added.
+ * test262/expectations.yaml: Mark 6 test cases as passing.
+
2019-10-08 Ross Kirsling <ross.kirsl...@sony.com>
Update test262 (2019.10.08).
Added: trunk/JSTests/microbenchmarks/json-parse-array-reviver-same-value.js (0 => 250860)
--- trunk/JSTests/microbenchmarks/json-parse-array-reviver-same-value.js (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-array-reviver-same-value.js 2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify([0,1,2,3,4,5,6,7,8,9]);
+for (let i = 0; i < 1e5; ++i)
+ JSON.parse(json, (_key, val) => val);
Added: trunk/JSTests/microbenchmarks/json-parse-array-reviver.js (0 => 250860)
--- trunk/JSTests/microbenchmarks/json-parse-array-reviver.js (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-array-reviver.js 2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify([0,1,2,3,4,5,6,7,8,9]);
+for (let i = 0; i < 1e5; ++i)
+ JSON.parse(json, (_key, val) => val + 1);
Added: trunk/JSTests/microbenchmarks/json-parse-object-reviver-same-value.js (0 => 250860)
--- trunk/JSTests/microbenchmarks/json-parse-object-reviver-same-value.js (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-object-reviver-same-value.js 2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify({a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9});
+for (let i = 0; i < 1e5; ++i)
+ JSON.parse(json, (_key, val) => val);
Added: trunk/JSTests/microbenchmarks/json-parse-object-reviver.js (0 => 250860)
--- trunk/JSTests/microbenchmarks/json-parse-object-reviver.js (rev 0)
+++ trunk/JSTests/microbenchmarks/json-parse-object-reviver.js 2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,3 @@
+const json = JSON.stringify({a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9});
+for (let i = 0; i < 1e5; ++i)
+ JSON.parse(json, (_key, val) => val + 1);
Added: trunk/JSTests/stress/json-parse-reviver-array-proxy.js (0 => 250860)
--- trunk/JSTests/stress/json-parse-reviver-array-proxy.js (rev 0)
+++ trunk/JSTests/stress/json-parse-reviver-array-proxy.js 2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+const json = '{"a": 1, "b": 2}';
+
+for (let i = 1; i < 10000; i++) {
+ let keys = [];
+ let proxy = new Proxy([2, 3], {
+ get: function(target, key) {
+ keys.push(key);
+ return target[key];
+ },
+ ownKeys: function() {
+ throw new Error('[[OwnPropertyKeys]] should not be called');
+ },
+ });
+
+ let result = JSON.parse(json, function(key, value) {
+ if (key === 'a')
+ this.b = proxy;
+ return value;
+ });
+
+ shouldBe(keys.toString(), 'length,0,1');
+ shouldBe(JSON.stringify(result), '{"a":1,"b":[2,3]}');
+}
Added: trunk/JSTests/stress/json-parse-reviver-revoked-proxy.js (0 => 250860)
--- trunk/JSTests/stress/json-parse-reviver-revoked-proxy.js (rev 0)
+++ trunk/JSTests/stress/json-parse-reviver-revoked-proxy.js 2019-10-08 21:23:14 UTC (rev 250860)
@@ -0,0 +1,48 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function revivedObjProxy(key, value) {
+ if (key === 'a') {
+ let {proxy, revoke} = Proxy.revocable({}, {});
+ revoke();
+ this.b = proxy;
+ }
+
+ return value;
+}
+
+function revivedArrProxy(key, value) {
+ if (key === '0') {
+ let {proxy, revoke} = Proxy.revocable([], {});
+ revoke();
+ this[1] = proxy;
+ }
+
+ return value;
+}
+
+const objJSON = '{"a": 1, "b": 2}';
+const arrJSON = '[3, 4]';
+const isArrayError = 'TypeError: Array.isArray cannot be called on a Proxy that has been revoked';
+
+for (let i = 1; i < 10000; i++) {
+ let error;
+ try {
+ JSON.parse(objJSON, revivedObjProxy);
+ } catch (e) {
+ error = e;
+ }
+ shouldBe(error.toString(), isArrayError);
+}
+
+for (let i = 1; i < 10000; i++) {
+ let error;
+ try {
+ JSON.parse(arrJSON, revivedArrProxy);
+ } catch (e) {
+ error = e;
+ }
+ shouldBe(error.toString(), isArrayError);
+}
Modified: trunk/JSTests/test262/expectations.yaml (250859 => 250860)
--- trunk/JSTests/test262/expectations.yaml 2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/JSTests/test262/expectations.yaml 2019-10-08 21:23:14 UTC (rev 250860)
@@ -1206,15 +1206,6 @@
test/built-ins/GeneratorFunction/proto-from-ctor-realm.js:
default: 'Test262Error: Expected SameValue(«[object GeneratorFunction]», «[object GeneratorFunction]») to be true'
strict mode: 'Test262Error: Expected SameValue(«[object GeneratorFunction]», «[object GeneratorFunction]») to be true'
-test/built-ins/JSON/parse/revived-proxy.js:
- default: 'Test262Error: proxy for array Expected SameValue(«true», «false») to be true'
- strict mode: 'Test262Error: proxy for array Expected SameValue(«true», «false») to be true'
-test/built-ins/JSON/parse/reviver-array-length-coerce-err.js:
- default: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
- strict mode: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
-test/built-ins/JSON/parse/reviver-array-length-get-err.js:
- default: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
- strict mode: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
test/built-ins/Map/proto-from-ctor-realm.js:
default: 'Test262Error: Expected SameValue(«[object Map]», «[object Map]») to be true'
strict mode: 'Test262Error: Expected SameValue(«[object Map]», «[object Map]») to be true'
Modified: trunk/Source/_javascript_Core/ChangeLog (250859 => 250860)
--- trunk/Source/_javascript_Core/ChangeLog 2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-10-08 21:23:14 UTC (rev 250860)
@@ -1,3 +1,16 @@
+2019-10-08 Alexey Shvayka <shvaikal...@gmail.com>
+
+ JSON.parse incorrectly handles array proxies
+ https://bugs.webkit.org/show_bug.cgi?id=199292
+
+ Reviewed by Saam Barati.
+
+ 1. Use isArray to correctly detect proxied arrays.
+ 2. Make "length" lookup observable to array proxies and handle exceptions.
+
+ * runtime/JSONObject.cpp:
+ (JSC::Walker::walk):
+
2019-10-08 Adrian Perez de Castro <ape...@igalia.com>
[GTK][WPE] Fix non-unified builds after r250486
Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (250859 => 250860)
--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp 2019-10-08 21:09:17 UTC (rev 250859)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp 2019-10-08 21:23:14 UTC (rev 250860)
@@ -32,6 +32,7 @@
#include "Error.h"
#include "ExceptionHelpers.h"
#include "JSArray.h"
+#include "JSArrayInlines.h"
#include "JSGlobalObject.h"
#include "LiteralParser.h"
#include "Lookup.h"
@@ -663,19 +664,21 @@
arrayStartState:
case ArrayStartState: {
ASSERT(inValue.isObject());
- ASSERT(asObject(inValue)->inherits<JSArray>(vm));
+ ASSERT(isJSArray(inValue) || inValue.inherits<ProxyObject>(vm));
if (markedStack.size() > maximumFilterRecursion)
return throwStackOverflowError(m_exec, scope);
- JSArray* array = asArray(inValue);
+ JSObject* array = asObject(inValue);
markedStack.appendWithCrashOnOverflow(array);
- arrayLengthStack.append(array->length());
+ unsigned length = toLength(m_exec, array);
+ RETURN_IF_EXCEPTION(scope, { });
+ arrayLengthStack.append(length);
indexStack.append(0);
}
arrayStartVisitMember:
FALLTHROUGH;
case ArrayStartVisitMember: {
- JSArray* array = jsCast<JSArray*>(markedStack.last());
+ JSObject* array = asObject(markedStack.last());
uint32_t index = indexStack.last();
unsigned arrayLength = arrayLengthStack.last();
if (index == arrayLength) {
@@ -704,7 +707,7 @@
FALLTHROUGH;
}
case ArrayEndVisitMember: {
- JSArray* array = jsCast<JSArray*>(markedStack.last());
+ JSObject* array = asObject(markedStack.last());
JSValue filteredValue = callReviver(array, jsString(vm, String::number(indexStack.last())), outValue);
RETURN_IF_EXCEPTION(scope, { });
if (filteredValue.isUndefined())
@@ -718,7 +721,7 @@
objectStartState:
case ObjectStartState: {
ASSERT(inValue.isObject());
- ASSERT(!asObject(inValue)->inherits<JSArray>(vm));
+ ASSERT(!isJSArray(inValue));
if (markedStack.size() > maximumFilterRecursion)
return throwStackOverflowError(m_exec, scope);
@@ -778,8 +781,9 @@
outValue = inValue;
break;
}
- JSObject* object = asObject(inValue);
- if (object->inherits<JSArray>(vm))
+ bool valueIsArray = isArray(m_exec, inValue);
+ RETURN_IF_EXCEPTION(scope, { });
+ if (valueIsArray)
goto arrayStartState;
goto objectStartState;
}