Commit 47aa7b77 authored by Choongwoo Han's avatar Choongwoo Han Committed by Commit Bot

[typedarray] Reimplement TA.p.subarray as CSA

- Remove TypedArray.prototype.subarray in js/typedarray.js
- Implement TypedArray.prototype.subarray as a CSA
- Implement TypedArraySpeciesCreateByArrayBuffer as a CSA
- Move a helper function for relative index from builtins-string-gec.cc
 to code-stub-assembler.cc
- Move SpeciesConstructor from builtins-promise-gen.cc to
 code-stub-assembler.cc

Bug: v8:7161, v8:5929
Change-Id: If3340476e16aa21659540eb4b24e3ead54e6a313
Reviewed-on: https://chromium-review.googlesource.com/830992
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50831}
parent d7fda252
......@@ -3097,6 +3097,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kTypedArrayPrototypeSlice, 2, false);
SimpleInstallFunction(prototype, "some", Builtins::kTypedArrayPrototypeSome,
1, false);
SimpleInstallFunction(prototype, "subarray",
Builtins::kTypedArrayPrototypeSubArray, 2, false);
}
{ // -- T y p e d A r r a y s
......
......@@ -1105,6 +1105,9 @@ namespace internal {
TFJ(TypedArrayPrototypeSet, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-%typedarray%.prototype.slice */ \
CPP(TypedArrayPrototypeSlice) \
/* ES6 %TypedArray%.prototype.subarray */ \
TFJ(TypedArrayPrototypeSubArray, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-get-%typedarray%.prototype-@@tostringtag */ \
TFJ(TypedArrayPrototypeToStringTag, 0) \
/* ES6 %TypedArray%.prototype.every */ \
......
......@@ -73,6 +73,27 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
// Returns the byte size of an element for a TypedArray elements kind.
TNode<IntPtrT> GetTypedArrayElementSize(TNode<Word32T> elements_kind);
TNode<Object> GetDefaultConstructor(TNode<Context> context,
TNode<JSTypedArray> exemplar);
TNode<Object> TypedArraySpeciesConstructor(TNode<Context> context,
TNode<JSTypedArray> exemplar,
TNode<Object> default_constructor);
TNode<JSTypedArray> SpeciesCreateByArrayBuffer(TNode<Context> context,
TNode<JSTypedArray> exemplar,
TNode<JSArrayBuffer> buffer,
TNode<Number> byte_offset,
TNode<Smi> len,
const char* method_name);
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
TNode<JSTypedArray> array);
TNode<JSTypedArray> ValidateTypedArray(TNode<Context> context,
TNode<Object> obj,
const char* method_name);
// Fast path for setting a TypedArray (source) onto another TypedArray
// (target) at an element offset.
void SetTypedArraySource(TNode<Context> context, TNode<JSTypedArray> source,
......@@ -937,6 +958,135 @@ TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize(
return element_size;
}
TNode<Object> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
TNode<Context> context, TNode<JSTypedArray> exemplar) {
TVARIABLE(IntPtrT, context_slot);
TNode<Word32T> elements_kind = LoadElementsKind(exemplar);
Label next(this), constructor_found(this),
if_unknown_type(this, Label::kDeferred);
size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND -
FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND +
1;
int32_t elements_kinds[kTypedElementsKindCount] = {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) TYPE##_ELEMENTS,
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
};
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
Label if_##type##array(this);
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
Label* elements_kind_labels[kTypedElementsKindCount] = {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) &if_##type##array,
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
};
Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels,
kTypedElementsKindCount);
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
BIND(&if_##type##array); \
{ \
context_slot = IntPtrConstant(Context::TYPE##_ARRAY_FUN_INDEX); \
Goto(&next); \
}
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
BIND(&if_unknown_type);
Unreachable();
BIND(&next);
return LoadContextElement(LoadNativeContext(context), context_slot);
}
TNode<Object> TypedArrayBuiltinsAssembler::TypedArraySpeciesConstructor(
TNode<Context> context, TNode<JSTypedArray> exemplar,
TNode<Object> default_constructor) {
TVARIABLE(Object, var_constructor);
Label slow(this), done(this);
var_constructor = default_constructor;
Node* map = LoadMap(exemplar);
GotoIfNot(IsPrototypeTypedArrayPrototype(context, map), &slow);
Branch(IsSpeciesProtectorCellInvalid(), &slow, &done);
BIND(&slow);
var_constructor =
CAST(SpeciesConstructor(context, exemplar, default_constructor));
Goto(&done);
BIND(&done);
return var_constructor;
}
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::SpeciesCreateByArrayBuffer(
TNode<Context> context, TNode<JSTypedArray> exemplar,
TNode<JSArrayBuffer> buffer, TNode<Number> byte_offset, TNode<Smi> len,
const char* method_name) {
// Let defaultConstructor be the intrinsic object listed in column one of
// Table 52 for exemplar.[[TypedArrayName]].
TNode<Object> default_constructor = GetDefaultConstructor(context, exemplar);
// Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
TNode<Object> constructor =
TypedArraySpeciesConstructor(context, exemplar, default_constructor);
// Let newTypedArray be ? Construct(constructor, argumentList).
TNode<Object> new_object =
CAST(ConstructJS(CodeFactory::Construct(isolate()), context, constructor,
buffer, byte_offset, len));
// Perform ? ValidateTypedArray(newTypedArray).
return ValidateTypedArray(context, new_object, method_name);
}
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
TNode<Context> context, TNode<JSTypedArray> array) {
Label call_runtime(this), done(this);
TVARIABLE(Object, var_result);
TNode<Object> buffer = LoadObjectField(array, JSTypedArray::kBufferOffset);
GotoIf(IsDetachedBuffer(buffer), &call_runtime);
TNode<UintPtrT> backing_store = LoadObjectField<UintPtrT>(
CAST(buffer), JSArrayBuffer::kBackingStoreOffset);
GotoIf(WordEqual(backing_store, IntPtrConstant(0)), &call_runtime);
var_result = buffer;
Goto(&done);
BIND(&call_runtime);
{
var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array);
Goto(&done);
}
BIND(&done);
return CAST(var_result);
}
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::ValidateTypedArray(
TNode<Context> context, TNode<Object> obj, const char* method_name) {
Label validation_done(this);
// If it is not a typed array, throw
ThrowIfNotInstanceType(context, obj, JS_TYPED_ARRAY_TYPE, method_name);
// If the typed array's buffer is detached, throw
TNode<Object> buffer =
LoadObjectField(CAST(obj), JSTypedArray::kBufferOffset);
GotoIfNot(IsDetachedBuffer(buffer), &validation_done);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
BIND(&validation_done);
return CAST(obj);
}
void TypedArrayBuiltinsAssembler::SetTypedArraySource(
TNode<Context> context, TNode<JSTypedArray> source,
TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime,
......@@ -1175,6 +1325,75 @@ TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
ThrowTypeError(context, MessageTemplate::kNotTypedArray);
}
// ES %TypedArray%.prototype.subarray
TF_BUILTIN(TypedArrayPrototypeSubArray, TypedArrayBuiltinsAssembler) {
const char* method_name = "%TypedArray%.prototype.subarray";
Label offset_done(this);
VARIABLE(var_begin, MachineRepresentation::kTagged);
VARIABLE(var_end, MachineRepresentation::kTagged);
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
CodeStubArguments args(
this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
// 1. Let O be the this value.
// 3. If O does not have a [[TypedArrayName]] internal slot, throw a TypeError
// exception.
TNode<Object> receiver = args.GetReceiver();
ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, method_name);
TNode<JSTypedArray> source = CAST(receiver);
// 5. Let buffer be O.[[ViewedArrayBuffer]].
TNode<JSArrayBuffer> buffer = GetBuffer(context, source);
// 6. Let srcLength be O.[[ArrayLength]].
TNode<Smi> source_length =
LoadObjectField<Smi>(source, JSTypedArray::kLengthOffset);
// 7. Let relativeBegin be ? ToInteger(begin).
// 8. If relativeBegin < 0, let beginIndex be max((srcLength + relativeBegin),
// 0); else let beginIndex be min(relativeBegin, srcLength).
TNode<Object> begin = args.GetOptionalArgumentValue(0, SmiConstant(0));
ConvertToRelativeIndex(context, &var_begin, begin, source_length);
TNode<Object> end = args.GetOptionalArgumentValue(1, UndefinedConstant());
// 9. If end is undefined, let relativeEnd be srcLength;
var_end.Bind(source_length);
GotoIf(IsUndefined(end), &offset_done);
// else, let relativeEnd be ? ToInteger(end).
// 10. If relativeEnd < 0, let endIndex be max((srcLength + relativeEnd), 0);
// else let endIndex be min(relativeEnd, srcLength).
ConvertToRelativeIndex(context, &var_end, end, source_length);
Goto(&offset_done);
BIND(&offset_done);
// 11. Let newLength be max(endIndex - beginIndex, 0).
TNode<Smi> new_length =
SmiMax(SmiSub(var_end.value(), var_begin.value()), SmiConstant(0));
// 12. Let constructorName be the String value of O.[[TypedArrayName]].
// 13. Let elementSize be the Number value of the Element Size value specified
// in Table 52 for constructorName.
TNode<Word32T> element_kind = LoadElementsKind(source);
TNode<IntPtrT> element_size = GetTypedArrayElementSize(element_kind);
// 14. Let srcByteOffset be O.[[ByteOffset]].
TNode<Number> source_byte_offset =
LoadObjectField<Number>(source, JSTypedArray::kByteOffsetOffset);
// 15. Let beginByteOffset be srcByteOffset + beginIndex × elementSize.
TNode<Number> offset = SmiMul(var_begin.value(), SmiFromWord(element_size));
TNode<Number> begin_byte_offset = CAST(NumberAdd(source_byte_offset, offset));
// 16. Let argumentsList be « buffer, beginByteOffset, newLength ».
// 17. Return ? TypedArraySpeciesCreate(O, argumentsList).
args.PopAndReturn(SpeciesCreateByArrayBuffer(
context, source, buffer, begin_byte_offset, new_length, method_name));
}
// ES #sec-get-%typedarray%.prototype-@@tostringtag
TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) {
Node* receiver = Parameter(Descriptor::kReceiver);
......
......@@ -581,6 +581,43 @@ TNode<Object> CodeStubAssembler::NumberMin(SloppyTNode<Object> a,
return TNode<Object>::UncheckedCast(result.value());
}
void CodeStubAssembler::ConvertToRelativeIndex(Node* context,
Variable* var_result,
Node* index, Node* length) {
TNode<Object> const index_int =
ToInteger(context, index, CodeStubAssembler::kTruncateMinusZero);
TNode<Smi> const zero = SmiConstant(0);
Label done(this);
Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
Branch(TaggedIsSmi(index_int), &if_issmi, &if_isheapnumber);
BIND(&if_issmi);
{
TNode<Smi> const start_int_smi = CAST(index_int);
var_result->Bind(
Select(SmiLessThan(start_int_smi, zero),
[&] { return SmiMax(SmiAdd(length, start_int_smi), zero); },
[&] { return SmiMin(start_int_smi, length); },
MachineRepresentation::kTagged));
Goto(&done);
}
BIND(&if_isheapnumber);
{
// If {index} is a heap number, it is definitely out of bounds. If it is
// negative, {index} = max({length} + {index}),0) = 0'. If it is positive,
// set {index} to {length}.
TNode<HeapNumber> const start_int_hn = CAST(index_int);
TNode<Float64T> const float_zero = Float64Constant(0.);
TNode<Float64T> const start_float = LoadHeapNumberValue(start_int_hn);
var_result->Bind(SelectTaggedConstant<Smi>(
Float64LessThan(start_float, float_zero), zero, length));
Goto(&done);
}
BIND(&done);
}
Node* CodeStubAssembler::SmiMod(Node* a, Node* b) {
VARIABLE(var_result, MachineRepresentation::kTagged);
Label return_result(this, &var_result),
......@@ -4090,6 +4127,18 @@ Node* CodeStubAssembler::IsPrototypeInitialArrayPrototype(Node* context,
return WordEqual(proto, initial_array_prototype);
}
TNode<BoolT> CodeStubAssembler::IsPrototypeTypedArrayPrototype(
SloppyTNode<Context> context, SloppyTNode<Map> map) {
TNode<Context> const native_context = LoadNativeContext(context);
TNode<Object> const typed_array_prototype =
LoadContextElement(native_context, Context::TYPED_ARRAY_PROTOTYPE_INDEX);
TNode<Object> proto = LoadMapPrototype(map);
TNode<Object> proto_of_proto = Select<Object>(
IsJSObject(proto), [=] { return LoadMapPrototype(LoadMap(CAST(proto))); },
[=] { return NullConstant(); }, MachineRepresentation::kTagged);
return WordEqual(proto_of_proto, typed_array_prototype);
}
Node* CodeStubAssembler::IsCallable(Node* object) {
return IsCallableMap(LoadMap(object));
}
......
......@@ -223,6 +223,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Select the minimum of the two provided Number values.
TNode<Object> NumberMin(SloppyTNode<Object> left, SloppyTNode<Object> right);
// After converting an index to an integer, calculate a relative index: if
// index < 0, max(length + index, 0); else min(index, length)
void ConvertToRelativeIndex(Node* context, Variable* var_result, Node* index,
Node* length);
// Tag a Word as a Smi value.
TNode<Smi> SmiTag(SloppyTNode<IntPtrT> value);
// Untag a Smi value as a Word.
......@@ -1125,6 +1130,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsPropertyArray(Node* object);
Node* IsPropertyCell(Node* object);
Node* IsPrototypeInitialArrayPrototype(Node* context, Node* map);
TNode<BoolT> IsPrototypeTypedArrayPrototype(SloppyTNode<Context> context,
SloppyTNode<Map> map);
Node* IsSequentialStringInstanceType(Node* instance_type);
Node* IsShortExternalStringInstanceType(Node* instance_type);
Node* IsSpecialReceiverInstanceType(Node* instance_type);
......
......@@ -145,59 +145,6 @@ function TypedArrayConstructByIterable(obj, iterable, iteratorFn, elementSize) {
}
}
macro TYPED_ARRAY_CONSTRUCTOR(NAME, ELEMENT_SIZE)
function NAMESubArray(begin, end) {
var beginInt = TO_INTEGER(begin);
if (!IS_UNDEFINED(end)) {
var endInt = TO_INTEGER(end);
var srcLength = %_TypedArrayGetLength(this);
} else {
var srcLength = %_TypedArrayGetLength(this);
var endInt = srcLength;
}
if (beginInt < 0) {
beginInt = MathMax(0, srcLength + beginInt);
} else {
beginInt = MathMin(beginInt, srcLength);
}
if (endInt < 0) {
endInt = MathMax(0, srcLength + endInt);
} else {
endInt = MathMin(endInt, srcLength);
}
if (endInt < beginInt) {
endInt = beginInt;
}
var newLength = endInt - beginInt;
var beginByteOffset =
%_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
return TypedArraySpeciesCreate(this, %TypedArrayGetBuffer(this),
beginByteOffset, newLength);
}
endmacro
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
DEFINE_METHOD(
GlobalTypedArray.prototype,
subarray(begin, end) {
switch (%_ClassOf(this)) {
macro TYPED_ARRAY_SUBARRAY_CASE(NAME, ELEMENT_SIZE)
case "NAME":
return %_Call(NAMESubArray, this, begin, end);
endmacro
TYPED_ARRAYS(TYPED_ARRAY_SUBARRAY_CASE)
}
throw %make_type_error(kIncompatibleMethodReceiver,
"get %TypedArray%.prototype.subarray", this);
}
);
// The following functions cannot be made efficient on sparse arrays while
// preserving the semantics, since the calls to the receiver function can add
// or delete elements from the array.
......
......@@ -108,6 +108,7 @@ void TestSpeciesProtector(char* code,
CompileRun(("let constructor = " + constructor + ";").c_str());
v8::Local<v8::Value> constructor_obj = CompileRun(constructor.c_str());
CHECK_EQ(constructor_obj, CompileRun("x.slice().constructor"));
CHECK_EQ(constructor_obj, CompileRun("x.subarray().constructor"));
CHECK_EQ(constructor_obj, CompileRun("x.map(()=>{}).constructor"));
std::string decl = "class MyTypedArray extends " + constructor + " { }";
CompileRun(decl.c_str());
......@@ -124,6 +125,7 @@ void TestSpeciesProtector(char* code,
v8::Local<v8::Value> my_typed_array = CompileRun("MyTypedArray");
CHECK_EQ(my_typed_array, CompileRun("x.slice().constructor"));
CHECK_EQ(my_typed_array, CompileRun("x.subarray().constructor"));
CHECK_EQ(my_typed_array, CompileRun("x.map(()=>{}).constructor"));
}
isolate->Exit();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment