Skip to content
Permalink
Browse files
[JSC] Improve resizable TypedArray's slice implementation's spec conf…
…ormance

https://bugs.webkit.org/show_bug.cgi?id=248337
rdar://problem/102661384

Reviewed by Ross Kirsling.

This patch fixes TypedArray#slice's spec conformance a bit: test length against previously passed value (not updatedLength).
We also import new V8 tests more.

* JSTests/stress/v8-regress-1376784.js: Added.
(test.const.factory.async foo):
(test.const.factory):
(test):
* JSTests/stress/v8-regress-1380398.js: Added.
(test):
* JSTests/stress/v8-regress-crbug-1359991.js: Added.
(MyFloat64Array):
* JSTests/stress/v8-regress-crbug-1362487.js: Added.
(MyInt8Array):
(assertThrows):
* JSTests/stress/v8-regress-crbug-1384474-variant2.js: Added.
(ta.Symbol.species):
(assertThrows):
* JSTests/stress/v8-regress-crbug-1384474-variant3.js: Added.
(ta.Symbol.species):
(assertThrows):
* JSTests/stress/v8-regress-crbug-1384474.js: Added.
(ta.Symbol.species):
(assertThrows):
* JSTests/stress/v8-regress-crbug-1392577.js: Added.
(start.valueOf):
* Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeFunctions.h:
(JSC::speciesConstruct):
(JSC::genericTypedArrayViewProtoFuncSlice):
(JSC::genericTypedArrayViewProtoFuncSubarray):

Canonical link: https://commits.webkit.org/257018@main
  • Loading branch information
Constellation committed Nov 25, 2022
1 parent 7560d94 commit c8c81173ec65dce5414985944e7f12e04a711308
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 19 deletions.
@@ -0,0 +1,36 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --allow-natives-syntax --turbofan

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

function test() {
let depth = 100;
const arr = [];
const factory = class MyArray extends Uint8Array {
constructor() {
async function foo() { new factory(); }
if(depth-- > 0) {
const x = foo();
super(arr);
this.__proto__ = x;
const unused1 = super.byteLength;
} else {
super(arr);
}
}
};
const unused2 = new factory();
arr.__proto__ = factory;
return arr;
}

test();
// %PrepareFunctionForOptimization(test);
test();
// %OptimizeFunctionOnNextCall(test);
test();
@@ -0,0 +1,23 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --allow-natives-syntax --turbofan --harmony-rab-gsab

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

function test() {
const ab = new ArrayBuffer(2996, { maxByteLength: 8588995 });
const dv = new DataView(ab);
const len = dv.byteLength;
return len >= 255;
}

// %PrepareFunctionForOptimization(test);
assertTrue(test());
assertTrue(test());
// %OptimizeFunctionOnNextCall(test);
assertTrue(test());
// assertOptimized(test);
@@ -0,0 +1,26 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Copyright 2022 Apple Inc. All rights reserved.
// Modified since original V8's test had an issue.

// Flags: --harmony-rab-gsab

"use strict";

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

const rab = new ArrayBuffer(1744, {"maxByteLength": 4000});
let callSlice = true;
class MyFloat64Array extends Float64Array {
constructor() {
super(rab);
if (callSlice) {
callSlice = false; // Prevent recursion
super.slice();
}
}
};
new MyFloat64Array();
@@ -0,0 +1,22 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Copyright 2022 Apple Inc. All rights reserved.
// Modified since original V8's test had an issue.

// Flags: --harmony-rab-gsab

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

const rab1 = new ArrayBuffer(1000, {'maxByteLength': 4000});
class MyInt8Array extends Int8Array {
constructor() {
super(rab1);
}
};
const rab2 = new ArrayBuffer(2000, {'maxByteLength': 4000});
const ta = new Int8Array(rab2);
ta.constructor = MyInt8Array;
assertThrows(() => { ta.slice(); }, TypeError);
@@ -0,0 +1,17 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --harmony-rab-gsab

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

const rab1 = new ArrayBuffer(4, {"maxByteLength": 100});
const ta = new Int8Array(rab1);
const rab2 = new ArrayBuffer(10, {"maxByteLength": 20});
const lengthTracking = new Int8Array(rab2);
rab2.resize(0);
ta.constructor = { [Symbol.species]: function() { return lengthTracking; } };
assertThrows(() => { ta.slice(); }, TypeError);
@@ -0,0 +1,18 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --harmony-rab-gsab

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

const rab1 = new ArrayBuffer(4, {"maxByteLength": 100});
const ta = new Int8Array(rab1);
const rab2 = new ArrayBuffer(10, {"maxByteLength": 20});
const lengthTracking = new Int8Array(rab2);
rab2.resize(0);
ta.constructor = { [Symbol.species]: function() { return lengthTracking; } };
assertThrows(() => { ta.filter(() => { return true; }); },
TypeError);
@@ -0,0 +1,16 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --harmony-rab-gsab

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

