Bug 1520286 - Part 2: Add VM-call for TypedArray constructor with Array/TypedArray arguments. r=jandem

--HG--
extra : rebase_source : 899acfc898773aec0a1b3b69aeb1aae07958cdc6
This commit is contained in:
André Bargull 2019-02-01 05:14:41 -08:00
Родитель 24cd46ac83
Коммит 2ee80e7ac9
9 изменённых файлов: 403 добавлений и 72 удалений

Просмотреть файл

@ -0,0 +1,165 @@
// Test TypedArray constructor when called with iterables or typed arrays.
function testPackedArray() {
function test() {
var array = [
1, 2, 3,
4, 5, 6,
7, 8, 9,
];
for (var i = 0; i < 1000; ++i) {
var ta = new Int32Array(array);
assertEq(ta.length, array.length);
for (var j = 0; j < array.length; ++j) {
assertEq(ta[j], array[j]);
}
}
}
for (var i = 0; i < 2; ++i) {
test();
}
}
testPackedArray();
function testHoleArray() {
function test() {
var array = [
1, /* hole */, 3,
4, /* hole */, 6,
7, /* hole */, 9,
];
for (var i = 0; i < 1000; ++i) {
var ta = new Int32Array(array);
assertEq(ta.length, array.length);
for (var j = 0; j < array.length; ++j) {
assertEq(ta[j], array[j] || 0);
}
}
}
for (var i = 0; i < 2; ++i) {
test();
}
}
testHoleArray();
function testTypedArraySameType() {
function test() {
var array = new Int32Array([
1, 2, 3,
4, 5, 6,
7, 8, 9,
]);
for (var i = 0; i < 1000; ++i) {
var ta = new Int32Array(array);
assertEq(ta.length, array.length);
for (var j = 0; j < array.length; ++j) {
assertEq(ta[j], array[j]);
}
}
}
for (var i = 0; i < 2; ++i) {
test();
}
}
testTypedArraySameType();
function testTypedArrayDifferentType() {
function test() {
var array = new Float32Array([
1, 2, 3,
4, 5, 6,
7, 8, 9,
]);
for (var i = 0; i < 1000; ++i) {
var ta = new Int32Array(array);
assertEq(ta.length, array.length);
for (var j = 0; j < array.length; ++j) {
assertEq(ta[j], array[j]);
}
}
}
for (var i = 0; i < 2; ++i) {
test();
}
}
testTypedArrayDifferentType();
function testIterable() {
function test() {
var array = [
1, 2, 3,
4, 5, 6,
7, 8, 9,
];
array = Object.defineProperties({
[Symbol.iterator]() {
var index = 0;
return {
next() {
var done = index >= array.length;
var value = !done ? array[index++] : undefined;
return {done, value};
}
};
}
}, Object.getOwnPropertyDescriptors(array));
for (var i = 0; i < 1000; ++i) {
var ta = new Int32Array(array);
assertEq(ta.length, array.length);
for (var j = 0; j < array.length; ++j) {
assertEq(ta[j], array[j]);
}
}
}
for (var i = 0; i < 2; ++i) {
test();
}
}
testIterable();
function testWrappedArray() {
var g = newGlobal();
function test() {
var array = new g.Array(
1, 2, 3,
4, 5, 6,
7, 8, 9,
);
for (var i = 0; i < 1000; ++i) {
var ta = new Int32Array(array);
assertEq(ta.length, array.length);
for (var j = 0; j < array.length; ++j) {
assertEq(ta[j], array[j]);
}
}
}
for (var i = 0; i < 2; ++i) {
test();
}
}
testWrappedArray();
function testWrappedTypedArray() {
var g = newGlobal();
function test() {
var array = new g.Int32Array([
1, 2, 3,
4, 5, 6,
7, 8, 9,
]);
for (var i = 0; i < 1000; ++i) {
var ta = new Int32Array(array);
assertEq(ta.length, array.length);
for (var j = 0; j < array.length; ++j) {
assertEq(ta[j], array[j]);
}
}
}
for (var i = 0; i < 2; ++i) {
test();
}
}
testWrappedTypedArray();

