[typedarray] Port TypedArray.from to CSA.
Factor out IterableToList into a helper stub to save space. There are two callers now, TypedArrayFrom and ConstructByIterable, and it is ~2.5kb so we save space by doing this. Increase test coverage to cover more of the branching in CSA. This is doesn't follow the control flow in the spec exactly - see the big code comment for an explanation. Change-Id: Ief39e93c4202cb7bf0e28a39dc6aa81b8b9c59d2 Reviewed-on: https://chromium-review.googlesource.com/908755 Commit-Queue: Peter Marshall <petermarshall@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#51377}
This commit is contained in:
parent
19e65114a1
commit
81a3742a88
@ -2947,6 +2947,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
|
||||
SimpleInstallFunction(typed_array_fun, "of", Builtins::kTypedArrayOf, 0,
|
||||
false);
|
||||
SimpleInstallFunction(typed_array_fun, "from", Builtins::kTypedArrayFrom, 1,
|
||||
false);
|
||||
|
||||
// Setup %TypedArrayPrototype%.
|
||||
Handle<JSObject> prototype(
|
||||
|
@ -1071,6 +1071,7 @@ namespace internal {
|
||||
TFJ(SymbolPrototypeValueOf, 0) \
|
||||
\
|
||||
/* TypedArray */ \
|
||||
TFS(IterableToList, kIterable, kIteratorFn) \
|
||||
TFS(TypedArrayInitialize, kHolder, kLength, kElementSize, kInitialize) \
|
||||
TFS(TypedArrayInitializeWithBuffer, kHolder, kLength, kBuffer, kElementSize, \
|
||||
kByteOffset) \
|
||||
@ -1140,6 +1141,8 @@ namespace internal {
|
||||
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 %TypedArray%.of */ \
|
||||
TFJ(TypedArrayOf, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 %TypedArray%.from */ \
|
||||
TFJ(TypedArrayFrom, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
\
|
||||
/* Wasm */ \
|
||||
ASM(WasmCompileLazy) \
|
||||
|
@ -622,68 +622,11 @@ void TypedArrayBuiltinsAssembler::ConstructByIterable(
|
||||
CSA_ASSERT(this, IsCallable(iterator_fn));
|
||||
Label fast_path(this), slow_path(this), done(this);
|
||||
|
||||
TVARIABLE(JSReceiver, array_like);
|
||||
TVARIABLE(Object, initial_length);
|
||||
|
||||
// This is a fast-path for ignoring the iterator.
|
||||
// TODO(petermarshall): Port to CSA.
|
||||
Node* elided =
|
||||
CallRuntime(Runtime::kIterableToListCanBeElided, context, iterable);
|
||||
CSA_ASSERT(this, IsBoolean(elided));
|
||||
Branch(IsTrue(elided), &fast_path, &slow_path);
|
||||
|
||||
BIND(&fast_path);
|
||||
{
|
||||
TNode<JSArray> js_array_iterable = CAST(iterable);
|
||||
// This .length access is unobservable, because it being observable would
|
||||
// mean that iteration has side effects, and we wouldn't reach this path.
|
||||
array_like = js_array_iterable;
|
||||
initial_length = LoadJSArrayLength(js_array_iterable);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&slow_path);
|
||||
{
|
||||
IteratorBuiltinsAssembler iterator_assembler(state());
|
||||
|
||||
// 1. Let iteratorRecord be ? GetIterator(items, method).
|
||||
IteratorRecord iterator_record =
|
||||
iterator_assembler.GetIterator(context, iterable, iterator_fn);
|
||||
|
||||
// 2. Let values be a new empty List.
|
||||
GrowableFixedArray values(state());
|
||||
|
||||
Variable* vars[] = {values.var_array(), values.var_length(),
|
||||
values.var_capacity()};
|
||||
Label loop_start(this, 3, vars), loop_end(this);
|
||||
Goto(&loop_start);
|
||||
// 3. Let next be true.
|
||||
// 4. Repeat, while next is not false
|
||||
BIND(&loop_start);
|
||||
{
|
||||
// a. Set next to ? IteratorStep(iteratorRecord).
|
||||
TNode<Object> next = CAST(
|
||||
iterator_assembler.IteratorStep(context, iterator_record, &loop_end));
|
||||
// b. If next is not false, then
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
TNode<Object> next_value =
|
||||
CAST(iterator_assembler.IteratorValue(context, next));
|
||||
// ii. Append nextValue to the end of the List values.
|
||||
values.Push(next_value);
|
||||
Goto(&loop_start);
|
||||
}
|
||||
BIND(&loop_end);
|
||||
|
||||
// 5. Return values.
|
||||
TNode<JSArray> js_array_values = values.ToJSArray(context);
|
||||
array_like = js_array_values;
|
||||
initial_length = LoadJSArrayLength(js_array_values);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
ConstructByArrayLike(context, holder, array_like.value(),
|
||||
initial_length.value(), element_size);
|
||||
TNode<JSArray> array_like = CAST(
|
||||
CallBuiltin(Builtins::kIterableToList, context, iterable, iterator_fn));
|
||||
TNode<Object> initial_length = LoadJSArrayLength(array_like);
|
||||
ConstructByArrayLike(context, holder, array_like, initial_length,
|
||||
element_size);
|
||||
}
|
||||
|
||||
TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
|
||||
@ -1632,6 +1575,251 @@ TF_BUILTIN(TypedArrayOf, TypedArrayBuiltinsAssembler) {
|
||||
ThrowTypeError(context, MessageTemplate::kNotConstructor, receiver);
|
||||
}
|
||||
|
||||
TF_BUILTIN(IterableToList, TypedArrayBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
TNode<Object> iterable = CAST(Parameter(Descriptor::kIterable));
|
||||
TNode<Object> iterator_fn = CAST(Parameter(Descriptor::kIteratorFn));
|
||||
|
||||
Label fast_path(this), slow_path(this), done(this);
|
||||
|
||||
TVARIABLE(JSArray, created_list);
|
||||
|
||||
// This is a fast-path for ignoring the iterator.
|
||||
// TODO(petermarshall): Port to CSA.
|
||||
Node* elided =
|
||||
CallRuntime(Runtime::kIterableToListCanBeElided, context, iterable);
|
||||
CSA_ASSERT(this, IsBoolean(elided));
|
||||
Branch(IsTrue(elided), &fast_path, &slow_path);
|
||||
|
||||
BIND(&fast_path);
|
||||
{
|
||||
created_list = CAST(iterable);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&slow_path);
|
||||
{
|
||||
IteratorBuiltinsAssembler iterator_assembler(state());
|
||||
|
||||
// 1. Let iteratorRecord be ? GetIterator(items, method).
|
||||
IteratorRecord iterator_record =
|
||||
iterator_assembler.GetIterator(context, iterable, iterator_fn);
|
||||
|
||||
// 2. Let values be a new empty List.
|
||||
GrowableFixedArray values(state());
|
||||
|
||||
Variable* vars[] = {values.var_array(), values.var_length(),
|
||||
values.var_capacity()};
|
||||
Label loop_start(this, 3, vars), loop_end(this);
|
||||
Goto(&loop_start);
|
||||
// 3. Let next be true.
|
||||
// 4. Repeat, while next is not false
|
||||
BIND(&loop_start);
|
||||
{
|
||||
// a. Set next to ? IteratorStep(iteratorRecord).
|
||||
TNode<Object> next = CAST(
|
||||
iterator_assembler.IteratorStep(context, iterator_record, &loop_end));
|
||||
// b. If next is not false, then
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
TNode<Object> next_value =
|
||||
CAST(iterator_assembler.IteratorValue(context, next));
|
||||
// ii. Append nextValue to the end of the List values.
|
||||
values.Push(next_value);
|
||||
Goto(&loop_start);
|
||||
}
|
||||
BIND(&loop_end);
|
||||
|
||||
// 5. Return values.
|
||||
TNode<JSArray> js_array_values = values.ToJSArray(context);
|
||||
created_list = js_array_values;
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
Return(created_list.value());
|
||||
}
|
||||
|
||||
// ES6 #sec-%typedarray%.from
|
||||
TF_BUILTIN(TypedArrayFrom, TypedArrayBuiltinsAssembler) {
|
||||
TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext));
|
||||
|
||||
Label check_iterator(this), from_array_like(this), fast_path(this),
|
||||
slow_path(this), create_typed_array(this),
|
||||
if_not_constructor(this, Label::kDeferred),
|
||||
if_map_fn_not_callable(this, Label::kDeferred),
|
||||
if_iterator_fn_not_callable(this, Label::kDeferred),
|
||||
unreachable(this, Label::kDeferred);
|
||||
|
||||
CodeStubArguments args(
|
||||
this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
|
||||
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(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_iterator);
|
||||
|
||||
// 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(map_fn), &if_map_fn_not_callable);
|
||||
mapping = Int32TrueConstant();
|
||||
Goto(&check_iterator);
|
||||
|
||||
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.
|
||||
|
||||
BIND(&check_iterator);
|
||||
{
|
||||
// 6. Let usingIterator be ? GetMethod(source, @@iterator).
|
||||
TNode<Object> iterator_fn =
|
||||
CAST(GetMethod(context, source, isolate()->factory()->iterator_symbol(),
|
||||
&from_array_like));
|
||||
GotoIf(TaggedIsSmi(iterator_fn), &if_iterator_fn_not_callable);
|
||||
GotoIfNot(IsCallable(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<Object> 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);
|
||||
{
|
||||
Label if_length_not_smi(this, Label::kDeferred);
|
||||
final_source = CAST(source);
|
||||
|
||||
// 10. Let len be ? ToLength(? Get(arrayLike, "length")).
|
||||
Node* raw_length =
|
||||
GetProperty(context, final_source.value(), LengthStringConstant());
|
||||
final_length = CAST(ToSmiLength(raw_length, context, &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 = CreateByLength(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<Word32T> elements_kind = LoadElementsKind(target_obj.value());
|
||||
|
||||
// 7e/13 : Copy the elements
|
||||
BuildFastLoop(
|
||||
SmiConstant(0), final_length.value(),
|
||||
[&](Node* index) {
|
||||
TNode<String> const index_string = NumberToString(CAST(index));
|
||||
TNode<Object> const k_value =
|
||||
CAST(GetProperty(context, final_source.value(), index_string));
|
||||
|
||||
TNode<Object> const mapped_value =
|
||||
CAST(CallJS(CodeFactory::Call(isolate()), context, map_fn, this_arg,
|
||||
k_value, index));
|
||||
|
||||
TNode<Number> const number_value =
|
||||
ToNumber_Inline(context, mapped_value);
|
||||
// map_fn or ToNumber could have executed JavaScript code, but
|
||||
// they could not have accessed the new typed array.
|
||||
DebugSanityCheckTypedArrayIndex(target_obj.value(), index);
|
||||
|
||||
DispatchTypedArrayByElementsKind(
|
||||
elements_kind,
|
||||
[&](ElementsKind kind, int size, int typed_array_fun_index) {
|
||||
if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
|
||||
// TODO(jkummerow): Add inline support.
|
||||
CallRuntime(Runtime::kSetProperty, context, target_obj.value(),
|
||||
index, number_value,
|
||||
SmiConstant(LanguageMode::kSloppy));
|
||||
} else {
|
||||
// Since we can guarantee that "number_value" is Number type,
|
||||
// PrepareValueForWriteToTypedArray cannot bail out.
|
||||
Node* const final_value = PrepareValueForWriteToTypedArray(
|
||||
number_value, kind, &unreachable);
|
||||
|
||||
// GC may move backing store in map_fn, thus load backing
|
||||
// store in each iteration of this loop.
|
||||
TNode<IntPtrT> const backing_store =
|
||||
UncheckedCast<IntPtrT>(LoadDataPtr(target_obj.value()));
|
||||
StoreElement(backing_store, kind, index, final_value,
|
||||
SMI_PARAMETERS);
|
||||
}
|
||||
});
|
||||
},
|
||||
1, ParameterMode::SMI_PARAMETERS, 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(&unreachable);
|
||||
Unreachable();
|
||||
}
|
||||
|
||||
// ES %TypedArray%.prototype.filter
|
||||
TF_BUILTIN(TypedArrayPrototypeFilter, TypedArrayBuiltinsAssembler) {
|
||||
const char* method_name = "%TypedArray%.prototype.filter";
|
||||
|
@ -956,13 +956,13 @@ Node* CodeAssembler::Projection(int index, Node* value) {
|
||||
|
||||
void CodeAssembler::GotoIfException(Node* node, Label* if_exception,
|
||||
Variable* exception_var) {
|
||||
DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
|
||||
|
||||
if (if_exception == nullptr) {
|
||||
// If no handler is supplied, don't add continuations
|
||||
return;
|
||||
}
|
||||
|
||||
DCHECK(!node->op()->HasProperty(Operator::kNoThrow));
|
||||
|
||||
Label success(this), exception(this, Label::kDeferred);
|
||||
success.MergeVariables();
|
||||
exception.MergeVariables();
|
||||
|
@ -3504,8 +3504,12 @@ class TypedElementsAccessor
|
||||
isolate, NewTypeError(MessageTemplate::kBigIntToNumber));
|
||||
}
|
||||
}
|
||||
CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset);
|
||||
return *isolate->factory()->undefined_value();
|
||||
// If we have to copy more elements than we have in the source, we need to
|
||||
// do special handling and conversion; that happens in the slow case.
|
||||
if (length + offset <= source_ta->length_value()) {
|
||||
CopyElementsFromTypedArray(*source_ta, *destination_ta, length, offset);
|
||||
return *isolate->factory()->undefined_value();
|
||||
}
|
||||
}
|
||||
|
||||
// Fast cases for packed numbers kinds where we don't need to allocate.
|
||||
|
@ -124,57 +124,6 @@ DEFINE_METHOD(
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// ES#sec-iterabletoarraylike Runtime Semantics: IterableToArrayLike( items )
|
||||
function IterableToArrayLike(items) {
|
||||
var iterable = GetMethod(items, iteratorSymbol);
|
||||
if (!IS_UNDEFINED(iterable)) {
|
||||
var internal_array = new InternalArray();
|
||||
var i = 0;
|
||||
for (var value of
|
||||
{ [iteratorSymbol]() { return GetIterator(items, iterable) } }) {
|
||||
internal_array[i] = value;
|
||||
i++;
|
||||
}
|
||||
var array = [];
|
||||
%MoveArrayContents(internal_array, array);
|
||||
return array;
|
||||
}
|
||||
return TO_OBJECT(items);
|
||||
}
|
||||
|
||||
|
||||
// ES#sec-%typedarray%.from
|
||||
// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
|
||||
DEFINE_METHOD_LEN(
|
||||
GlobalTypedArray,
|
||||
'from'(source, mapfn, thisArg) {
|
||||
if (!%IsConstructor(this)) throw %make_type_error(kNotConstructor, this);
|
||||
var mapping;
|
||||
if (!IS_UNDEFINED(mapfn)) {
|
||||
if (!IS_CALLABLE(mapfn)) throw %make_type_error(kCalledNonCallable, this);
|
||||
mapping = true;
|
||||
} else {
|
||||
mapping = false;
|
||||
}
|
||||
var arrayLike = IterableToArrayLike(source);
|
||||
var length = TO_LENGTH(arrayLike.length);
|
||||
var targetObject = TypedArrayCreate(this, length);
|
||||
var value, mappedValue;
|
||||
for (var i = 0; i < length; i++) {
|
||||
value = arrayLike[i];
|
||||
if (mapping) {
|
||||
mappedValue = %_Call(mapfn, thisArg, value, i);
|
||||
} else {
|
||||
mappedValue = value;
|
||||
}
|
||||
targetObject[i] = mappedValue;
|
||||
}
|
||||
return targetObject;
|
||||
},
|
||||
1 /* Set function length. */
|
||||
);
|
||||
|
||||
// TODO(bmeurer): Migrate this to a proper builtin.
|
||||
function TypedArrayConstructor() {
|
||||
throw %make_type_error(kConstructAbstractClass, "TypedArray");
|
||||
|
@ -1231,7 +1231,7 @@ RUNTIME_FUNCTION(Runtime_CreateDataProperty) {
|
||||
RUNTIME_FUNCTION(Runtime_IterableToListCanBeElided) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, obj, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(HeapObject, obj, 0);
|
||||
|
||||
if (!obj->IsJSObject()) return isolate->heap()->ToBoolean(false);
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
var typedArrayConstructors = [
|
||||
Uint8Array,
|
||||
Int8Array,
|
||||
@ -14,19 +16,65 @@ var typedArrayConstructors = [
|
||||
Float64Array
|
||||
];
|
||||
|
||||
function defaultValue(constr) {
|
||||
if (constr == Float32Array || constr == Float64Array) return NaN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function assertArrayLikeEquals(value, expected, type) {
|
||||
assertEquals(value.__proto__, type.prototype);
|
||||
assertEquals(expected.length, value.length);
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
assertEquals(expected[i], value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
(function() {
|
||||
var source = [-0, 0, 2**40, 2**41, 2**42];
|
||||
var arr = Float64Array.from(source);
|
||||
assertArrayLikeEquals(arr, source, Float64Array);
|
||||
|
||||
arr = Float32Array.from(source);
|
||||
assertArrayLikeEquals(arr, source, Float32Array);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var source = [-0, 0, , 2**42];
|
||||
var expected = [-0, 0, NaN, 2**42];
|
||||
var arr = Float64Array.from(source);
|
||||
assertArrayLikeEquals(arr, expected, Float64Array);
|
||||
|
||||
arr = Float32Array.from(source);
|
||||
assertArrayLikeEquals(arr, expected, Float32Array);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var source = {0: -0, 1: 0, 2: 2**40, 3: 2**41, 4: 2**42, length: 5};
|
||||
var expected = [-0, 0, 2**40, 2**41, 2**42];
|
||||
var arr = Float64Array.from(source);
|
||||
assertArrayLikeEquals(arr, expected, Float64Array);
|
||||
|
||||
arr = Float32Array.from(source);
|
||||
assertArrayLikeEquals(arr, expected, Float32Array);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
var source = [-0, 0, , 2**42];
|
||||
Object.getPrototypeOf(source)[2] = 27;
|
||||
var expected = [-0, 0, 27, 2**42];
|
||||
var arr = Float64Array.from(source);
|
||||
assertArrayLikeEquals(arr, expected, Float64Array);
|
||||
|
||||
arr = Float32Array.from(source);
|
||||
assertArrayLikeEquals(arr, expected, Float32Array);
|
||||
})();
|
||||
|
||||
for (var constructor of typedArrayConstructors) {
|
||||
assertEquals(1, constructor.from.length);
|
||||
|
||||
// TypedArray.from only callable on this subclassing %TypedArray%
|
||||
assertThrows(function () {constructor.from.call(Array, [])}, TypeError);
|
||||
|
||||
function assertArrayLikeEquals(value, expected, type) {
|
||||
assertEquals(value.__proto__, type.prototype);
|
||||
assertEquals(expected.length, value.length);
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
assertEquals(expected[i], value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Assert that calling mapfn with / without thisArg in sloppy and strict modes
|
||||
// works as expected.
|
||||
@ -47,6 +95,14 @@ for (var constructor of typedArrayConstructors) {
|
||||
assertThrows(function() {constructor.from.call(1, [])}, TypeError);
|
||||
assertThrows(function() {constructor.from.call(undefined, [])}, TypeError);
|
||||
|
||||
// Use a map function that returns non-numbers.
|
||||
function mapper(value, index) {
|
||||
return String.fromCharCode(value);
|
||||
}
|
||||
var d = defaultValue(constructor);
|
||||
assertArrayLikeEquals(
|
||||
constructor.from([72, 69, 89], mapper), [d, d, d], constructor);
|
||||
|
||||
// Converting from various other types, demonstrating that it can
|
||||
// operate on array-like objects as well as iterables.
|
||||
// TODO(littledan): constructors should have similar flexibility.
|
||||
@ -72,12 +128,50 @@ for (var constructor of typedArrayConstructors) {
|
||||
assertArrayLikeEquals(constructor.from(generator()),
|
||||
[4, 5, 6], constructor);
|
||||
|
||||
// Check mapper is used for non-iterator case.
|
||||
function mapper2(value, index) {
|
||||
return value + 1;
|
||||
}
|
||||
var array_like = {
|
||||
0: 1,
|
||||
1: 2,
|
||||
2: 3,
|
||||
length: 3
|
||||
};
|
||||
assertArrayLikeEquals(constructor.from(array_like, mapper2),
|
||||
[2, 3, 4], constructor);
|
||||
|
||||
// With a smi source. Step 10 will set len = 0.
|
||||
var source = 1;
|
||||
assertArrayLikeEquals(constructor.from(source), [], constructor);
|
||||
|
||||
assertThrows(function() { constructor.from(null); }, TypeError);
|
||||
assertThrows(function() { constructor.from(undefined); }, TypeError);
|
||||
assertThrows(function() { constructor.from([], null); }, TypeError);
|
||||
assertThrows(function() { constructor.from([], 'noncallable'); },
|
||||
TypeError);
|
||||
|
||||
source = [1, 2, 3];
|
||||
source[Symbol.iterator] = undefined;
|
||||
assertArrayLikeEquals(constructor.from(source), source, constructor);
|
||||
|
||||
source = [{ valueOf: function(){ return 42; }}];
|
||||
source[Symbol.iterator] = undefined;
|
||||
assertArrayLikeEquals(constructor.from(source), [42], constructor);
|
||||
|
||||
source = [1, 2, 3];
|
||||
var proxy = new Proxy(source, {});
|
||||
assertArrayLikeEquals(constructor.from(proxy), source, constructor);
|
||||
|
||||
proxy = new Proxy(source, {
|
||||
get: function(target, name) {
|
||||
if (name === Symbol.iterator) return target[name];
|
||||
if (name === "length") return 3;
|
||||
return target[name] + 1;
|
||||
}
|
||||
});
|
||||
assertArrayLikeEquals(constructor.from(proxy), [2, 3, 4], constructor);
|
||||
|
||||
var nullIterator = {};
|
||||
nullIterator[Symbol.iterator] = null;
|
||||
assertArrayLikeEquals(constructor.from(nullIterator), [],
|
||||
@ -90,6 +184,26 @@ for (var constructor of typedArrayConstructors) {
|
||||
|
||||
assertThrows(function() { constructor.from([], null); }, TypeError);
|
||||
|
||||
d = defaultValue(constructor);
|
||||
let ta1 = new constructor(3).fill(1);
|
||||
Object.defineProperty(ta1, "length", {get: function() {
|
||||
return 6;
|
||||
}});
|
||||
delete ta1[Symbol.iterator];
|
||||
delete ta1.__proto__[Symbol.iterator];
|
||||
delete ta1.__proto__.__proto__[Symbol.iterator];
|
||||
assertArrayLikeEquals(constructor.from(ta1), [1, 1, 1, d, d, d], constructor);
|
||||
|
||||
let ta2 = new constructor(3).fill(1);
|
||||
Object.defineProperty(ta2, "length", {get: function() {
|
||||
%ArrayBufferNeuter(ta2.buffer);
|
||||
return 6;
|
||||
}});
|
||||
assertArrayLikeEquals(constructor.from(ta2), [d, d, d, d, d, d], constructor);
|
||||
|
||||
var o1 = {0: 0, 1: 1, 2: 2, length: 6};
|
||||
assertArrayLikeEquals(constructor.from(o1), [0, 1, 2, d, d, d], constructor);
|
||||
|
||||
// Ensure iterator is only accessed once, and only invoked once
|
||||
var called = 0;
|
||||
var arr = [1, 2, 3];
|
||||
|
Loading…
Reference in New Issue
Block a user