const ta = new Int8Array(4);
const rab = new ArrayBuffer(10, {"maxByteLength": 20});
const lengthTracking = new Int8Array(rab);
rab.resize(0);
ta.constructor = { [Symbol.species]: function() { return lengthTracking; } };
assertThrows(() => { ta.slice(); }, TypeError);
@@ -0,0 +1,18 @@
//@ requireOptions("--useResizableArrayBuffer=1")
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --harmony-rab-gsab

load("./resources/v8-mjsunit.js", "caller relative");
load("./resources/v8-typedarray-helpers.js", "caller relative");

const rab = new ArrayBuffer(50, {"maxByteLength": 100});
const ta = new Int8Array(rab);
const start = {};
start.valueOf = function() {
rab.resize(0);
return 5;
}
ta.fill(5, start);
@@ -92,7 +92,7 @@ ALWAYS_INLINE bool speciesWatchpointIsValid(JSGlobalObject* globalObject, ViewCl
// Note, that this function throws.
// https://tc39.es/ecma262/#typedarray-species-create
template<typename ViewClass, typename Functor, typename SlowPathArgsConstructor>
inline JSArrayBufferView* speciesConstruct(JSGlobalObject* globalObject, ViewClass* exemplar, const Functor& defaultConstructor, const SlowPathArgsConstructor& constructArgs)
inline JSArrayBufferView* speciesConstruct(JSGlobalObject* globalObject, ViewClass* exemplar, const Functor& defaultConstructor, const SlowPathArgsConstructor& constructArgs, std::optional<size_t> length)
{
VM& vm = getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
@@ -150,6 +150,23 @@ inline JSArrayBufferView* speciesConstruct(JSGlobalObject* globalObject, ViewCla
validateTypedArray(globalObject, view);
RETURN_IF_EXCEPTION(scope, nullptr);

// https://tc39.es/ecma262/#typedarray-create
// 3. If argumentList is a List of a single Number, then
// a. If newTypedArray.[[ArrayLength]] < R(argumentList[0]), throw a TypeError exception.
if (length) {
if (UNLIKELY(view->length() < length.value())) {
throwTypeError(globalObject, scope, "TypedArray.prototype.slice constructed typed array of insufficient length"_s);
return nullptr;
}
}

// https://tc39.es/ecma262/#typedarray-species-create
// If result.[[ContentType]] ≠ exemplar.[[ContentType]], throw a TypeError exception.
if (UNLIKELY(contentType(view->type()) != ViewClass::contentType)) {
throwTypeError(globalObject, scope, "Content types of source and created typed arrays are different"_s);
return nullptr;
}

return view;
}

@@ -838,14 +855,9 @@ ALWAYS_INLINE EncodedJSValue genericTypedArrayViewProtoFuncSlice(VM& vm, JSGloba
}, [&](MarkedArgumentBuffer& args) {
args.append(jsNumber(length));
ASSERT(!args.hasOverflowed());
});
}, length);
RETURN_IF_EXCEPTION(scope, { });

// https://tc39.es/ecma262/#typedarray-species-create
// If result.[[ContentType]] ≠ exemplar.[[ContentType]], throw a TypeError exception.
if (contentType(result->type()) != ViewClass::contentType)
return throwVMTypeError(globalObject, scope, "Content types of source and created typed arrays are different"_s);

// We return early here since we don't allocate a backing store if length is 0 and memmove does not like nullptrs
if (!length)
return JSValue::encode(result);
@@ -858,19 +870,15 @@ ALWAYS_INLINE EncodedJSValue genericTypedArrayViewProtoFuncSlice(VM& vm, JSGloba
end = std::min(updatedLength.value(), end);
}

// Clamp end to begin, again.
end = std::max(begin, end);
ASSERT(end >= begin);
length = end - begin;

// The species constructor may return an array with any arbitrary length.
if (result->length() < length)
return throwVMTypeError(globalObject, scope, "TypedArray.prototype.slice constructed typed array of insufficient length"_s);

// If length is zero, begin and end can point at random places (because of resize). We should avoid calling `set`, and return early here.
if (!length)
// It is possible that |begin| becomes larger than |end| at this point. In this case, we do nothing.
if (begin >= end)
return JSValue::encode(result);

ASSERT(end > begin);
// This length is always smaller than the previous length.
length = end - begin;
ASSERT(result->length() >= length);

switch (result->type()) {
case Int8ArrayType:
scope.release();
@@ -981,7 +989,7 @@ ALWAYS_INLINE EncodedJSValue genericTypedArrayViewProtoFuncSubarray(VM& vm, JSGl
if (count)
args.append(jsNumber(count.value()));
ASSERT(!args.hasOverflowed());
}));
}, std::nullopt));
}

template<typename ViewClass>

0 comments on commit c8c8117

Please sign in to comment.