[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:
Igor Sheludko 2019-10-31 19:20:41 +01:00 committed by Commit Bot
parent b701330857
commit bff5a05319
13 changed files with 358 additions and 373 deletions

View File

@ -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",

View File

@ -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;

View File

@ -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) \

View File

@ -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

View File

@ -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

View File

@ -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';

View File

@ -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

View 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);
}
}
}

View File

@ -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;

View File

@ -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;
} }

View File

@ -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,

View File

@ -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);

View File

@ -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;
} }