Просмотреть файл

@ -3382,12 +3382,8 @@ static bool GetTemplateObjectForNative(JSContext* cx, HandleFunction target,
return true;
}
size_t len = 0;
if (args[0].isInt32() && args[0].toInt32() >= 0) {
len = args[0].toInt32();
}
return TypedArrayObject::GetTemplateObjectForNative(cx, target->native(),
len, res);
args[0], res);
}
default:

Просмотреть файл

@ -6318,6 +6318,19 @@ void CodeGenerator::visitNewTypedArrayDynamicLength(
masm.bind(ool->rejoin());
}
typedef TypedArrayObject* (*TypedArrayCreateWithTemplateFn)(JSContext*,
HandleObject,
HandleObject);
static const VMFunction TypedArrayCreateWithTemplateInfo =
FunctionInfo<TypedArrayCreateWithTemplateFn>(
js::TypedArrayCreateWithTemplate, "TypedArrayCreateWithTemplate");
void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) {
pushArg(ToRegister(lir->array()));
pushArg(ImmGCPtr(lir->mir()->templateObject()));
callVM(TypedArrayCreateWithTemplateInfo, lir);
}
// Out-of-line object allocation for JSOP_NEWOBJECT.
class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator> {
LNewObject* lir_;

Просмотреть файл

@ -202,6 +202,15 @@ void LIRGenerator::visitNewTypedArrayDynamicLength(
assignSafepoint(lir, ins);
}
void LIRGenerator::visitNewTypedArrayFromArray(MNewTypedArrayFromArray* ins) {
MDefinition* array = ins->array();
MOZ_ASSERT(array->type() == MIRType::Object);
auto* lir = new (alloc()) LNewTypedArrayFromArray(useRegisterAtStart(array));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
}
void LIRGenerator::visitNewObject(MNewObject* ins) {
LNewObject* lir = new (alloc()) LNewObject(temp());
define(lir, ins);

Просмотреть файл

@ -2931,19 +2931,11 @@ IonBuilder::InliningResult IonBuilder::inlineTypedArray(CallInfo& callInfo,
return InliningStatus_NotInlined;
}
MDefinition* arg = callInfo.getArg(0);
if (arg->type() != MIRType::Int32) {
return InliningStatus_NotInlined;
}
JSObject* templateObject = inspector->getTemplateObjectForNative(pc, native);
if (!templateObject) {
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeNoTemplateObj);
return InliningStatus_NotInlined;
}
MOZ_ASSERT(templateObject->is<TypedArrayObject>());
TypedArrayObject* obj = &templateObject->as<TypedArrayObject>();
@ -2953,35 +2945,59 @@ IonBuilder::InliningResult IonBuilder::inlineTypedArray(CallInfo& callInfo,
return InliningStatus_NotInlined;
}
MInstruction* ins = nullptr;
MDefinition* arg = callInfo.getArg(0);
MInstruction* ins;
if (arg->type() == MIRType::Int32) {
if (!arg->isConstant()) {
ins = MNewTypedArrayDynamicLength::New(
alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()), arg);
} else {
// Negative lengths must throw a RangeError. (We don't track that this
// might have previously thrown, when determining whether to inline, so we
// have to deal with this error case when inlining.)
int32_t providedLen = arg->maybeConstantValue()->toInt32();
if (providedLen <= 0) {
return InliningStatus_NotInlined;
}
if (!arg->isConstant()) {
callInfo.setImplicitlyUsedUnchecked();
ins = MNewTypedArrayDynamicLength::New(
uint32_t len = AssertedCast<uint32_t>(providedLen);
if (obj->length() != len) {
return InliningStatus_NotInlined;
}
MConstant* templateConst =
MConstant::NewConstraintlessObject(alloc(), obj);
current->add(templateConst);
ins = MNewTypedArray::New(alloc(), constraints(), templateConst,
obj->group()->initialHeap(constraints()));
}
} else if (arg->type() == MIRType::Object) {
TemporaryTypeSet* types = arg->resultTypeSet();
if (!types) {
return InliningStatus_NotInlined;
}
// Don't inline if the argument is a, possibly wrapped, ArrayBuffer or
// SharedArrayBuffer object.
auto IsPossiblyWrappedArrayBufferMaybeSharedClass = [](const Class* clasp) {
return clasp->isProxy() || clasp == &ArrayBufferObject::class_ ||
clasp == &SharedArrayBufferObject::class_;
};
auto result = types->forAllClasses(
constraints(), IsPossiblyWrappedArrayBufferMaybeSharedClass);
if (result != TemporaryTypeSet::ForAllResult::ALL_FALSE) {
return InliningStatus_NotInlined;
}
ins = MNewTypedArrayFromArray::New(
alloc(), constraints(), templateObject,
templateObject->group()->initialHeap(constraints()), arg);
} else {
// Negative lengths must throw a RangeError. (We don't track that this
// might have previously thrown, when determining whether to inline, so we
// have to deal with this error case when inlining.)
int32_t providedLen = arg->maybeConstantValue()->toInt32();
if (providedLen <= 0) {
return InliningStatus_NotInlined;
}
uint32_t len = AssertedCast<uint32_t>(providedLen);
if (obj->length() != len) {
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), obj);
current->add(templateConst);
ins = MNewTypedArray::New(alloc(), constraints(), templateConst,
obj->group()->initialHeap(constraints()));
return InliningStatus_NotInlined;
}
callInfo.setImplicitlyUsedUnchecked();
current->add(ins);
current->push(ins);
MOZ_TRY(resumeAfter(ins));

Просмотреть файл

@ -2138,12 +2138,10 @@ class MNewTypedArrayDynamicLength : public MUnaryInstruction,
: MUnaryInstruction(classOpcode, length),
templateObject_(templateObject),
initialHeap_(initialHeap) {
MOZ_ASSERT(!templateObject->isSingleton());
setGuard(); // Need to throw if length is negative.
setResultType(MIRType::Object);
if (!templateObject->isSingleton()) {
setResultTypeSet(
MakeSingletonTypeSet(alloc, constraints, templateObject));
}
setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
}
public:
@ -2161,6 +2159,40 @@ class MNewTypedArrayDynamicLength : public MUnaryInstruction,
}
};
// Create a new TypedArray from an Array (or Array-like object) or a TypedArray.
class MNewTypedArrayFromArray : public MUnaryInstruction,
public SingleObjectPolicy::Data {
CompilerObject templateObject_;
gc::InitialHeap initialHeap_;
MNewTypedArrayFromArray(TempAllocator& alloc,
CompilerConstraintList* constraints,
JSObject* templateObject, gc::InitialHeap initialHeap,
MDefinition* array)
: MUnaryInstruction(classOpcode, array),
templateObject_(templateObject),
initialHeap_(initialHeap) {
MOZ_ASSERT(!templateObject->isSingleton());
setGuard(); // Can throw during construction.
setResultType(MIRType::Object);
setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
}
public:
INSTRUCTION_HEADER(NewTypedArrayFromArray)
TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
MDefinition* array() const { return getOperand(0); }
JSObject* templateObject() const { return templateObject_; }
gc::InitialHeap initialHeap() const { return initialHeap_; }
bool appendRoots(MRootList& roots) const override {
return roots.append(templateObject_);
}
bool possiblyCalls() const override { return true; }
};
class MNewObject : public MUnaryInstruction, public NoTypePolicy::Data {
public:
enum Mode { ObjectLiteral, ObjectCreate };

Просмотреть файл

@ -386,6 +386,22 @@ class LNewTypedArrayDynamicLength : public LInstructionHelper<1, 1, 1> {
}
};
class LNewTypedArrayFromArray : public LCallInstructionHelper<1, 1, 0> {
public:
LIR_HEADER(NewTypedArrayFromArray)
explicit LNewTypedArrayFromArray(const LAllocation& array)
: LCallInstructionHelper(classOpcode) {
setOperand(0, array);
}
const LAllocation* array() { return getOperand(0); }
MNewTypedArrayFromArray* mir() const {
return mir_->toNewTypedArrayFromArray();
}
};
class LNewObject : public LInstructionHelper<1, 0, 1> {
public:
LIR_HEADER(NewObject)

Просмотреть файл

@ -382,11 +382,26 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
static TypedArrayObject* makeTypedInstance(JSContext* cx,
CreateSingleton createSingleton,
HandleObjectGroup group,
gc::AllocKind allocKind) {
if (createSingleton == CreateSingleton::Yes) {
MOZ_ASSERT(!group);
return newBuiltinClassInstance(cx, allocKind, SingletonObject);
}
if (group) {
MOZ_ASSERT(group->clasp() == instanceClass());
NewObjectKind newKind = GenericObject;
{
AutoSweepObjectGroup sweep(group);
if (group->shouldPreTenure(sweep)) {
newKind = TenuredObject;
}
}
return NewObjectWithGroup<TypedArrayObject>(cx, group, allocKind,
newKind);
}
jsbytecode* pc;
RootedScript script(cx, cx->currentScript(&pc));
Rooted<TypedArrayObject*> obj(
@ -406,7 +421,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
static TypedArrayObject* makeInstance(
JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
CreateSingleton createSingleton, uint32_t byteOffset, uint32_t len,
HandleObject proto) {
HandleObject proto, HandleObjectGroup group = nullptr) {
MOZ_ASSERT(len < INT32_MAX / BYTES_PER_ELEMENT);
gc::AllocKind allocKind =
@ -427,9 +442,10 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
AutoSetNewObjectMetadata metadata(cx);
Rooted<TypedArrayObject*> obj(cx);
if (proto && proto != checkProto) {
MOZ_ASSERT(!group);
obj = makeProtoInstance(cx, proto, allocKind);
} else {
obj = makeTypedInstance(cx, createSingleton, allocKind);
obj = makeTypedInstance(cx, createSingleton, group, allocKind);
}
if (!obj || !obj->init(cx, buffer, byteOffset, len, BYTES_PER_ELEMENT)) {
return nullptr;
@ -446,6 +462,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
bool fitsInline = nbytes <= INLINE_BUFFER_LIMIT;
gc::AllocKind allocKind = !fitsInline ? gc::GetGCObjectKind(instanceClass())
: AllocKindForLazyBuffer(nbytes);
MOZ_ASSERT(allocKind >= gc::GetGCObjectKind(instanceClass()));
AutoSetNewObjectMetadata metadata(cx);
jsbytecode* pc;
@ -552,6 +569,15 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
return obj;
}
static TypedArrayObject* makeTypedArrayWithTemplate(
JSContext* cx, TypedArrayObject* templateObj, HandleObject array) {
MOZ_ASSERT(!IsWrapper(array));
MOZ_ASSERT(!array->is<ArrayBufferObjectMaybeShared>());
RootedObjectGroup group(cx, templateObj->group());
return fromArray(cx, array, nullptr, group);
}
// ES2018 draft rev 8340bf9a8427ea81bb0d1459471afbcc91d18add
// 22.2.4.1 TypedArray ( )
// 22.2.4.2 TypedArray ( length )
@ -881,14 +907,17 @@ class TypedArrayObjectTemplate : public TypedArrayObject {
uint32_t count,
MutableHandle<ArrayBufferObject*> buffer);
static JSObject* fromArray(JSContext* cx, HandleObject other,
HandleObject proto = nullptr);
static TypedArrayObject* fromArray(JSContext* cx, HandleObject other,
HandleObject proto = nullptr,
HandleObjectGroup group = nullptr);
static JSObject* fromTypedArray(JSContext* cx, HandleObject other,
bool isWrapped, HandleObject proto);
static TypedArrayObject* fromTypedArray(JSContext* cx, HandleObject other,
bool isWrapped, HandleObject proto,
HandleObjectGroup group);
static JSObject* fromObject(JSContext* cx, HandleObject other,
HandleObject proto);
static TypedArrayObject* fromObject(JSContext* cx, HandleObject other,
HandleObject proto,
HandleObjectGroup group);
static const NativeType getIndex(TypedArrayObject* tarray, uint32_t index) {
MOZ_ASSERT(index < tarray->length());
@ -931,6 +960,24 @@ TypedArrayObject* js::TypedArrayCreateWithTemplate(JSContext* cx,
}
}
TypedArrayObject* js::TypedArrayCreateWithTemplate(JSContext* cx,
HandleObject templateObj,
HandleObject array) {
MOZ_ASSERT(templateObj->is<TypedArrayObject>());
TypedArrayObject* tobj = &templateObj->as<TypedArrayObject>();
switch (tobj->type()) {
#define CREATE_TYPED_ARRAY(T, N) \
case Scalar::N: \
return TypedArrayObjectTemplate<T>::makeTypedArrayWithTemplate(cx, tobj, \
array);
JS_FOR_EACH_TYPED_ARRAY(CREATE_TYPED_ARRAY)
#undef CREATE_TYPED_ARRAY
default:
MOZ_CRASH("Unsupported TypedArray type");
}
}
// ES2018 draft rev 2aea8f3e617b49df06414eb062ab44fad87661d3
// 24.1.1.1 AllocateArrayBuffer ( constructor, byteLength )
// byteLength = count * BYTES_PER_ELEMENT
@ -1025,27 +1072,29 @@ static JSObject* GetBufferSpeciesConstructor(
}
template <typename T>
/* static */ JSObject* TypedArrayObjectTemplate<T>::fromArray(
JSContext* cx, HandleObject other, HandleObject proto /* = nullptr */) {
/* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromArray(
JSContext* cx, HandleObject other, HandleObject proto /* = nullptr */,
HandleObjectGroup group /* = nullptr */) {
// Allow nullptr proto for FriendAPI methods, which don't care about
// subclassing.
if (other->is<TypedArrayObject>()) {
return fromTypedArray(cx, other, /* wrapped= */ false, proto);
return fromTypedArray(cx, other, /* wrapped= */ false, proto, group);
}
if (other->is<WrapperObject>() &&
UncheckedUnwrap(other)->is<TypedArrayObject>()) {
return fromTypedArray(cx, other, /* wrapped= */ true, proto);
return fromTypedArray(cx, other, /* wrapped= */ true, proto, group);
}
return fromObject(cx, other, proto);
return fromObject(cx, other, proto, group);
}
// ES2018 draft rev 272beb67bc5cd9fd18a220665198384108208ee1
// 22.2.4.3 TypedArray ( typedArray )
template <typename T>
/* static */ JSObject* TypedArrayObjectTemplate<T>::fromTypedArray(
JSContext* cx, HandleObject other, bool isWrapped, HandleObject proto) {
/* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromTypedArray(
JSContext* cx, HandleObject other, bool isWrapped, HandleObject proto,
HandleObjectGroup group) {
// Step 1.
MOZ_ASSERT_IF(!isWrapped, other->is<TypedArrayObject>());
MOZ_ASSERT_IF(isWrapped, other->is<WrapperObject>() &&
@ -1123,8 +1172,8 @@ template <typename T>
// Steps 3-4 (remaining part), 20-23.
Rooted<TypedArrayObject*> obj(
cx,
makeInstance(cx, buffer, CreateSingleton::No, 0, elementLength, proto));
cx, makeInstance(cx, buffer, CreateSingleton::No, 0, elementLength, proto,
group));
if (!obj) {
return nullptr;
}
@ -1165,8 +1214,9 @@ static MOZ_ALWAYS_INLINE bool IsOptimizableInit(JSContext* cx,
// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e
// 22.2.4.4 TypedArray ( object )
template <typename T>
/* static */ JSObject* TypedArrayObjectTemplate<T>::fromObject(
JSContext* cx, HandleObject other, HandleObject proto) {
/* static */ TypedArrayObject* TypedArrayObjectTemplate<T>::fromObject(
JSContext* cx, HandleObject other, HandleObject proto,
HandleObjectGroup group) {
// Steps 1-2 (Already performed in caller).
// Steps 3-4 (Allocation deferred until later).
@ -1191,7 +1241,8 @@ template <typename T>
}
Rooted<TypedArrayObject*> obj(
cx, makeInstance(cx, buffer, CreateSingleton::No, 0, len, proto));
cx,
makeInstance(cx, buffer, CreateSingleton::No, 0, len, proto, group));
if (!obj) {
return nullptr;
}
@ -1266,7 +1317,7 @@ template <typename T>
}
Rooted<TypedArrayObject*> obj(
cx, makeInstance(cx, buffer, CreateSingleton::No, 0, len, proto));
cx, makeInstance(cx, buffer, CreateSingleton::No, 0, len, proto, group));
if (!obj) {
return nullptr;
}
@ -1290,18 +1341,46 @@ bool TypedArrayConstructor(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
template <typename T>
static bool GetTemplateObjectForNative(JSContext* cx, HandleValue arg,
MutableHandleObject res) {
if (arg.isInt32()) {
uint32_t len = 0;
if (arg.toInt32() >= 0) {
len = arg.toInt32();
}
size_t nbytes;
if (!js::CalculateAllocSize<T>(len, &nbytes)) {
return true;
}
if (nbytes >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
return true;
}
res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len));
return !!res;
}
if (arg.isObject() && !IsWrapper(&arg.toObject()) &&
!arg.toObject().is<ArrayBufferObjectMaybeShared>()) {
// We don't use the template's length in the object case, so we can create
// the template typed array with an initial length of zero.
uint32_t len = 0;
res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len));
return !!res;
}
return true;
}
/* static */ bool TypedArrayObject::GetTemplateObjectForNative(
JSContext* cx, Native native, uint32_t len, MutableHandleObject res) {
JSContext* cx, Native native, HandleValue arg, MutableHandleObject res) {
MOZ_ASSERT(!res);
#define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N) \
if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
size_t nbytes; \
if (!js::CalculateAllocSize<T>(len, &nbytes)) return true; \
\
if (nbytes < TypedArrayObject::SINGLETON_BYTE_LENGTH) { \
res.set(TypedArrayObjectTemplate<T>::makeTemplateObject(cx, len)); \
return !!res; \
} \
#define CHECK_TYPED_ARRAY_CONSTRUCTOR(T, N) \
if (native == &TypedArrayObjectTemplate<T>::class_constructor) { \
return ::GetTemplateObjectForNative<T>(cx, arg, res); \
}
JS_FOR_EACH_TYPED_ARRAY(CHECK_TYPED_ARRAY_CONSTRUCTOR)
#undef CHECK_TYPED_ARRAY_CONSTRUCTOR

Просмотреть файл

@ -136,7 +136,8 @@ class TypedArrayObject : public ArrayBufferViewObject {
void getElements(Value* vp);
static bool GetTemplateObjectForNative(JSContext* cx, Native native,
uint32_t len, MutableHandleObject res);
HandleValue arg,
MutableHandleObject res);
/*
* Byte length above which created typed arrays will have singleton types
@ -191,6 +192,10 @@ extern TypedArrayObject* TypedArrayCreateWithTemplate(JSContext* cx,
HandleObject templateObj,
int32_t len);
extern TypedArrayObject* TypedArrayCreateWithTemplate(JSContext* cx,
HandleObject templateObj,
HandleObject array);
inline bool IsTypedArrayClass(const Class* clasp) {
return &TypedArrayObject::classes[0] <= clasp &&
clasp < &TypedArrayObject::classes[Scalar::MaxTypedArrayViewType];