[builtins] Port %TypedArray%.from to Torque
... in an uintptr index friendly way. Bug: v8:8906, v8:4153 Change-Id: Ib06ac205453fe3ff653b4fb73194e1ab53ca0d00 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1891610 Commit-Queue: Igor Sheludko <ishell@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#64731}
This commit is contained in:
parent
b701330857
commit
bff5a05319
1
BUILD.gn
1
BUILD.gn
@ -995,6 +995,7 @@ torque_files = [
|
|||||||
"src/builtins/typed-array-find.tq",
|
"src/builtins/typed-array-find.tq",
|
||||||
"src/builtins/typed-array-findindex.tq",
|
"src/builtins/typed-array-findindex.tq",
|
||||||
"src/builtins/typed-array-foreach.tq",
|
"src/builtins/typed-array-foreach.tq",
|
||||||
|
"src/builtins/typed-array-from.tq",
|
||||||
"src/builtins/typed-array-reduce.tq",
|
"src/builtins/typed-array-reduce.tq",
|
||||||
"src/builtins/typed-array-reduceright.tq",
|
"src/builtins/typed-array-reduceright.tq",
|
||||||
"src/builtins/typed-array-slice.tq",
|
"src/builtins/typed-array-slice.tq",
|
||||||
|
@ -1373,6 +1373,8 @@ const kInvalidArrayBufferLength: constexpr MessageTemplate
|
|||||||
generates 'MessageTemplate::kInvalidArrayBufferLength';
|
generates 'MessageTemplate::kInvalidArrayBufferLength';
|
||||||
const kInvalidArrayLength: constexpr MessageTemplate
|
const kInvalidArrayLength: constexpr MessageTemplate
|
||||||
generates 'MessageTemplate::kInvalidArrayLength';
|
generates 'MessageTemplate::kInvalidArrayLength';
|
||||||
|
const kNotConstructor: constexpr MessageTemplate
|
||||||
|
generates 'MessageTemplate::kNotConstructor';
|
||||||
const kCalledNonCallable: constexpr MessageTemplate
|
const kCalledNonCallable: constexpr MessageTemplate
|
||||||
generates 'MessageTemplate::kCalledNonCallable';
|
generates 'MessageTemplate::kCalledNonCallable';
|
||||||
const kCalledOnNullOrUndefined: constexpr MessageTemplate
|
const kCalledOnNullOrUndefined: constexpr MessageTemplate
|
||||||
@ -1491,7 +1493,7 @@ extern macro Int32FalseConstant(): bool;
|
|||||||
extern macro EmptyStringConstant(): EmptyString;
|
extern macro EmptyStringConstant(): EmptyString;
|
||||||
extern macro LengthStringConstant(): String;
|
extern macro LengthStringConstant(): String;
|
||||||
extern macro NanConstant(): NaN;
|
extern macro NanConstant(): NaN;
|
||||||
extern macro IteratorSymbolConstant(): Symbol;
|
extern macro IteratorSymbolConstant(): PublicSymbol;
|
||||||
extern macro MatchSymbolConstant(): Symbol;
|
extern macro MatchSymbolConstant(): Symbol;
|
||||||
|
|
||||||
const TheHole: TheHole = TheHoleConstant();
|
const TheHole: TheHole = TheHoleConstant();
|
||||||
@ -3245,8 +3247,6 @@ operator '[]=' macro StoreFixedArrayDirect(a: FixedArray, i: Smi, v: Object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern macro GetNumberDictionaryNumberOfElements(NumberDictionary): Smi;
|
extern macro GetNumberDictionaryNumberOfElements(NumberDictionary): Smi;
|
||||||
extern macro GetIteratorMethod(implicit context: Context)(HeapObject): JSAny
|
|
||||||
labels IfIteratorUndefined;
|
|
||||||
|
|
||||||
extern macro LoadConstructorOrBackPointer(Map): Object;
|
extern macro LoadConstructorOrBackPointer(Map): Object;
|
||||||
|
|
||||||
@ -3653,6 +3653,7 @@ extern macro IsName(HeapObject): bool;
|
|||||||
extern macro IsPrivateSymbol(HeapObject): bool;
|
extern macro IsPrivateSymbol(HeapObject): bool;
|
||||||
extern macro IsNumber(Object): bool;
|
extern macro IsNumber(Object): bool;
|
||||||
extern macro IsNumberNormalized(Number): bool;
|
extern macro IsNumberNormalized(Number): bool;
|
||||||
|
extern macro IsSafeInteger(Object): bool;
|
||||||
extern macro IsOddball(HeapObject): bool;
|
extern macro IsOddball(HeapObject): bool;
|
||||||
extern macro IsSymbol(HeapObject): bool;
|
extern macro IsSymbol(HeapObject): bool;
|
||||||
extern macro IsJSArrayMap(Map): bool;
|
extern macro IsJSArrayMap(Map): bool;
|
||||||
@ -3826,11 +3827,29 @@ transitioning macro GetLengthProperty(implicit context: Context)(o: JSAny):
|
|||||||
}
|
}
|
||||||
|
|
||||||
transitioning macro GetMethod(implicit context: Context)(
|
transitioning macro GetMethod(implicit context: Context)(
|
||||||
o: JSAny, name: constexpr string): Callable labels IfNullOrUndefined {
|
o: JSAny, name: AnyName): Callable labels IfNullOrUndefined,
|
||||||
|
IfMethodNotCallable(JSAny) {
|
||||||
const value = GetProperty(o, name);
|
const value = GetProperty(o, name);
|
||||||
|
// TODO(v8:9933): Consider checking for null/undefined after checking for
|
||||||
|
// callable because the latter seems to be more common.
|
||||||
if (value == Undefined || value == Null) goto IfNullOrUndefined;
|
if (value == Undefined || value == Null) goto IfNullOrUndefined;
|
||||||
return Cast<Callable>(value)
|
return Cast<Callable>(value)
|
||||||
otherwise ThrowTypeError(kPropertyNotFunction, value, name, o);
|
otherwise goto IfMethodNotCallable(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
transitioning macro GetMethod(implicit context: Context)(
|
||||||
|
o: JSAny, name: String): Callable labels IfNullOrUndefined {
|
||||||
|
try {
|
||||||
|
return GetMethod(o, name) otherwise IfNullOrUndefined, IfMethodNotCallable;
|
||||||
|
}
|
||||||
|
label IfMethodNotCallable(value: JSAny) deferred {
|
||||||
|
ThrowTypeError(kPropertyNotFunction, value, name, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transitioning macro GetMethod(implicit context: Context)(
|
||||||
|
o: JSAny, name: constexpr string): Callable labels IfNullOrUndefined {
|
||||||
|
return GetMethod(o, StringConstant(name)) otherwise IfNullOrUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern macro NumberToString(Number): String;
|
extern macro NumberToString(Number): String;
|
||||||
|
@ -941,8 +941,6 @@ namespace internal {
|
|||||||
TFJ(TypedArrayPrototypeMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
TFJ(TypedArrayPrototypeMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||||
/* ES6 %TypedArray%.of */ \
|
/* ES6 %TypedArray%.of */ \
|
||||||
TFJ(TypedArrayOf, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
TFJ(TypedArrayOf, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||||
/* ES6 %TypedArray%.from */ \
|
|
||||||
TFJ(TypedArrayFrom, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
|
||||||
\
|
\
|
||||||
/* Wasm */ \
|
/* Wasm */ \
|
||||||
ASM(WasmCompileLazy, Dummy) \
|
ASM(WasmCompileLazy, Dummy) \
|
||||||
|
@ -231,36 +231,6 @@ TNode<JSFunction> TypedArrayBuiltinsAssembler::GetDefaultConstructor(
|
|||||||
LoadContextElement(LoadNativeContext(context), context_slot.value()));
|
LoadContextElement(LoadNativeContext(context), context_slot.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<JSTypedArray> TypedArrayBuiltinsAssembler::TypedArrayCreateByLength(
|
|
||||||
TNode<Context> context, TNode<Object> constructor, TNode<Smi> len,
|
|
||||||
const char* method_name) {
|
|
||||||
CSA_ASSERT(this, TaggedIsPositiveSmi(len));
|
|
||||||
|
|
||||||
// Let newTypedArray be ? Construct(constructor, argumentList).
|
|
||||||
TNode<Object> new_object = CAST(ConstructJS(CodeFactory::Construct(isolate()),
|
|
||||||
context, constructor, len));
|
|
||||||
|
|
||||||
// Perform ? ValidateTypedArray(newTypedArray).
|
|
||||||
TNode<JSTypedArray> new_typed_array =
|
|
||||||
ValidateTypedArray(context, new_object, method_name);
|
|
||||||
|
|
||||||
ThrowIfLengthLessThan(context, new_typed_array, len);
|
|
||||||
return new_typed_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TypedArrayBuiltinsAssembler::ThrowIfLengthLessThan(
|
|
||||||
TNode<Context> context, TNode<JSTypedArray> typed_array,
|
|
||||||
TNode<Smi> min_length) {
|
|
||||||
// If typed_array.[[ArrayLength]] < min_length, throw a TypeError exception.
|
|
||||||
Label if_length_is_not_short(this);
|
|
||||||
TNode<UintPtrT> new_length = LoadJSTypedArrayLength(typed_array);
|
|
||||||
GotoIfNot(UintPtrLessThan(new_length, SmiUntag(min_length)),
|
|
||||||
&if_length_is_not_short);
|
|
||||||
ThrowTypeError(context, MessageTemplate::kTypedArrayTooShort);
|
|
||||||
|
|
||||||
BIND(&if_length_is_not_short);
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
|
TNode<JSArrayBuffer> TypedArrayBuiltinsAssembler::GetBuffer(
|
||||||
TNode<Context> context, TNode<JSTypedArray> array) {
|
TNode<Context> context, TNode<JSTypedArray> array) {
|
||||||
Label call_runtime(this), done(this);
|
Label call_runtime(this), done(this);
|
||||||
@ -605,6 +575,23 @@ void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
|
||||||
|
TNode<Context> context, TNode<JSTypedArray> typed_array,
|
||||||
|
TNode<UintPtrT> index, TNode<Object> value, ElementsKind elements_kind,
|
||||||
|
Label* if_detached) {
|
||||||
|
// |prepared_value| is Word32T or Float64T or Float32T or BigInt.
|
||||||
|
Node* prepared_value =
|
||||||
|
PrepareValueForWriteToTypedArray(value, elements_kind, context);
|
||||||
|
|
||||||
|
// ToNumber/ToBigInt may execute JavaScript code, which could detach
|
||||||
|
// the array's buffer.
|
||||||
|
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(typed_array);
|
||||||
|
GotoIf(IsDetachedBuffer(buffer), if_detached);
|
||||||
|
|
||||||
|
TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array);
|
||||||
|
StoreElement(data_ptr, elements_kind, index, prepared_value);
|
||||||
|
}
|
||||||
|
|
||||||
// ES #sec-get-%typedarray%.prototype.set
|
// ES #sec-get-%typedarray%.prototype.set
|
||||||
TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
|
TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) {
|
||||||
const char* method_name = "%TypedArray%.prototype.set";
|
const char* method_name = "%TypedArray%.prototype.set";
|
||||||
@ -790,7 +777,7 @@ TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
|
|||||||
|
|
||||||
// 5. Let newObj be ? TypedArrayCreate(C, len).
|
// 5. Let newObj be ? TypedArrayCreate(C, len).
|
||||||
TNode<JSTypedArray> new_typed_array = TypedArrayCreateByLength(
|
TNode<JSTypedArray> new_typed_array = TypedArrayCreateByLength(
|
||||||
context, receiver, SmiTag(length), "%TypedArray%.of");
|
context, CAST(receiver), SmiTag(length), "%TypedArray%.of");
|
||||||
|
|
||||||
TNode<Int32T> elements_kind = LoadElementsKind(new_typed_array);
|
TNode<Int32T> elements_kind = LoadElementsKind(new_typed_array);
|
||||||
|
|
||||||
@ -836,231 +823,5 @@ TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
|
|||||||
"%TypedArray%.of");
|
"%TypedArray%.of");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES6 #sec-%typedarray%.from
|
|
||||||
TF_BUILTIN(TypedArrayFrom, TypedArrayBuiltinsAssembler) {
|
|
||||||
TNode<Int32T> argc =
|
|
||||||
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
|
|
||||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
|
||||||
|
|
||||||
Label check_iterator(this), from_array_like(this), fast_path(this),
|
|
||||||
slow_path(this), create_typed_array(this), check_typedarray(this),
|
|
||||||
if_not_constructor(this, Label::kDeferred),
|
|
||||||
if_map_fn_not_callable(this, Label::kDeferred),
|
|
||||||
if_iterator_fn_not_callable(this, Label::kDeferred),
|
|
||||||
if_detached(this, Label::kDeferred);
|
|
||||||
|
|
||||||
CodeStubArguments args(this, argc);
|
|
||||||
TNode<Object> source = args.GetOptionalArgumentValue(0);
|
|
||||||
|
|
||||||
// 5. If thisArg is present, let T be thisArg; else let T be undefined.
|
|
||||||
TNode<Object> this_arg = args.GetOptionalArgumentValue(2);
|
|
||||||
|
|
||||||
// 1. Let C be the this value.
|
|
||||||
// 2. If IsConstructor(C) is false, throw a TypeError exception.
|
|
||||||
TNode<Object> receiver = args.GetReceiver();
|
|
||||||
GotoIf(TaggedIsSmi(receiver), &if_not_constructor);
|
|
||||||
GotoIfNot(IsConstructor(CAST(receiver)), &if_not_constructor);
|
|
||||||
|
|
||||||
// 3. If mapfn is present and mapfn is not undefined, then
|
|
||||||
TNode<Object> map_fn = args.GetOptionalArgumentValue(1);
|
|
||||||
TVARIABLE(BoolT, mapping, Int32FalseConstant());
|
|
||||||
GotoIf(IsUndefined(map_fn), &check_typedarray);
|
|
||||||
|
|
||||||
// a. If IsCallable(mapfn) is false, throw a TypeError exception.
|
|
||||||
// b. Let mapping be true.
|
|
||||||
// 4. Else, let mapping be false.
|
|
||||||
GotoIf(TaggedIsSmi(map_fn), &if_map_fn_not_callable);
|
|
||||||
GotoIfNot(IsCallable(CAST(map_fn)), &if_map_fn_not_callable);
|
|
||||||
mapping = Int32TrueConstant();
|
|
||||||
Goto(&check_typedarray);
|
|
||||||
|
|
||||||
TVARIABLE(Object, final_source);
|
|
||||||
TVARIABLE(Smi, final_length);
|
|
||||||
|
|
||||||
// We split up this builtin differently to the way it is written in the spec.
|
|
||||||
// We already have great code in the elements accessor for copying from a
|
|
||||||
// JSArray into a TypedArray, so we use that when possible. We only avoid
|
|
||||||
// calling into the elements accessor when we have a mapping function, because
|
|
||||||
// we can't handle that. Here, presence of a mapping function is the slow
|
|
||||||
// path. We also combine the two different loops in the specification
|
|
||||||
// (starting at 7.e and 13) because they are essentially identical. We also
|
|
||||||
// save on code-size this way.
|
|
||||||
|
|
||||||
// Get the iterator function
|
|
||||||
BIND(&check_typedarray);
|
|
||||||
TNode<Object> iterator_fn =
|
|
||||||
CAST(GetMethod(context, source, isolate()->factory()->iterator_symbol(),
|
|
||||||
&from_array_like));
|
|
||||||
GotoIf(TaggedIsSmi(iterator_fn), &if_iterator_fn_not_callable);
|
|
||||||
|
|
||||||
{
|
|
||||||
// TypedArrays have iterators, so normally we would go through the
|
|
||||||
// IterableToList case below, which would convert the TypedArray to a
|
|
||||||
// JSArray (boxing the values if they won't fit in a Smi).
|
|
||||||
//
|
|
||||||
// However, if we can guarantee that the source object has the built-in
|
|
||||||
// iterator and that the %ArrayIteratorPrototype%.next method has not been
|
|
||||||
// overridden, then we know the behavior of the iterator: returning the
|
|
||||||
// values in the TypedArray sequentially from index 0 to length-1.
|
|
||||||
//
|
|
||||||
// In this case, we can avoid creating the intermediate array and the
|
|
||||||
// associated HeapNumbers, and use the fast path in TypedArrayCopyElements
|
|
||||||
// which uses the same ordering as the default iterator.
|
|
||||||
//
|
|
||||||
// Drop through to the default check_iterator behavior if any of these
|
|
||||||
// checks fail.
|
|
||||||
|
|
||||||
// Check that the source is a TypedArray
|
|
||||||
GotoIf(TaggedIsSmi(source), &check_iterator);
|
|
||||||
GotoIfNot(IsJSTypedArray(CAST(source)), &check_iterator);
|
|
||||||
TNode<JSArrayBuffer> source_buffer =
|
|
||||||
LoadJSArrayBufferViewBuffer(CAST(source));
|
|
||||||
GotoIf(IsDetachedBuffer(source_buffer), &check_iterator);
|
|
||||||
|
|
||||||
// Check that the iterator function is Builtins::kTypedArrayPrototypeValues
|
|
||||||
GotoIfNot(IsJSFunction(CAST(iterator_fn)), &check_iterator);
|
|
||||||
TNode<SharedFunctionInfo> shared_info = LoadObjectField<SharedFunctionInfo>(
|
|
||||||
CAST(iterator_fn), JSFunction::kSharedFunctionInfoOffset);
|
|
||||||
GotoIfNot(
|
|
||||||
TaggedEqual(LoadObjectField(shared_info,
|
|
||||||
SharedFunctionInfo::kFunctionDataOffset),
|
|
||||||
SmiConstant(Builtins::kTypedArrayPrototypeValues)),
|
|
||||||
&check_iterator);
|
|
||||||
// Check that the ArrayIterator prototype's "next" method hasn't been
|
|
||||||
// overridden
|
|
||||||
TNode<PropertyCell> protector_cell = ArrayIteratorProtectorConstant();
|
|
||||||
GotoIfNot(
|
|
||||||
TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
|
|
||||||
SmiConstant(Protectors::kProtectorValid)),
|
|
||||||
&check_iterator);
|
|
||||||
|
|
||||||
// Source is a TypedArray with unmodified iterator behavior. Use the
|
|
||||||
// source object directly, taking advantage of the special-case code in
|
|
||||||
// TypedArrayCopyElements
|
|
||||||
// TODO(v8:4153): This needs to be handle to huge TypedArrays.
|
|
||||||
final_length = SmiTag(Signed(LoadJSTypedArrayLength(CAST(source))));
|
|
||||||
final_source = source;
|
|
||||||
Goto(&create_typed_array);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&check_iterator);
|
|
||||||
{
|
|
||||||
// 6. Let usingIterator be ? GetMethod(source, @@iterator).
|
|
||||||
GotoIfNot(IsCallable(CAST(iterator_fn)), &if_iterator_fn_not_callable);
|
|
||||||
|
|
||||||
// We are using the iterator.
|
|
||||||
Label if_length_not_smi(this, Label::kDeferred);
|
|
||||||
// 7. If usingIterator is not undefined, then
|
|
||||||
// a. Let values be ? IterableToList(source, usingIterator).
|
|
||||||
// b. Let len be the number of elements in values.
|
|
||||||
TNode<JSArray> values = CAST(
|
|
||||||
CallBuiltin(Builtins::kIterableToList, context, source, iterator_fn));
|
|
||||||
|
|
||||||
// This is not a spec'd limit, so it doesn't particularly matter when we
|
|
||||||
// throw the range error for typed array length > MaxSmi.
|
|
||||||
TNode<Number> raw_length = LoadJSArrayLength(values);
|
|
||||||
GotoIfNot(TaggedIsSmi(raw_length), &if_length_not_smi);
|
|
||||||
|
|
||||||
final_length = CAST(raw_length);
|
|
||||||
final_source = values;
|
|
||||||
Goto(&create_typed_array);
|
|
||||||
|
|
||||||
BIND(&if_length_not_smi);
|
|
||||||
ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
|
|
||||||
raw_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&from_array_like);
|
|
||||||
{
|
|
||||||
// TODO(7881): support larger-than-smi typed array lengths
|
|
||||||
Label if_length_not_smi(this, Label::kDeferred);
|
|
||||||
final_source = source;
|
|
||||||
|
|
||||||
// 10. Let len be ? ToLength(? Get(arrayLike, "length")).
|
|
||||||
TNode<Object> raw_length =
|
|
||||||
GetProperty(context, final_source.value(), LengthStringConstant());
|
|
||||||
final_length = ToSmiLength(context, raw_length, &if_length_not_smi);
|
|
||||||
Goto(&create_typed_array);
|
|
||||||
|
|
||||||
BIND(&if_length_not_smi);
|
|
||||||
ThrowRangeError(context, MessageTemplate::kInvalidTypedArrayLength,
|
|
||||||
raw_length);
|
|
||||||
}
|
|
||||||
|
|
||||||
TVARIABLE(JSTypedArray, target_obj);
|
|
||||||
|
|
||||||
BIND(&create_typed_array);
|
|
||||||
{
|
|
||||||
// 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»).
|
|
||||||
target_obj = TypedArrayCreateByLength(
|
|
||||||
context, receiver, final_length.value(), "%TypedArray%.from");
|
|
||||||
|
|
||||||
Branch(mapping.value(), &slow_path, &fast_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&fast_path);
|
|
||||||
{
|
|
||||||
Label done(this);
|
|
||||||
GotoIf(SmiEqual(final_length.value(), SmiConstant(0)), &done);
|
|
||||||
|
|
||||||
CallRuntime(Runtime::kTypedArrayCopyElements, context, target_obj.value(),
|
|
||||||
final_source.value(), final_length.value());
|
|
||||||
Goto(&done);
|
|
||||||
|
|
||||||
BIND(&done);
|
|
||||||
args.PopAndReturn(target_obj.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&slow_path);
|
|
||||||
TNode<Int32T> elements_kind = LoadElementsKind(target_obj.value());
|
|
||||||
|
|
||||||
// 7e/13 : Copy the elements
|
|
||||||
BuildFastLoop<Smi>(
|
|
||||||
SmiConstant(0), final_length.value(),
|
|
||||||
[&](TNode<Smi> index) {
|
|
||||||
const TNode<Object> k_value =
|
|
||||||
GetProperty(context, final_source.value(), index);
|
|
||||||
|
|
||||||
const TNode<Object> mapped_value =
|
|
||||||
CallJS(CodeFactory::Call(isolate()), context, map_fn, this_arg,
|
|
||||||
k_value, index);
|
|
||||||
|
|
||||||
DispatchTypedArrayByElementsKind(
|
|
||||||
elements_kind,
|
|
||||||
[&](ElementsKind kind, int size, int typed_array_fun_index) {
|
|
||||||
Node* const final_value =
|
|
||||||
PrepareValueForWriteToTypedArray(mapped_value, kind, context);
|
|
||||||
|
|
||||||
// ToNumber/ToBigInt may execute JavaScript code, which could
|
|
||||||
// detach the array's buffer.
|
|
||||||
TNode<JSArrayBuffer> buffer =
|
|
||||||
LoadJSArrayBufferViewBuffer(target_obj.value());
|
|
||||||
GotoIf(IsDetachedBuffer(buffer), &if_detached);
|
|
||||||
|
|
||||||
// GC may move backing store in map_fn, thus load backing
|
|
||||||
// store in each iteration of this loop.
|
|
||||||
TNode<RawPtrT> data_ptr =
|
|
||||||
LoadJSTypedArrayDataPtr(target_obj.value());
|
|
||||||
StoreElement(data_ptr, kind, index, final_value, SMI_PARAMETERS);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
1, IndexAdvanceMode::kPost);
|
|
||||||
|
|
||||||
args.PopAndReturn(target_obj.value());
|
|
||||||
|
|
||||||
BIND(&if_not_constructor);
|
|
||||||
ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
|
|
||||||
|
|
||||||
BIND(&if_map_fn_not_callable);
|
|
||||||
ThrowTypeError(context, MessageTemplate::kCalledNonCallable, map_fn);
|
|
||||||
|
|
||||||
BIND(&if_iterator_fn_not_callable);
|
|
||||||
ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable);
|
|
||||||
|
|
||||||
BIND(&if_detached);
|
|
||||||
ThrowTypeError(context, MessageTemplate::kDetachedOperation,
|
|
||||||
"%TypedArray%.from");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -50,15 +50,6 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
TNode<JSFunction> GetDefaultConstructor(TNode<Context> context,
|
TNode<JSFunction> GetDefaultConstructor(TNode<Context> context,
|
||||||
TNode<JSTypedArray> exemplar);
|
TNode<JSTypedArray> exemplar);
|
||||||
|
|
||||||
TNode<JSTypedArray> TypedArrayCreateByLength(TNode<Context> context,
|
|
||||||
TNode<Object> constructor,
|
|
||||||
TNode<Smi> len,
|
|
||||||
const char* method_name);
|
|
||||||
|
|
||||||
void ThrowIfLengthLessThan(TNode<Context> context,
|
|
||||||
TNode<JSTypedArray> typed_array,
|
|
||||||
TNode<Smi> min_length);
|
|
||||||
|
|
||||||
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
|
TNode<JSArrayBuffer> GetBuffer(TNode<Context> context,
|
||||||
TNode<JSTypedArray> array);
|
TNode<JSTypedArray> array);
|
||||||
|
|
||||||
@ -117,6 +108,12 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
TNode<UintPtrT> index_node,
|
TNode<UintPtrT> index_node,
|
||||||
TNode<Numeric> value,
|
TNode<Numeric> value,
|
||||||
ElementsKind elements_kind);
|
ElementsKind elements_kind);
|
||||||
|
void StoreJSTypedArrayElementFromTagged(TNode<Context> context,
|
||||||
|
TNode<JSTypedArray> typed_array,
|
||||||
|
TNode<UintPtrT> index_node,
|
||||||
|
TNode<Object> value,
|
||||||
|
ElementsKind elements_kind,
|
||||||
|
Label* if_detached);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -27,8 +27,6 @@ namespace promise {
|
|||||||
const PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN:
|
const PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN:
|
||||||
constexpr NativeContextSlot
|
constexpr NativeContextSlot
|
||||||
generates 'Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN';
|
generates 'Context::PROMISE_GET_CAPABILITIES_EXECUTOR_SHARED_FUN';
|
||||||
const kNotConstructor: constexpr MessageTemplate
|
|
||||||
generates 'MessageTemplate::kNotConstructor';
|
|
||||||
const kPromiseNonCallable: constexpr MessageTemplate
|
const kPromiseNonCallable: constexpr MessageTemplate
|
||||||
generates 'MessageTemplate::kPromiseNonCallable';
|
generates 'MessageTemplate::kPromiseNonCallable';
|
||||||
|
|
||||||
|
@ -283,15 +283,46 @@ namespace typed_array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 22.2.4.6 TypedArrayCreate ( constructor, argumentList )
|
||||||
|
// ES #typedarray-create
|
||||||
|
@export
|
||||||
|
transitioning macro TypedArrayCreateByLength(implicit context: Context)(
|
||||||
|
constructor: Constructor, length: Number, methodName: constexpr string):
|
||||||
|
JSTypedArray {
|
||||||
|
assert(IsSafeInteger(length));
|
||||||
|
|
||||||
|
// 1. Let newTypedArray be ? Construct(constructor, argumentList).
|
||||||
|
const newTypedArrayObj = Construct(constructor, length);
|
||||||
|
|
||||||
|
// 2. Perform ? ValidateTypedArray(newTypedArray).
|
||||||
|
// ValidateTypedArray currently returns the array, not the ViewBuffer.
|
||||||
|
const newTypedArray: JSTypedArray =
|
||||||
|
ValidateTypedArray(context, newTypedArrayObj, methodName);
|
||||||
|
|
||||||
|
if (IsDetachedBuffer(newTypedArray.buffer)) deferred {
|
||||||
|
ThrowTypeError(kDetachedOperation, methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If argumentList is a List of a single Number, then
|
||||||
|
// a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a
|
||||||
|
// TypeError exception.
|
||||||
|
if (newTypedArray.length < Convert<uintptr>(length)) deferred {
|
||||||
|
ThrowTypeError(kTypedArrayTooShort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Return newTypedArray.
|
||||||
|
return newTypedArray;
|
||||||
|
}
|
||||||
|
|
||||||
transitioning macro ConstructByJSReceiver(implicit context:
|
transitioning macro ConstructByJSReceiver(implicit context:
|
||||||
Context)(obj: JSReceiver): never
|
Context)(obj: JSReceiver): never
|
||||||
labels IfConstructByArrayLike(JSReceiver, uintptr, JSReceiver) {
|
labels IfConstructByArrayLike(JSReceiver, uintptr, JSReceiver) {
|
||||||
try {
|
try {
|
||||||
const iteratorMethod: Object =
|
// TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
|
||||||
GetIteratorMethod(obj) otherwise IfIteratorUndefined;
|
// labels.
|
||||||
const iteratorFn: Callable = Cast<Callable>(iteratorMethod)
|
const iteratorMethod = GetMethod(obj, IteratorSymbolConstant())
|
||||||
otherwise ThrowTypeError(kIteratorSymbolNonCallable);
|
otherwise IfIteratorUndefined, IfIteratorNotCallable;
|
||||||
ConstructByIterable(obj, iteratorFn)
|
ConstructByIterable(obj, iteratorMethod)
|
||||||
otherwise IfConstructByArrayLike;
|
otherwise IfConstructByArrayLike;
|
||||||
}
|
}
|
||||||
label IfIteratorUndefined {
|
label IfIteratorUndefined {
|
||||||
@ -307,6 +338,9 @@ namespace typed_array {
|
|||||||
label IfInvalidLength(length: Number) {
|
label IfInvalidLength(length: Number) {
|
||||||
ThrowRangeError(kInvalidTypedArrayLength, length);
|
ThrowRangeError(kInvalidTypedArrayLength, length);
|
||||||
}
|
}
|
||||||
|
label IfIteratorNotCallable(_value: JSAny) deferred {
|
||||||
|
ThrowTypeError(kIteratorSymbolNonCallable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 22.2.4 The TypedArray Constructors
|
// 22.2.4 The TypedArray Constructors
|
||||||
|
186
src/builtins/typed-array-from.tq
Normal file
186
src/builtins/typed-array-from.tq
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
#include 'src/builtins/builtins-typed-array-gen.h'
|
||||||
|
|
||||||
|
namespace typed_array {
|
||||||
|
const kBuiltinNameFrom: constexpr string = '%TypedArray%.from';
|
||||||
|
|
||||||
|
type BuiltinsName extends int31 constexpr 'Builtins::Name';
|
||||||
|
const kTypedArrayPrototypeValues: constexpr BuiltinsName
|
||||||
|
generates 'Builtins::kTypedArrayPrototypeValues';
|
||||||
|
|
||||||
|
extern builtin IterableToList(implicit context: Context)(JSAny, JSAny):
|
||||||
|
JSArray;
|
||||||
|
|
||||||
|
// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
|
||||||
|
// https://tc39.github.io/ecma262/#sec-%typedarray%.from
|
||||||
|
transitioning javascript builtin
|
||||||
|
TypedArrayFrom(js-implicit context: Context, receiver: JSAny)(...arguments):
|
||||||
|
JSTypedArray {
|
||||||
|
try {
|
||||||
|
const source: JSAny = arguments[0];
|
||||||
|
|
||||||
|
// 1. Let C be the this value.
|
||||||
|
// 2. If IsConstructor(C) is false, throw a TypeError exception.
|
||||||
|
const constructor = Cast<Constructor>(receiver) otherwise NotConstructor;
|
||||||
|
|
||||||
|
// 3. If mapfn is present and mapfn is not undefined, then
|
||||||
|
// a. If IsCallable(mapfn) is false, throw a TypeError exception.
|
||||||
|
// b. Let mapping be true.
|
||||||
|
// 4. Else, let mapping be false.
|
||||||
|
const mapping: bool = arguments.length > 1;
|
||||||
|
const mapfnObj: JSAny = mapping ? arguments[1] : Undefined;
|
||||||
|
if (mapping && !TaggedIsCallable(mapfnObj)) deferred {
|
||||||
|
ThrowTypeError(kCalledNonCallable, mapfnObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. If thisArg is present, let T be thisArg; else let T be undefined.
|
||||||
|
const thisArg = arguments.length > 2 ? arguments[2] : Undefined;
|
||||||
|
|
||||||
|
// We split up this builtin differently to the way it is written in the
|
||||||
|
// spec. We already have great code in the elements accessor for copying
|
||||||
|
// from a JSArray into a TypedArray, so we use that when possible. We only
|
||||||
|
// avoid calling into the elements accessor when we have a mapping
|
||||||
|
// function, because we can't handle that. Here, presence of a mapping
|
||||||
|
// function is the slow path. We also combine the two different loops in
|
||||||
|
// the specification (starting at 7.e and 13) because they are essentially
|
||||||
|
// identical. We also save on code-size this way.
|
||||||
|
|
||||||
|
let finalLength: uintptr;
|
||||||
|
let finalSource: JSAny;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 6. Let usingIterator be ? GetMethod(source, @@iterator).
|
||||||
|
// TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
|
||||||
|
// labels.
|
||||||
|
const usingIterator = GetMethod(source, IteratorSymbolConstant())
|
||||||
|
otherwise IteratorIsUndefined, IteratorNotCallable;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TypedArrays have iterators, so normally we would go through the
|
||||||
|
// IterableToList case below, which would convert the TypedArray to a
|
||||||
|
// JSArray (boxing the values if they won't fit in a Smi).
|
||||||
|
//
|
||||||
|
// However, if we can guarantee that the source object has the
|
||||||
|
// built-in iterator and that the %ArrayIteratorPrototype%.next method
|
||||||
|
// has not been overridden, then we know the behavior of the iterator:
|
||||||
|
// returning the values in the TypedArray sequentially from index 0 to
|
||||||
|
// length-1.
|
||||||
|
//
|
||||||
|
// In this case, we can avoid creating the intermediate array and the
|
||||||
|
// associated HeapNumbers, and use the fast path in
|
||||||
|
// TypedArrayCopyElements which uses the same ordering as the default
|
||||||
|
// iterator.
|
||||||
|
//
|
||||||
|
// Drop through to the default check_iterator behavior if any of these
|
||||||
|
// checks fail.
|
||||||
|
const sourceTypedArray =
|
||||||
|
Cast<JSTypedArray>(source) otherwise UseUserProvidedIterator;
|
||||||
|
const sourceBuffer = sourceTypedArray.buffer;
|
||||||
|
if (IsDetachedBuffer(sourceBuffer)) goto UseUserProvidedIterator;
|
||||||
|
|
||||||
|
// Check that the iterator function is exactly
|
||||||
|
// Builtins::kTypedArrayPrototypeValues.
|
||||||
|
const iteratorFn =
|
||||||
|
Cast<JSFunction>(usingIterator) otherwise UseUserProvidedIterator;
|
||||||
|
if (TaggedEqual(
|
||||||
|
iteratorFn.shared_function_info.function_data,
|
||||||
|
SmiConstant(kTypedArrayPrototypeValues)))
|
||||||
|
goto UseUserProvidedIterator;
|
||||||
|
|
||||||
|
// Check that the ArrayIterator prototype's "next" method hasn't been
|
||||||
|
// overridden.
|
||||||
|
if (IsArrayIteratorProtectorCellInvalid())
|
||||||
|
goto UseUserProvidedIterator;
|
||||||
|
|
||||||
|
// Source is a TypedArray with unmodified iterator behavior. Use the
|
||||||
|
// source object directly, taking advantage of the special-case code
|
||||||
|
// in TypedArrayCopyElements
|
||||||
|
finalLength = sourceTypedArray.length;
|
||||||
|
finalSource = source;
|
||||||
|
}
|
||||||
|
label UseUserProvidedIterator {
|
||||||
|
// 7. If usingIterator is not undefined, then
|
||||||
|
// a. Let values be ? IterableToList(source, usingIterator).
|
||||||
|
// b. Let len be the number of elements in values.
|
||||||
|
const values: JSArray = IterableToList(source, usingIterator);
|
||||||
|
|
||||||
|
finalLength = Convert<uintptr>(values.length);
|
||||||
|
finalSource = values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
label IteratorIsUndefined {
|
||||||
|
// 8. NOTE: source is not an Iterable so assume it is already an
|
||||||
|
// array-like object.
|
||||||
|
|
||||||
|
// 9. Let arrayLike be ! ToObject(source).
|
||||||
|
const arrayLike: JSReceiver = ToObject_Inline(context, source);
|
||||||
|
|
||||||
|
// 10. Let len be ? ToLength(? Get(arrayLike, "length")).
|
||||||
|
finalLength = Convert<uintptr>(GetLengthProperty(arrayLike));
|
||||||
|
finalSource = arrayLike;
|
||||||
|
}
|
||||||
|
label IteratorNotCallable(_value: JSAny) deferred {
|
||||||
|
ThrowTypeError(kIteratorSymbolNonCallable);
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalLengthNum = Convert<Number>(finalLength);
|
||||||
|
|
||||||
|
// 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»).
|
||||||
|
const targetObj = TypedArrayCreateByLength(
|
||||||
|
constructor, finalLengthNum, kBuiltinNameFrom);
|
||||||
|
|
||||||
|
if (!mapping) {
|
||||||
|
// Fast path.
|
||||||
|
if (finalLength != 0) {
|
||||||
|
// Call runtime.
|
||||||
|
TypedArrayCopyElements(
|
||||||
|
context, targetObj, finalSource, finalLengthNum);
|
||||||
|
}
|
||||||
|
return targetObj;
|
||||||
|
}
|
||||||
|
// Slow path.
|
||||||
|
|
||||||
|
const mapfn: Callable = Cast<Callable>(mapfnObj) otherwise unreachable;
|
||||||
|
const accessor: TypedArrayAccessor =
|
||||||
|
GetTypedArrayAccessor(targetObj.elements_kind);
|
||||||
|
|
||||||
|
// 7d-7e and 12-13.
|
||||||
|
// 12. Let k be 0.
|
||||||
|
// 13. Repeat, while k < len
|
||||||
|
for (let k: uintptr = 0; k < finalLength; k++) {
|
||||||
|
// 13a. Let Pk be ! ToString(k).
|
||||||
|
const kNum = Convert<Number>(k);
|
||||||
|
|
||||||
|
// 13b. Let kValue be ? Get(arrayLike, Pk).
|
||||||
|
const kValue: JSAny = GetProperty(finalSource, kNum);
|
||||||
|
|
||||||
|
let mappedValue: JSAny;
|
||||||
|
// 13c. If mapping is true, then
|
||||||
|
if (mapping) {
|
||||||
|
// i. Let mappedValue be ? Call(mapfn, T, « kValue, k »).
|
||||||
|
mappedValue = Call(context, mapfn, thisArg, kValue, kNum);
|
||||||
|
} else {
|
||||||
|
// 13d. Else, let mappedValue be kValue.
|
||||||
|
mappedValue = kValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 13e. Perform ? Set(targetObj, Pk, mappedValue, true).
|
||||||
|
// Buffer may be detached during executing ToNumber/ToBigInt.
|
||||||
|
accessor.StoreJSAny(context, targetObj, k, mappedValue)
|
||||||
|
otherwise IfDetached;
|
||||||
|
|
||||||
|
// 13f. Set k to k + 1. (done by the loop).
|
||||||
|
}
|
||||||
|
return targetObj;
|
||||||
|
}
|
||||||
|
label NotConstructor deferred {
|
||||||
|
ThrowTypeError(kNotConstructor, receiver);
|
||||||
|
}
|
||||||
|
label IfDetached deferred {
|
||||||
|
ThrowTypeError(kDetachedOperation, kBuiltinNameFrom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -59,7 +59,7 @@ namespace typed_array {
|
|||||||
// 1. Let O be the this value.
|
// 1. Let O be the this value.
|
||||||
// 2. Perform ? ValidateTypedArray(O).
|
// 2. Perform ? ValidateTypedArray(O).
|
||||||
const src: JSTypedArray =
|
const src: JSTypedArray =
|
||||||
typed_array::ValidateTypedArray(context, receiver, kBuiltinNameSlice);
|
ValidateTypedArray(context, receiver, kBuiltinNameSlice);
|
||||||
|
|
||||||
// 3. Let len be O.[[ArrayLength]].
|
// 3. Let len be O.[[ArrayLength]].
|
||||||
const len: uintptr = src.length;
|
const len: uintptr = src.length;
|
||||||
|
@ -79,63 +79,85 @@ namespace typed_array {
|
|||||||
RawPtr, uintptr, constexpr ElementsKind): Numeric;
|
RawPtr, uintptr, constexpr ElementsKind): Numeric;
|
||||||
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(
|
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(
|
||||||
Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind);
|
Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind);
|
||||||
|
extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
|
||||||
|
Context, JSTypedArray, uintptr, JSAny,
|
||||||
|
constexpr ElementsKind) labels IfDetached;
|
||||||
|
|
||||||
type LoadNumericFn = builtin(Context, JSTypedArray, uintptr) => Numeric;
|
type LoadNumericFn = builtin(Context, JSTypedArray, uintptr) => Numeric;
|
||||||
type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) =>
|
type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) => Smi;
|
||||||
JSAny;
|
type StoreJSAnyFn = builtin(Context, JSTypedArray, uintptr, JSAny) => Smi;
|
||||||
|
|
||||||
|
// The result codes returned by StoreNumericFn and StoreJSAnyFn builtins.
|
||||||
|
const kStoreSucceded: Smi = 0;
|
||||||
|
const kStoreFailureArrayDetached: Smi = 1;
|
||||||
|
|
||||||
|
struct TypedArrayAccessor {
|
||||||
|
LoadNumeric(
|
||||||
|
context: Context, array: JSTypedArray, index: uintptr): Numeric {
|
||||||
|
const loadfn: LoadNumericFn = this.loadNumericFn;
|
||||||
|
return loadfn(context, array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreNumeric(
|
||||||
|
context: Context, array: JSTypedArray, index: uintptr, value: Numeric) {
|
||||||
|
const storefn: StoreNumericFn = this.storeNumericFn;
|
||||||
|
const result = storefn(context, array, index, value);
|
||||||
|
assert(result == kStoreSucceded);
|
||||||
|
}
|
||||||
|
|
||||||
|
StoreJSAny(
|
||||||
|
context: Context, array: JSTypedArray, index: uintptr, value: JSAny)
|
||||||
|
labels IfDetached {
|
||||||
|
const storefn: StoreJSAnyFn = this.storeJSAnyFn;
|
||||||
|
const result = storefn(context, array, index, value);
|
||||||
|
if (result == kStoreFailureArrayDetached) {
|
||||||
|
goto IfDetached;
|
||||||
|
}
|
||||||
|
assert(result == kStoreSucceded);
|
||||||
|
}
|
||||||
|
|
||||||
struct LoadStoreFn {
|
|
||||||
loadNumericFn: LoadNumericFn;
|
loadNumericFn: LoadNumericFn;
|
||||||
storeNumericFn: StoreNumericFn;
|
storeNumericFn: StoreNumericFn;
|
||||||
|
storeJSAnyFn: StoreJSAnyFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro GetLoadStoreFnForElementsKind(elementsKind: ElementsKind): LoadStoreFn {
|
macro GetTypedArrayAccessor<T : type extends ElementsKind>():
|
||||||
let loadNumericFn: LoadNumericFn;
|
TypedArrayAccessor {
|
||||||
let storeNumericFn: StoreNumericFn;
|
const loadNumericFn = LoadTypedElement<T>;
|
||||||
|
const storeNumericFn = StoreTypedElementNumeric<T>;
|
||||||
|
const storeJSAnyFn = StoreTypedElementJSAny<T>;
|
||||||
|
return TypedArrayAccessor{loadNumericFn, storeNumericFn, storeJSAnyFn};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro GetTypedArrayAccessor(elementsKind: ElementsKind): TypedArrayAccessor {
|
||||||
if (IsElementsKindGreaterThan(elementsKind, UINT32_ELEMENTS)) {
|
if (IsElementsKindGreaterThan(elementsKind, UINT32_ELEMENTS)) {
|
||||||
if (elementsKind == INT32_ELEMENTS) {
|
if (elementsKind == INT32_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Int32Elements>;
|
return GetTypedArrayAccessor<Int32Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Int32Elements>;
|
|
||||||
} else if (elementsKind == FLOAT32_ELEMENTS) {
|
} else if (elementsKind == FLOAT32_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Float32Elements>;
|
return GetTypedArrayAccessor<Float32Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Float32Elements>;
|
|
||||||
} else if (elementsKind == FLOAT64_ELEMENTS) {
|
} else if (elementsKind == FLOAT64_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Float64Elements>;
|
return GetTypedArrayAccessor<Float64Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Float64Elements>;
|
|
||||||
} else if (elementsKind == UINT8_CLAMPED_ELEMENTS) {
|
} else if (elementsKind == UINT8_CLAMPED_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Uint8ClampedElements>;
|
return GetTypedArrayAccessor<Uint8ClampedElements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Uint8ClampedElements>;
|
|
||||||
} else if (elementsKind == BIGUINT64_ELEMENTS) {
|
} else if (elementsKind == BIGUINT64_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<BigUint64Elements>;
|
return GetTypedArrayAccessor<BigUint64Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<BigUint64Elements>;
|
|
||||||
} else if (elementsKind == BIGINT64_ELEMENTS) {
|
} else if (elementsKind == BIGINT64_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<BigInt64Elements>;
|
return GetTypedArrayAccessor<BigInt64Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<BigInt64Elements>;
|
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (elementsKind == UINT8_ELEMENTS) {
|
if (elementsKind == UINT8_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Uint8Elements>;
|
return GetTypedArrayAccessor<Uint8Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Uint8Elements>;
|
|
||||||
} else if (elementsKind == INT8_ELEMENTS) {
|
} else if (elementsKind == INT8_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Int8Elements>;
|
return GetTypedArrayAccessor<Int8Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Int8Elements>;
|
|
||||||
} else if (elementsKind == UINT16_ELEMENTS) {
|
} else if (elementsKind == UINT16_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Uint16Elements>;
|
return GetTypedArrayAccessor<Uint16Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Uint16Elements>;
|
|
||||||
} else if (elementsKind == INT16_ELEMENTS) {
|
} else if (elementsKind == INT16_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Int16Elements>;
|
return GetTypedArrayAccessor<Int16Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Int16Elements>;
|
|
||||||
} else if (elementsKind == UINT32_ELEMENTS) {
|
} else if (elementsKind == UINT32_ELEMENTS) {
|
||||||
loadNumericFn = LoadTypedElement<Uint32Elements>;
|
return GetTypedArrayAccessor<Uint32Elements>();
|
||||||
storeNumericFn = StoreTypedElementNumeric<Uint32Elements>;
|
|
||||||
} else {
|
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return LoadStoreFn{loadNumericFn, storeNumericFn};
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
|
extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
|
||||||
@ -179,11 +201,11 @@ namespace typed_array {
|
|||||||
macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray):
|
macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray):
|
||||||
AttachedJSTypedArrayWitness {
|
AttachedJSTypedArrayWitness {
|
||||||
const kind = array.elements_kind;
|
const kind = array.elements_kind;
|
||||||
const accessors: LoadStoreFn = GetLoadStoreFnForElementsKind(kind);
|
const accessor: TypedArrayAccessor = GetTypedArrayAccessor(kind);
|
||||||
return AttachedJSTypedArrayWitness{
|
return AttachedJSTypedArrayWitness{
|
||||||
stable: array,
|
stable: array,
|
||||||
unstable: array,
|
unstable: array,
|
||||||
loadfn: accessors.loadNumericFn
|
loadfn: accessor.loadNumericFn
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,10 +253,25 @@ namespace typed_array {
|
|||||||
|
|
||||||
builtin StoreTypedElementNumeric<T : type extends ElementsKind>(
|
builtin StoreTypedElementNumeric<T : type extends ElementsKind>(
|
||||||
context: Context, typedArray: JSTypedArray, index: uintptr,
|
context: Context, typedArray: JSTypedArray, index: uintptr,
|
||||||
value: Numeric): JSAny {
|
value: Numeric): Smi {
|
||||||
StoreJSTypedArrayElementFromNumeric(
|
StoreJSTypedArrayElementFromNumeric(
|
||||||
context, typedArray, index, value, KindForArrayType<T>());
|
context, typedArray, index, value, KindForArrayType<T>());
|
||||||
return Undefined;
|
return kStoreSucceded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns True on sucess or False if the typedArrays was detached.
|
||||||
|
builtin StoreTypedElementJSAny<T : type extends ElementsKind>(
|
||||||
|
context: Context, typedArray: JSTypedArray, index: uintptr,
|
||||||
|
value: JSAny): Smi {
|
||||||
|
try {
|
||||||
|
StoreJSTypedArrayElementFromTagged(
|
||||||
|
context, typedArray, index, value, KindForArrayType<T>())
|
||||||
|
otherwise IfDetached;
|
||||||
|
}
|
||||||
|
label IfDetached {
|
||||||
|
return kStoreFailureArrayDetached;
|
||||||
|
}
|
||||||
|
return kStoreSucceded;
|
||||||
}
|
}
|
||||||
|
|
||||||
transitioning macro CallCompare(
|
transitioning macro CallCompare(
|
||||||
@ -345,10 +382,8 @@ namespace typed_array {
|
|||||||
|
|
||||||
const comparefn: Callable =
|
const comparefn: Callable =
|
||||||
Cast<Callable>(comparefnObj) otherwise unreachable;
|
Cast<Callable>(comparefnObj) otherwise unreachable;
|
||||||
const accessors: LoadStoreFn =
|
const accessor: TypedArrayAccessor =
|
||||||
GetLoadStoreFnForElementsKind(array.elements_kind);
|
GetTypedArrayAccessor(array.elements_kind);
|
||||||
const loadfn = accessors.loadNumericFn;
|
|
||||||
const storefn = accessors.storeNumericFn;
|
|
||||||
|
|
||||||
// Prepare the two work arrays. All numbers are converted to tagged
|
// Prepare the two work arrays. All numbers are converted to tagged
|
||||||
// objects first, and merge sorted between the two FixedArrays.
|
// objects first, and merge sorted between the two FixedArrays.
|
||||||
@ -357,7 +392,7 @@ namespace typed_array {
|
|||||||
const work2: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
|
const work2: FixedArray = AllocateZeroedFixedArray(Convert<intptr>(len));
|
||||||
|
|
||||||
for (let i: uintptr = 0; i < len; ++i) {
|
for (let i: uintptr = 0; i < len; ++i) {
|
||||||
const element: Numeric = loadfn(context, array, i);
|
const element: Numeric = accessor.LoadNumeric(context, array, i);
|
||||||
work1.objects[i] = element;
|
work1.objects[i] = element;
|
||||||
work2.objects[i] = element;
|
work2.objects[i] = element;
|
||||||
}
|
}
|
||||||
@ -365,8 +400,10 @@ namespace typed_array {
|
|||||||
TypedArrayMergeSort(work2, 0, len, work1, array, comparefn);
|
TypedArrayMergeSort(work2, 0, len, work1, array, comparefn);
|
||||||
|
|
||||||
// work1 contains the sorted numbers. Write them back.
|
// work1 contains the sorted numbers. Write them back.
|
||||||
for (let i: uintptr = 0; i < len; ++i)
|
for (let i: uintptr = 0; i < len; ++i) {
|
||||||
storefn(context, array, i, UnsafeCast<Numeric>(work1.objects[i]));
|
accessor.StoreNumeric(
|
||||||
|
context, array, i, UnsafeCast<Numeric>(work1.objects[i]));
|
||||||
|
}
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
@ -7512,44 +7512,6 @@ TNode<JSReceiver> CodeStubAssembler::ToObject_Inline(TNode<Context> context,
|
|||||||
return result.value();
|
return result.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<Smi> CodeStubAssembler::ToSmiLength(TNode<Context> context,
|
|
||||||
TNode<Object> input,
|
|
||||||
Label* range_error) {
|
|
||||||
TVARIABLE(Smi, result);
|
|
||||||
Label to_integer(this), negative_check(this),
|
|
||||||
heap_number_negative_check(this), return_zero(this), done(this);
|
|
||||||
|
|
||||||
GotoIfNot(TaggedIsSmi(input), &to_integer);
|
|
||||||
result = CAST(input);
|
|
||||||
Goto(&negative_check);
|
|
||||||
|
|
||||||
BIND(&to_integer);
|
|
||||||
{
|
|
||||||
TNode<Number> integer_input = CAST(
|
|
||||||
CallBuiltin(Builtins::kToInteger_TruncateMinusZero, context, input));
|
|
||||||
GotoIfNot(TaggedIsSmi(integer_input), &heap_number_negative_check);
|
|
||||||
result = CAST(integer_input);
|
|
||||||
Goto(&negative_check);
|
|
||||||
|
|
||||||
// integer_input can still be a negative HeapNumber here.
|
|
||||||
BIND(&heap_number_negative_check);
|
|
||||||
TNode<HeapNumber> heap_number_input = CAST(integer_input);
|
|
||||||
Branch(IsTrue(CallBuiltin(Builtins::kLessThan, context, heap_number_input,
|
|
||||||
SmiConstant(0))),
|
|
||||||
&return_zero, range_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&negative_check);
|
|
||||||
Branch(SmiLessThan(result.value(), SmiConstant(0)), &return_zero, &done);
|
|
||||||
|
|
||||||
BIND(&return_zero);
|
|
||||||
result = SmiConstant(0);
|
|
||||||
Goto(&done);
|
|
||||||
|
|
||||||
BIND(&done);
|
|
||||||
return result.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
TNode<Number> CodeStubAssembler::ToLength_Inline(SloppyTNode<Context> context,
|
TNode<Number> CodeStubAssembler::ToLength_Inline(SloppyTNode<Context> context,
|
||||||
SloppyTNode<Object> input) {
|
SloppyTNode<Object> input) {
|
||||||
TNode<Smi> smi_zero = SmiConstant(0);
|
TNode<Smi> smi_zero = SmiConstant(0);
|
||||||
@ -10593,11 +10555,7 @@ TNode<TIndex> CodeStubAssembler::BuildFastLoop(const VariableList& vars,
|
|||||||
return var.value();
|
return var.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate BuildFastLoop for Smi and IntPtrT.
|
// Instantiate BuildFastLoop for IntPtrT and UintPtrT.
|
||||||
template TNode<Smi> CodeStubAssembler::BuildFastLoop<Smi>(
|
|
||||||
const VariableList& vars, TNode<Smi> start_index, TNode<Smi> end_index,
|
|
||||||
const FastLoopBody<Smi>& body, int increment,
|
|
||||||
IndexAdvanceMode advance_mode);
|
|
||||||
template TNode<IntPtrT> CodeStubAssembler::BuildFastLoop<IntPtrT>(
|
template TNode<IntPtrT> CodeStubAssembler::BuildFastLoop<IntPtrT>(
|
||||||
const VariableList& vars, TNode<IntPtrT> start_index,
|
const VariableList& vars, TNode<IntPtrT> start_index,
|
||||||
TNode<IntPtrT> end_index, const FastLoopBody<IntPtrT>& body, int increment,
|
TNode<IntPtrT> end_index, const FastLoopBody<IntPtrT>& body, int increment,
|
||||||
|
@ -2659,10 +2659,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
|||||||
kTruncateMinusZero,
|
kTruncateMinusZero,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ES6 7.1.15 ToLength, but jumps to range_error if the result is not a Smi.
|
|
||||||
TNode<Smi> ToSmiLength(TNode<Context> context, TNode<Object> input,
|
|
||||||
Label* range_error);
|
|
||||||
|
|
||||||
// ES6 7.1.15 ToLength, but with inlined fast path.
|
// ES6 7.1.15 ToLength, but with inlined fast path.
|
||||||
TNode<Number> ToLength_Inline(SloppyTNode<Context> context,
|
TNode<Number> ToLength_Inline(SloppyTNode<Context> context,
|
||||||
SloppyTNode<Object> input);
|
SloppyTNode<Object> input);
|
||||||
|
@ -387,7 +387,7 @@ void PrintTypedArrayElements(std::ostream& os, const ElementType* data_ptr,
|
|||||||
if (previous_index != i - 1) {
|
if (previous_index != i - 1) {
|
||||||
ss << '-' << (i - 1);
|
ss << '-' << (i - 1);
|
||||||
}
|
}
|
||||||
os << std::setw(12) << ss.str() << ": " << previous_value;
|
os << std::setw(12) << ss.str() << ": " << +previous_value;
|
||||||
previous_index = i;
|
previous_index = i;
|
||||||
previous_value = value;
|
previous_value = value;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user