Added: trunk/JSTests/stress/array-slice-on-frozen-object.js (0 => 207226)
--- trunk/JSTests/stress/array-slice-on-frozen-object.js (rev 0)
+++ trunk/JSTests/stress/array-slice-on-frozen-object.js 2016-10-12 18:27:50 UTC (rev 207226)
@@ -0,0 +1,64 @@
+//@ runFTLNoCJIT
+
+let totalFailed = 0;
+
+function shouldEqual(testId, actual, expected) {
+ if (actual != expected) {
+ throw testId + ": ERROR: expect " + expected + ", actual " + actual;
+ }
+}
+
+function makeArray() {
+ return ['unmodifiable'];
+}
+
+function makeArrayLikeObject() {
+ var obj = {};
+ obj[0] = 'unmodifiable';
+ obj.length = 1;
+ return obj;
+}
+
+function emptyArraySourceMaker() {
+ return [];
+}
+
+function singleElementArraySourceMaker() {
+ return ['modified_1'];
+}
+
+// Make test functions with unique codeblocks.
+function makeSliceTest(testId) {
+ return new Function("arr", "arr.slice(0); return " + testId + ";");
+}
+
+let numIterations = 10000;
+
+function runTest(testId, testMaker, targetMaker, sourceMaker, expectedValue, expectedException) {
+ var test = testMaker(testId);
+ noInline(test);
+
+ for (var i = 0; i < numIterations; i++) {
+ var exception = undefined;
+
+ var obj = targetMaker();
+ obj = Object.freeze(obj);
+
+ var arr = sourceMaker();
+ arr.constructor = { [Symbol.species]: function() { return obj; } };
+
+ try {
+ test(arr);
+ } catch (e) {
+ exception = "" + e;
+ exception = exception.substr(0, 10); // Search for "TypeError:".
+ }
+ shouldEqual(testId, exception, expectedException);
+ shouldEqual(testId, obj[0], expectedValue);
+ }
+}
+
+runTest(10010, makeSliceTest, makeArray, emptyArraySourceMaker, "unmodifiable", "TypeError:");
+runTest(10011, makeSliceTest, makeArray, singleElementArraySourceMaker, "unmodifiable", "TypeError:");
+runTest(10020, makeSliceTest, makeArrayLikeObject, emptyArraySourceMaker, "unmodifiable", "TypeError:");
+runTest(10021, makeSliceTest, makeArrayLikeObject, singleElementArraySourceMaker, "unmodifiable", "TypeError:");
Modified: trunk/Source/_javascript_Core/ChangeLog (207225 => 207226)
--- trunk/Source/_javascript_Core/ChangeLog 2016-10-12 17:41:00 UTC (rev 207225)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-10-12 18:27:50 UTC (rev 207226)
@@ -1,3 +1,46 @@
+2016-10-12 Mark Lam <mark....@apple.com>
+
+ Array.prototype.slice should not modify frozen objects.
+ https://bugs.webkit.org/show_bug.cgi?id=163338
+
+ Reviewed by Filip Pizlo.
+
+ 1. The ES6 spec for Array.prototype.slice
+ (https://tc39.github.io/ecma262/#sec-array.prototype.slice) states that it uses
+ the CreateDataPropertyOrThrow()
+ (https://tc39.github.io/ecma262/#sec-createdatapropertyorthrow) to add items to
+ the result array. The spec for CreateDataPropertyOrThrow states:
+
+ "This abstract operation creates a property whose attributes are set to the
+ same defaults used for properties created by the ECMAScript language assignment
+ operator. Normally, the property will not already exist. If it does exist and
+ is not configurable or if O is not extensible, [[DefineOwnProperty]] will
+ return false causing this operation to throw a TypeError exception."
+
+ 2. Array.prototype.slice also uses a Set function
+ (https://tc39.github.io/ecma262/#sec-set-o-p-v-throw) to set the "length"
+ property and passes true for the Throw argument. Ultimately, it ends up
+ calling the OrdinarySet function
+ (https://tc39.github.io/ecma262/#sec-ordinaryset) that will fail if the
+ property is not writable. This failure should result in a TypeError being
+ thrown in Set.
+
+ Since the properties of frozen objects are not extensible, not configurable,
+ and not writeable, Array.prototype.slice should fail to write to the result
+ array if it is frozen.
+
+ If the source array being sliced has 1 or more elements, (1) will take effect
+ when we try to set the element in the non-writeable result obj.
+ If the source array being sliced has 0 elements, we will not set any elements and
+ (1) will not trigger. Subsequently, (2) will take effect when we will try to
+ set the length of the result obj.
+
+ * runtime/ArrayPrototype.cpp:
+ (JSC::putLength):
+ (JSC::setLength):
+ (JSC::arrayProtoFuncSlice):
+ (JSC::arrayProtoFuncSplice):
+
2016-10-12 Filip Pizlo <fpi...@apple.com>
Remove JITWriteBarrier.h
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (207225 => 207226)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2016-10-12 17:41:00 UTC (rev 207225)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2016-10-12 18:27:50 UTC (rev 207226)
@@ -163,17 +163,24 @@
return slot.getValue(exec, index);
}
-static ALWAYS_INLINE void putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
+static ALWAYS_INLINE bool putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
{
PutPropertySlot slot(obj);
- obj->methodTable()->put(obj, exec, vm.propertyNames->length, value, slot);
+ return obj->methodTable()->put(obj, exec, vm.propertyNames->length, value, slot);
}
static ALWAYS_INLINE void setLength(ExecState* exec, VM& vm, JSObject* obj, unsigned value)
{
- if (isJSArray(obj))
- jsCast<JSArray*>(obj)->setLength(exec, value);
- putLength(exec, vm, obj, jsNumber(value));
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ static const bool throwException = true;
+ if (isJSArray(obj)) {
+ jsCast<JSArray*>(obj)->setLength(exec, value, throwException);
+ RETURN_IF_EXCEPTION(scope, void());
+ }
+ bool success = putLength(exec, vm, obj, jsNumber(value));
+ RETURN_IF_EXCEPTION(scope, void());
+ if (UNLIKELY(!success))
+ throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
}
inline bool speciesWatchpointsValid(ExecState* exec, JSObject* thisObject)
@@ -874,8 +881,9 @@
JSValue v = getProperty(exec, thisObj, k);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
if (v)
- result->putDirectIndex(exec, n, v);
+ result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
}
+ scope.release();
setLength(exec, vm, result, n);
return JSValue::encode(result);
}
@@ -907,6 +915,8 @@
}
setLength(exec, vm, result, 0);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ scope.release();
setLength(exec, vm, thisObj, length);
return JSValue::encode(result);
}
@@ -972,6 +982,7 @@
RETURN_IF_EXCEPTION(scope, encodedJSValue());
}
+ scope.release();
setLength(exec, vm, thisObj, length - deleteCount + additionalArgs);
return JSValue::encode(result);
}