Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (191211 => 191212)
--- trunk/Source/_javascript_Core/ChangeLog 2015-10-16 21:25:52 UTC (rev 191211)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-10-16 21:40:21 UTC (rev 191212)
@@ -1,3 +1,27 @@
+2015-10-16 Keith Miller <keith_mil...@apple.com>
+
+ Fix some issues with TypedArrays
+ https://bugs.webkit.org/show_bug.cgi?id=150216
+
+ Reviewed by Geoffrey Garen.
+
+ This fixes a couple of issues:
+ 1) The DFG had a separate case for creating new typedarrays in the dfg when the first argument is an object.
+ Since the code for creating a Typedarray in the dfg is almost the same as the code in Baseline/LLInt
+ the two cases have been merged.
+ 2) If the length property on an object was unset then the construction could crash.
+ 3) The TypedArray.prototype.set function and the TypedArray constructor should not call [[Get]] for the
+ length of the source object when the source object is a TypedArray.
+ 4) The conditions that were used to decide if the iterator could be skipped were incorrect.
+ Instead of checking for have a bad time we should have checked the Indexing type did not allow for
+ indexed accessors.
+
+ * dfg/DFGOperations.cpp:
+ * runtime/JSGenericTypedArrayViewConstructorInlines.h:
+ (JSC::constructGenericTypedArrayViewWithArguments):
+ (JSC::constructGenericTypedArrayView):
+ (JSC::constructGenericTypedArrayViewWithFirstArgument): Deleted.
+
2015-10-16 Anders Carlsson <ander...@apple.com>
Fix Windows build.
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (191211 => 191212)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2015-10-16 21:25:52 UTC (rev 191211)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2015-10-16 21:40:21 UTC (rev 191212)
@@ -142,64 +142,6 @@
return bitwise_cast<char*>(ViewClass::create(exec, structure, size));
}
-template<typename ViewClass>
-char* newTypedArrayWithOneArgument(
- ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
-{
- VM& vm = exec->vm();
- NativeCallFrameTracer tracer(&vm, exec);
-
- JSValue value = JSValue::decode(encodedValue);
-
- if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(value)) {
- RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
-
- if (buffer->byteLength() % ViewClass::elementSize) {
- vm.throwException(exec, createRangeError(exec, ASCIILiteral("ArrayBuffer length minus the byteOffset is not a multiple of the element size")));
- return 0;
- }
- return bitwise_cast<char*>(
- ViewClass::create(
- exec, structure, buffer, 0, buffer->byteLength() / ViewClass::elementSize));
- }
-
- if (JSObject* object = jsDynamicCast<JSObject*>(value)) {
- unsigned length = object->get(exec, vm.propertyNames->length).toUInt32(exec);
- if (exec->hadException())
- return 0;
-
- ViewClass* result = ViewClass::createUninitialized(exec, structure, length);
- if (!result)
- return 0;
-
- if (!result->set(exec, object, 0, length))
- return 0;
-
- return bitwise_cast<char*>(result);
- }
-
- int length;
- if (value.isInt32())
- length = value.asInt32();
- else if (!value.isNumber()) {
- vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument")));
- return 0;
- } else {
- length = static_cast<int>(value.asNumber());
- if (length != value.asNumber()) {
- vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument (fractional lengths not allowed)")));
- return 0;
- }
- }
-
- if (length < 0) {
- vm.throwException(exec, createRangeError(exec, ASCIILiteral("Requested length is negative")));
- return 0;
- }
-
- return bitwise_cast<char*>(ViewClass::create(exec, structure, length));
-}
-
extern "C" {
EncodedJSValue JIT_OPERATION operationToThis(ExecState* exec, EncodedJSValue encodedOp)
@@ -664,7 +606,7 @@
char* JIT_OPERATION operationNewInt8ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSInt8Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSInt8Array>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewInt16ArrayWithSize(
@@ -676,7 +618,7 @@
char* JIT_OPERATION operationNewInt16ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSInt16Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSInt16Array>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewInt32ArrayWithSize(
@@ -688,7 +630,7 @@
char* JIT_OPERATION operationNewInt32ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSInt32Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSInt32Array>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewUint8ArrayWithSize(
@@ -700,7 +642,7 @@
char* JIT_OPERATION operationNewUint8ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSUint8Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint8Array>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewUint8ClampedArrayWithSize(
@@ -712,7 +654,7 @@
char* JIT_OPERATION operationNewUint8ClampedArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSUint8ClampedArray>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint8ClampedArray>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewUint16ArrayWithSize(
@@ -724,7 +666,7 @@
char* JIT_OPERATION operationNewUint16ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSUint16Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint16Array>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewUint32ArrayWithSize(
@@ -736,7 +678,7 @@
char* JIT_OPERATION operationNewUint32ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSUint32Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSUint32Array>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewFloat32ArrayWithSize(
@@ -748,7 +690,7 @@
char* JIT_OPERATION operationNewFloat32ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSFloat32Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSFloat32Array>(exec, structure, encodedValue, 0, Nullopt));
}
char* JIT_OPERATION operationNewFloat64ArrayWithSize(
@@ -760,7 +702,7 @@
char* JIT_OPERATION operationNewFloat64ArrayWithOneArgument(
ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
{
- return newTypedArrayWithOneArgument<JSFloat64Array>(exec, structure, encodedValue);
+ return reinterpret_cast<char*>(constructGenericTypedArrayViewWithArguments<JSFloat64Array>(exec, structure, encodedValue, 0, Nullopt));
}
JSCell* JIT_OPERATION operationCreateActivationDirect(ExecState* exec, Structure* structure, JSScope* scope, SymbolTable* table, EncodedJSValue initialValueEncoded)
Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewConstructorInlines.h (191211 => 191212)
--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewConstructorInlines.h 2015-10-16 21:25:52 UTC (rev 191211)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewConstructorInlines.h 2015-10-16 21:40:21 UTC (rev 191212)
@@ -77,23 +77,23 @@
}
template<typename ViewClass>
-static EncodedJSValue constructGenericTypedArrayViewFromIterator(ExecState* exec, Structure* structure, JSValue iterator)
+static JSObject* constructGenericTypedArrayViewFromIterator(ExecState* exec, Structure* structure, JSValue iterator)
{
if (!iterator.isObject())
- return JSValue::encode(throwTypeError(exec, "Symbol.Iterator for the first argument did not return an object."));
+ return throwTypeError(exec, "Symbol.Iterator for the first argument did not return an object.");
MarkedArgumentBuffer storage;
while (true) {
JSValue next = iteratorStep(exec, iterator);
if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ return nullptr;
if (next.isFalse())
break;
JSValue nextItem = iteratorValue(exec, next);
if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ return nullptr;
storage.append(nextItem);
}
@@ -101,132 +101,156 @@
ViewClass* result = ViewClass::createUninitialized(exec, structure, storage.size());
if (!result) {
ASSERT(exec->hadException());
- return JSValue::encode(jsUndefined());
+ return nullptr;
}
for (unsigned i = 0; i < storage.size(); ++i) {
if (!result->setIndex(exec, i, storage.at(i))) {
ASSERT(exec->hadException());
- return JSValue::encode(jsUndefined());
+ return nullptr;
}
}
- return JSValue::encode(result);
+ return result;
}
template<typename ViewClass>
-static EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec)
+static JSObject* constructGenericTypedArrayViewWithArguments(ExecState* exec, Structure* structure, EncodedJSValue firstArgument, unsigned offset, Optional<unsigned> lengthOpt)
{
- Structure* structure =
- asInternalFunction(exec->callee())->globalObject()->typedArrayStructure(
- ViewClass::TypedArrayStorageType);
-
+ JSValue firstValue = JSValue::decode(firstArgument);
VM& vm = exec->vm();
- if (!exec->argumentCount()) {
- if (ViewClass::TypedArrayStorageType == TypeDataView)
- return throwVMError(exec, createTypeError(exec, "DataView constructor requires at least one argument."));
-
- // Even though the documentation doesn't say so, it's correct to say
- // "new Int8Array()". This is the same as allocating an array of zero
- // length.
- return JSValue::encode(ViewClass::create(exec, structure, 0));
- }
-
- if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(exec->argument(0))) {
+ if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(firstValue)) {
RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
-
- unsigned offset = (exec->argumentCount() > 1) ? exec->uncheckedArgument(1).toUInt32(exec) : 0;
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
unsigned length = 0;
- if (exec->argumentCount() > 2) {
- length = exec->uncheckedArgument(2).toUInt32(exec);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
- } else {
+
+ if (lengthOpt)
+ length = lengthOpt.value();
+ else {
if ((buffer->byteLength() - offset) % ViewClass::elementSize)
- return throwVMError(exec, createRangeError(exec, "ArrayBuffer length minus the byteOffset is not a multiple of the element size"));
+ return throwRangeError(exec, "ArrayBuffer length minus the byteOffset is not a multiple of the element size");
length = (buffer->byteLength() - offset) / ViewClass::elementSize;
+
}
- return JSValue::encode(ViewClass::create(exec, structure, buffer, offset, length));
+
+ return ViewClass::create(exec, structure, buffer, offset, length);
}
+ ASSERT(!offset && !lengthOpt);
if (ViewClass::TypedArrayStorageType == TypeDataView)
- return throwVMError(exec, createTypeError(exec, "Expected ArrayBuffer for the first argument."));
+ return throwTypeError(exec, "Expected ArrayBuffer for the first argument.");
// For everything but DataView, we allow construction with any of:
// - Another array. This creates a copy of the of that array.
// - An integer. This creates a new typed array of that length and zero-initializes it.
-
- if (JSObject* object = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0))) {
- PropertySlot lengthSlot(object);
- object->getPropertySlot(exec, vm.propertyNames->length, lengthSlot);
- if (!isTypedView(object->classInfo()->typedArrayStorageType)) {
+ if (JSObject* object = jsDynamicCast<JSObject*>(firstValue)) {
+ unsigned length;
+
+ if (isTypedView(object->classInfo()->typedArrayStorageType))
+ length = jsCast<JSArrayBufferView*>(object)->length();
+ else {
+ PropertySlot lengthSlot(object);
+ object->getPropertySlot(exec, vm.propertyNames->length, lengthSlot);
+
JSValue iteratorFunc = object->get(exec, vm.propertyNames->iteratorSymbol);
if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ return nullptr;
// We would like not use the iterator as it is painfully slow. Fortunately, unless
// 1) The iterator is not a known iterator.
// 2) The base object does not have a length getter.
- // 3) Bad times are being had.
+ // 3) The base object might have indexed getters.
// it should not be observable that we do not use the iterator.
if (!iteratorFunc.isUndefined()
- && (iteratorFunc != exec->lexicalGlobalObject()->arrayProtoValuesFunction()
+ && (iteratorFunc != object->globalObject()->arrayProtoValuesFunction()
|| lengthSlot.isAccessor() || lengthSlot.isCustom()
- || exec->lexicalGlobalObject()->isHavingABadTime())) {
+ || hasAnyArrayStorage(object->indexingType()))) {
CallData callData;
CallType callType = getCallData(iteratorFunc, callData);
if (callType == CallTypeNone)
- return JSValue::encode(throwTypeError(exec, "Symbol.Iterator for the first argument cannot be called."));
+ return throwTypeError(exec, "Symbol.Iterator for the first argument cannot be called.");
ArgList arguments;
JSValue iterator = call(exec, iteratorFunc, callType, callData, object, arguments);
if (exec->hadException())
- return JSValue::encode(jsUndefined());
+ return nullptr;
return constructGenericTypedArrayViewFromIterator<ViewClass>(exec, structure, iterator);
+ }
- }
+ length = lengthSlot.isUnset() ? 0 : lengthSlot.getValue(exec, vm.propertyNames->length).toUInt32(exec);
+ if (exec->hadException())
+ return nullptr;
}
- unsigned length = lengthSlot.getValue(exec, vm.propertyNames->length).toUInt32(exec);
- if (exec->hadException())
- return JSValue::encode(jsUndefined());
ViewClass* result = ViewClass::createUninitialized(exec, structure, length);
if (!result) {
ASSERT(exec->hadException());
- return JSValue::encode(jsUndefined());
+ return nullptr;
}
if (!result->set(exec, object, 0, length))
- return JSValue::encode(jsUndefined());
+ return nullptr;
- return JSValue::encode(result);
+ return result;
}
int length;
- if (exec->uncheckedArgument(0).isInt32())
- length = exec->uncheckedArgument(0).asInt32();
- else if (!exec->uncheckedArgument(0).isNumber())
- return throwVMError(exec, createTypeError(exec, "Invalid array length argument"));
+ if (firstValue.isInt32())
+ length = firstValue.asInt32();
+ else if (!firstValue.isNumber())
+ return throwTypeError(exec, "Invalid array length argument");
else {
- length = static_cast<int>(exec->uncheckedArgument(0).asNumber());
- if (length != exec->uncheckedArgument(0).asNumber())
- return throwVMError(exec, createTypeError(exec, "Invalid array length argument (fractional lengths not allowed)"));
+ length = static_cast<int>(firstValue.asNumber());
+ if (length != firstValue.asNumber())
+ return throwTypeError(exec, "Invalid array length argument (fractional lengths not allowed)");
}
if (length < 0)
- return throwVMError(exec, createRangeError(exec, "Requested length is negative"));
- return JSValue::encode(ViewClass::create(exec, structure, length));
+ return throwRangeError(exec, "Requested length is negative");
+ return ViewClass::create(exec, structure, length);
}
template<typename ViewClass>
+static EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec)
+{
+ Structure* structure =
+ asInternalFunction(exec->callee())->globalObject()->typedArrayStructure(
+ ViewClass::TypedArrayStorageType);
+
+ size_t argCount = exec->argumentCount();
+
+ if (!argCount) {
+ if (ViewClass::TypedArrayStorageType == TypeDataView)
+ return throwVMError(exec, createTypeError(exec, "DataView constructor requires at least one argument."));
+
+ return JSValue::encode(ViewClass::create(exec, structure, 0));
+ }
+
+ JSValue firstValue = exec->uncheckedArgument(0);
+ unsigned offset = 0;
+ Optional<unsigned> length = Nullopt;
+ if (jsDynamicCast<JSArrayBuffer*>(firstValue) && argCount > 1) {
+ offset = exec->uncheckedArgument(1).toUInt32(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+
+ if (argCount > 2) {
+ length = exec->uncheckedArgument(2).toUInt32(exec);
+ if (exec->hadException())
+ return JSValue::encode(jsUndefined());
+ }
+
+ }
+
+ return JSValue::encode(constructGenericTypedArrayViewWithArguments<ViewClass>(exec, structure, JSValue::encode(firstValue), offset, length));
+}
+
+template<typename ViewClass>
ConstructType JSGenericTypedArrayViewConstructor<ViewClass>::getConstructData(JSCell*, ConstructData& constructData)
{
constructData.native.function = constructGenericTypedArrayView<ViewClass>;
Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h (191211 => 191212)
--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h 2015-10-16 21:25:52 UTC (rev 191211)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h 2015-10-16 21:40:21 UTC (rev 191212)
@@ -66,10 +66,6 @@
if (!exec->argumentCount())
return throwVMError(exec, createTypeError(exec, "Expected at least one argument"));
- JSObject* sourceArray = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0));
- if (!sourceArray)
- return throwVMError(exec, createTypeError(exec, "First argument should be an object"));
-
unsigned offset;
if (exec->argumentCount() >= 2) {
offset = exec->uncheckedArgument(1).toUInt32(exec);
@@ -78,7 +74,16 @@
} else
offset = 0;
- unsigned length = sourceArray->get(exec, exec->vm().propertyNames->length).toUInt32(exec);
+ JSObject* sourceArray = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0));
+ if (!sourceArray)
+ return throwVMError(exec, createTypeError(exec, "First argument should be an object"));
+
+ unsigned length;
+ if (isTypedView(sourceArray->classInfo()->typedArrayStorageType))
+ length = jsDynamicCast<JSArrayBufferView*>(sourceArray)->length();
+ else
+ length = sourceArray->get(exec, exec->vm().propertyNames->length).toUInt32(exec);
+
if (exec->hadException())
return JSValue::encode(jsUndefined());
@@ -283,7 +288,6 @@
return JSValue::encode(jsNumber(thisObject->byteOffset()));
}
-
template<typename ViewClass>
EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncReverse(ExecState* exec)
{
Added: trunk/Source/_javascript_Core/tests/stress/typedarray-construct-iterator.js (0 => 191212)
--- trunk/Source/_javascript_Core/tests/stress/typedarray-construct-iterator.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/typedarray-construct-iterator.js 2015-10-16 21:40:21 UTC (rev 191212)
@@ -0,0 +1,66 @@
+// Test a bunch of things about typed array constructors with iterators.
+
+// Test that the dfg actually respects iterators.
+let foo = [1,2,3,4];
+
+function iterator() {
+ return { i: 0,
+ next: function() {
+ if (this.i < foo.length/2) {
+ return { done: false,
+ value: foo[this.i++]
+ };
+ }
+ return { done: true };
+ }
+ };
+}
+
+foo[Symbol.iterator] = iterator;
+
+(function body() {
+
+ for (var i = 1; i < 100000; i++) {
+ if (new Int32Array(foo).length !== 2)
+ throw "iterator did not run";
+ }
+
+})();
+
+// Test that the optimizations used for iterators during construction is valid.
+
+foo = { 0:0, 1:1, 2:2, 3:3 };
+count = 4;
+foo.__defineGetter__("length", function() {
+ return count--;
+});
+
+foo[Symbol.iterator] = Array.prototype[Symbol.iterator];
+
+if (new Int32Array(foo).length !== 2)
+ throw "iterator did not run";
+
+// Test that we handle length is unset... whoops.
+
+foo = { 0:0, 2:2, 3:3 };
+
+if (new Int32Array(foo).length !== 0)
+ throw "did not handle object with unset length";
+
+// Test that we handle prototypes with accessors.
+
+foo = { 0:0, 2:2, 3:3 };
+foo[Symbol.iterator] = Array.prototype[Symbol.iterator];
+foo.length = 4;
+bar = { };
+
+bar.__defineGetter__("1", function() {
+ foo.length = 0;
+ return 1;
+});
+
+
+foo.__proto__ = bar;
+
+if (new Int32Array(foo).length !== 2)
+ throw "did not handle object with accessor on prototype";