Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (203296 => 203297)
--- trunk/Source/_javascript_Core/ChangeLog 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-07-15 20:58:52 UTC (rev 203297)
@@ -1,3 +1,58 @@
+2016-07-15 Keith Miller <[email protected]>
+
+ %TypedArray%.prototype.indexOf is coercing non-integers or non-floats to numbers wrongly
+ https://bugs.webkit.org/show_bug.cgi?id=159400
+
+ Reviewed by Geoffrey Garen.
+
+ This patch fixes coercion of non-numbers in indexOf/lastIndexOf.
+ Additionally, this patch fixes an issue with includes where it
+ would not check that the buffer remained non-neutered after
+ calling the toInteger() function. Lastly, some extra release
+ asserts have been added in some places to inform us of any issues
+ in the future.
+
+ Additionally, this patch changes bool toNativeFromDouble to
+ Optional<Type> toNativeFromDoubleWithoutCoercion. This makes it a
+ little clearer what the function does and also removes the return
+ argument. The only behavior change is that the function no longer
+ coerces non-numbers into numbers. That behavior was unused (maybe
+ unintended), however.
+
+ * runtime/JSGenericTypedArrayView.h:
+ (JSC::JSGenericTypedArrayView::toAdaptorNativeFromValueWithoutCoercion):
+ (JSC::JSGenericTypedArrayView::sort):
+ (JSC::JSGenericTypedArrayView::toAdaptorNativeFromValue): Deleted.
+ * runtime/JSGenericTypedArrayViewPrototypeFunctions.h:
+ (JSC::genericTypedArrayViewProtoFuncCopyWithin):
+ (JSC::genericTypedArrayViewProtoFuncIncludes):
+ (JSC::genericTypedArrayViewProtoFuncIndexOf):
+ (JSC::genericTypedArrayViewProtoFuncLastIndexOf):
+ * runtime/ToNativeFromValue.h:
+ (JSC::toNativeFromValueWithoutCoercion):
+ (JSC::toNativeFromValue): Deleted.
+ * runtime/TypedArrayAdaptors.h:
+ (JSC::IntegralTypedArrayAdaptor::toNativeFromInt32WithoutCoercion):
+ (JSC::IntegralTypedArrayAdaptor::toNativeFromUint32WithoutCoercion):
+ (JSC::IntegralTypedArrayAdaptor::toNativeFromDoubleWithoutCoercion):
+ (JSC::FloatTypedArrayAdaptor::toNativeFromInt32WithoutCoercion):
+ (JSC::FloatTypedArrayAdaptor::toNativeFromDoubleWithoutCoercion):
+ (JSC::Uint8ClampedAdaptor::toNativeFromInt32WithoutCoercion):
+ (JSC::Uint8ClampedAdaptor::toNativeFromDoubleWithoutCoercion):
+ (JSC::IntegralTypedArrayAdaptor::toNativeFromInt32): Deleted.
+ (JSC::IntegralTypedArrayAdaptor::toNativeFromUint32): Deleted.
+ (JSC::IntegralTypedArrayAdaptor::toNativeFromDouble): Deleted.
+ (JSC::FloatTypedArrayAdaptor::toNativeFromInt32): Deleted.
+ (JSC::FloatTypedArrayAdaptor::toNativeFromDouble): Deleted.
+ (JSC::Uint8ClampedAdaptor::toNativeFromInt32): Deleted.
+ (JSC::Uint8ClampedAdaptor::toNativeFromDouble): Deleted.
+ * tests/stress/resources/typedarray-test-helper-functions.js:
+ * tests/stress/typedarray-functions-with-neutered.js:
+ (callWithArgs):
+ * tests/stress/typedarray-includes.js: Added.
+ * tests/stress/typedarray-indexOf.js:
+ * tests/stress/typedarray-lastIndexOf.js:
+
2016-07-15 Csaba Osztrogonác <[email protected]>
Add new functions to ARMAssembler after r202214
Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h (203296 => 203297)
--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h 2016-07-15 20:58:52 UTC (rev 203297)
@@ -186,10 +186,11 @@
static ElementType toAdaptorNativeFromValue(ExecState* exec, JSValue jsValue) { return toNativeFromValue<Adaptor>(exec, jsValue); }
- static bool toAdaptorNativeFromValue(ExecState* exec, JSValue jsValue, ElementType& result) { return toNativeFromValue<Adaptor>(exec, jsValue, result); }
+ static Optional<ElementType> toAdaptorNativeFromValueWithoutCoercion(JSValue jsValue) { return toNativeFromValueWithoutCoercion<Adaptor>(jsValue); }
void sort()
{
+ RELEASE_ASSERT(!isNeutered());
switch (Adaptor::typeValue) {
case TypeFloat32:
sortFloat<int32_t>();
Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h (203296 => 203297)
--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h 2016-07-15 20:58:52 UTC (rev 203297)
@@ -154,14 +154,14 @@
if (vm.exception())
return encodedJSValue();
- if (thisObject->isNeutered())
- return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
-
if (final < from)
return JSValue::encode(exec->thisValue());
long count = std::min(length - std::max(to, from), final - from);
+ if (thisObject->isNeutered())
+ return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
+
typename ViewClass::ElementType* array = thisObject->typedVector();
memmove(array + to, array + from, count * thisObject->elementSize);
@@ -171,6 +171,7 @@
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIncludes(ExecState* exec)
{
+ VM& vm = exec->vm();
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
@@ -183,19 +184,21 @@
JSValue valueToFind = exec->argument(0);
unsigned index = argumentClampedIndexFromStartOrEnd(exec, 1, length);
+ if (vm.exception())
+ return JSValue::encode(jsUndefined());
- if (!valueToFind.isNumber())
- return JSValue::encode(jsBoolean(false));
+ if (thisObject->isNeutered())
+ return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
typename ViewClass::ElementType* array = thisObject->typedVector();
- typename ViewClass::ElementType target;
- if (!ViewClass::toAdaptorNativeFromValue(exec, valueToFind, target))
+ auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
+ if (!targetOption)
return JSValue::encode(jsBoolean(false));
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ ASSERT(!vm.exception());
+ RELEASE_ASSERT(!thisObject->isNeutered());
- if (std::isnan(static_cast<double>(target))) {
+ if (std::isnan(static_cast<double>(*targetOption))) {
for (; index < length; ++index) {
if (std::isnan(static_cast<double>(array[index])))
return JSValue::encode(jsBoolean(true));
@@ -202,7 +205,7 @@
}
} else {
for (; index < length; ++index) {
- if (array[index] == target)
+ if (array[index] == targetOption)
return JSValue::encode(jsBoolean(true));
}
}
@@ -233,12 +236,14 @@
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
typename ViewClass::ElementType* array = thisObject->typedVector();
- typename ViewClass::ElementType target = ViewClass::toAdaptorNativeFromValue(exec, valueToFind);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
+ if (!targetOption)
+ return JSValue::encode(jsNumber(-1));
+ ASSERT(!vm.exception());
+ RELEASE_ASSERT(!thisObject->isNeutered());
for (; index < length; ++index) {
- if (array[index] == target)
+ if (array[index] == targetOption)
return JSValue::encode(jsNumber(index));
}
@@ -287,6 +292,7 @@
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncLastIndexOf(ExecState* exec)
{
// 22.2.3.16
+ VM& vm = exec->vm();
ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
@@ -311,16 +317,22 @@
index = static_cast<unsigned>(fromDouble);
}
+ if (vm.exception())
+ return JSValue::encode(JSValue());
+
if (thisObject->isNeutered())
return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
+ auto targetOption = ViewClass::toAdaptorNativeFromValueWithoutCoercion(valueToFind);
+ if (!targetOption)
+ return JSValue::encode(jsNumber(-1));
+
typename ViewClass::ElementType* array = thisObject->typedVector();
- typename ViewClass::ElementType target = ViewClass::toAdaptorNativeFromValue(exec, valueToFind);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ ASSERT(!vm.exception());
+ RELEASE_ASSERT(!thisObject->isNeutered());
for (; index >= 0; --index) {
- if (array[index] == target)
+ if (array[index] == targetOption)
return JSValue::encode(jsNumber(index));
}
Modified: trunk/Source/_javascript_Core/runtime/ToNativeFromValue.h (203296 => 203297)
--- trunk/Source/_javascript_Core/runtime/ToNativeFromValue.h 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/runtime/ToNativeFromValue.h 2016-07-15 20:58:52 UTC (rev 203297)
@@ -49,13 +49,13 @@
}
template<typename Adaptor>
-bool toNativeFromValue(ExecState* exec, JSValue value, typename Adaptor::Type& result)
+Optional<typename Adaptor::Type> toNativeFromValueWithoutCoercion(JSValue value)
{
+ if (!value.isNumber())
+ return Nullopt;
if (value.isInt32())
- return Adaptor::toNativeFromInt32(value.asInt32(), result);
- if (value.isNumber())
- return Adaptor::toNativeFromDouble(value.asDouble(), result);
- return Adaptor::toNativeFromDouble(value.toNumber(exec), result);
+ return Adaptor::toNativeFromInt32WithoutCoercion(value.asInt32());
+ return Adaptor::toNativeFromDoubleWithoutCoercion(value.asDouble());
}
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/TypedArrayAdaptors.h (203296 => 203297)
--- trunk/Source/_javascript_Core/runtime/TypedArrayAdaptors.h 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/runtime/TypedArrayAdaptors.h 2016-07-15 20:58:52 UTC (rev 203297)
@@ -79,36 +79,31 @@
return OtherAdaptor::toNativeFromInt32(value);
}
- static bool toNativeFromInt32(int32_t value, Type& result)
+ static Optional<Type> toNativeFromInt32WithoutCoercion(int32_t value)
{
if ((value >= 0 && static_cast<uint32_t>(value) > static_cast<uint32_t>(maxValue)) || value < static_cast<int32_t>(minValue))
- return false;
-
- result = static_cast<Type>(value);
-
- return true;
+ return Nullopt;
+ return static_cast<Type>(value);
}
- static bool toNativeFromUint32(uint32_t value, Type& result)
+ static Optional<Type> toNativeFromUint32WithoutCoercion(uint32_t value)
{
if (value > static_cast<uint32_t>(maxValue))
- return false;
+ return Nullopt;
- result = static_cast<Type>(value);
-
- return true;
+ return static_cast<Type>(value);
}
- static bool toNativeFromDouble(double value, Type& result)
+ static Optional<Type> toNativeFromDoubleWithoutCoercion(double value)
{
Type integer = static_cast<Type>(value);
if (static_cast<double>(integer) != value)
- return false;
+ return Nullopt;
if (value < 0)
- return toNativeFromInt32(static_cast<int32_t>(value), result);
+ return toNativeFromInt32WithoutCoercion(static_cast<int32_t>(value));
- return toNativeFromUint32(static_cast<uint32_t>(value), result);
+ return toNativeFromUint32WithoutCoercion(static_cast<uint32_t>(value));
}
};
@@ -154,29 +149,25 @@
return OtherAdaptor::toNativeFromDouble(value);
}
- static bool toNativeFromInt32(int32_t value, Type& result)
+ static Optional<Type> toNativeFromInt32WithoutCoercion(int32_t value)
{
- result = static_cast<Type>(value);
- return true;
+ return static_cast<Type>(value);
}
- static Type toNativeFromDouble(double value, Type& result)
+ static Optional<Type> toNativeFromDoubleWithoutCoercion(double value)
{
- if (std::isnan(value) || std::isinf(value)) {
- result = static_cast<Type>(value);
- return true;
- }
+ if (std::isnan(value) || std::isinf(value))
+ return static_cast<Type>(value);
Type valueResult = static_cast<Type>(value);
if (static_cast<double>(valueResult) != value)
- return false;
+ return Nullopt;
if (value < minValue || value > maxValue)
- return false;
+ return Nullopt;
- result = valueResult;
- return true;
+ return valueResult;
}
};
@@ -264,24 +255,21 @@
return OtherAdaptor::toNativeFromInt32(value);
}
- static bool toNativeFromInt32(int32_t value, Type& result)
+ static Optional<Type> toNativeFromInt32WithoutCoercion(int32_t value)
{
if (value > maxValue || value < minValue)
- return false;
+ return Nullopt;
- result = static_cast<Type>(value);
-
- return true;
+ return static_cast<Type>(value);
}
- static bool toNativeFromDouble(double value, uint8_t& result)
+ static Optional<Type> toNativeFromDoubleWithoutCoercion(double value)
{
uint8_t integer = static_cast<uint8_t>(value);
if (static_cast<double>(integer) != value)
- return false;
+ return Nullopt;
- result = integer;
- return true;
+ return integer;
}
private:
Modified: trunk/Source/_javascript_Core/tests/stress/resources/typedarray-test-helper-functions.js (203296 => 203297)
--- trunk/Source/_javascript_Core/tests/stress/resources/typedarray-test-helper-functions.js 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/tests/stress/resources/typedarray-test-helper-functions.js 2016-07-15 20:58:52 UTC (rev 203297)
@@ -4,6 +4,8 @@
var signedArrays = [Int8Array, Int16Array, Int32Array, Float32Array, Float64Array];
+var intArrays = [Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array];
+
var floatArrays = [Float32Array, Float64Array];
function forEachTypedArray(constructors, testFunction /* , initialValues */ ) {
Modified: trunk/Source/_javascript_Core/tests/stress/typedarray-functions-with-neutered.js (203296 => 203297)
--- trunk/Source/_javascript_Core/tests/stress/typedarray-functions-with-neutered.js 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/tests/stress/typedarray-functions-with-neutered.js 2016-07-15 20:58:52 UTC (rev 203297)
@@ -83,6 +83,7 @@
{ func:proto.findIndex, args:["func"] },
{ func:proto.forEach, args:["func"] },
{ func:proto.indexOf, args:["na", "prim"] },
+ { func:proto.includes, args:["na", "prim"] },
{ func:proto.join, args:["prim"] },
{ func:proto.lastIndexOf, args:["na", "prim"] },
{ func:proto.map, args:["func"] },
@@ -105,7 +106,7 @@
return argNum;
}
-function callWithArgs(func, array, args) {
+function callWithArgs(func, array, args, argNum) {
let failed = true;
try {
func.call(array, ...args);
@@ -115,7 +116,7 @@
failed = false;
}
if (failed)
- throw new Error([func, args]);
+ throw new Error([func, argNum]);
}
@@ -135,13 +136,13 @@
transferArrayBuffer(array.buffer);
return func === array.every ? 1 : 0;
};
- callWithArgs(func, array, callArgs);
+ callWithArgs(func, array, callArgs, argNum);
} else if (arg === "prim") {
callArgs[argNum] = { [Symbol.toPrimitive]() {
transferArrayBuffer(array.buffer);
return argNum;
} };
- callWithArgs(func, array, callArgs);
+ callWithArgs(func, array, callArgs, argNum);
} else if (arg === "array") {
callArgs[argNum] = new Array(4);
callArgs[argNum].fill(2);
@@ -150,7 +151,7 @@
return 1;
} };
Object.defineProperty(callArgs[argNum], 1, desc);
- callWithArgs(func, array, callArgs);
+ callWithArgs(func, array, callArgs, argNum);
} else
throw new Error(arg);
}
Added: trunk/Source/_javascript_Core/tests/stress/typedarray-includes.js (0 => 203297)
--- trunk/Source/_javascript_Core/tests/stress/typedarray-includes.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/typedarray-includes.js 2016-07-15 20:58:52 UTC (rev 203297)
@@ -0,0 +1,10 @@
+load("resources/typedarray-test-helper-functions.js");
+
+for (constructor of typedArrays) {
+ let a = new constructor(10);
+ passed = true;
+ result = a.includes({ valueOf() { passed = false; return 1; } });
+ shouldBeTrue("passed");
+ shouldBeFalse("result");
+}
+finishJSTest();
Modified: trunk/Source/_javascript_Core/tests/stress/typedarray-indexOf.js (203296 => 203297)
--- trunk/Source/_javascript_Core/tests/stress/typedarray-indexOf.js 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/tests/stress/typedarray-indexOf.js 2016-07-15 20:58:52 UTC (rev 203297)
@@ -28,4 +28,40 @@
shouldBeTrue("testPrototypeFunction('indexOf', '(2, -1)', array, 3)");
shouldBeTrue("testPrototypeFunction('indexOf', '(2, -2)', array, 3)");
debug("");
+
+debug("Check object coersion");
+for (constructor of typedArrays) {
+ a = new constructor([0,2,3]);
+ passed = true;
+
+ shouldBe("a.indexOf({ valueOf() { passed = false; return 1; }})", "-1");
+ shouldBeTrue("passed");
+ shouldBe("a.indexOf(3, {valueOf: () => -1})", "2");
+
+ // test we don't coerce non-native values
+ shouldBe("a.indexOf(\"abc\")", "-1");
+ shouldBe("a.indexOf(null)", "-1");
+ shouldBe("a.indexOf(undefined)", "-1");
+ shouldBe("a.indexOf({1: ''})", "-1");
+ shouldBe("a.indexOf(\"\")", "-1");
+}
+
+
+for (constructor of intArrays) {
+ a = new constructor([0,2,3]);
+
+ shouldBe("a.indexOf(2.0)", "1");
+ shouldBe("a.indexOf(2.5)", "-1");
+}
+
+for (constructor of floatArrays) {
+ a = new constructor([0,2.0,3.6, NaN, Infinity]);
+
+ shouldBe("a.indexOf(2.0)", "1");
+ shouldBe("a.indexOf(2.5)", "-1");
+ shouldBe("a.indexOf(3.600001)", "-1");
+ shouldBe("a.indexOf(NaN)", "-1");
+ shouldBe("a.indexOf(Infinity)", "4");
+}
+
finishJSTest();
Modified: trunk/Source/_javascript_Core/tests/stress/typedarray-lastIndexOf.js (203296 => 203297)
--- trunk/Source/_javascript_Core/tests/stress/typedarray-lastIndexOf.js 2016-07-15 20:56:29 UTC (rev 203296)
+++ trunk/Source/_javascript_Core/tests/stress/typedarray-lastIndexOf.js 2016-07-15 20:58:52 UTC (rev 203297)
@@ -25,4 +25,40 @@
shouldBeTrue("testPrototypeFunction('lastIndexOf', '(2, -1)', array, 3)");
shouldBeTrue("testPrototypeFunction('lastIndexOf', '(2, -2)', array, 0)");
debug("");
+
+debug("Check object coersion");
+for (constructor of typedArrays) {
+ a = new constructor([0,2,3]);
+ passed = true;
+
+ shouldBe("a.lastIndexOf({ valueOf() { passed = false; return 1; }})", "-1");
+ shouldBeTrue("passed");
+ shouldBe("a.lastIndexOf(3, {valueOf: () => 3})", "2");
+
+ // test we don't coerce non-native values
+ shouldBe("a.lastIndexOf(\"abc\")", "-1");
+ shouldBe("a.lastIndexOf(null)", "-1");
+ shouldBe("a.lastIndexOf(undefined)", "-1");
+ shouldBe("a.lastIndexOf({1: ''})", "-1");
+ shouldBe("a.lastIndexOf(\"\")", "-1");
+}
+
+
+for (constructor of intArrays) {
+ a = new constructor([0,2,3]);
+
+ shouldBe("a.lastIndexOf(2.0)", "1");
+ shouldBe("a.lastIndexOf(2.5)", "-1");
+}
+
+for (constructor of floatArrays) {
+ a = new constructor([0,2.0,3.6, NaN, Infinity]);
+
+ shouldBe("a.lastIndexOf(2.0)", "1");
+ shouldBe("a.lastIndexOf(2.5)", "-1");
+ shouldBe("a.lastIndexOf(3.600001)", "-1");
+ shouldBe("a.lastIndexOf(NaN)", "-1");
+ shouldBe("a.lastIndexOf(Infinity)", "4");
+}
+
finishJSTest();