Commit abfdb819 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by Commit Bot

[builtins] Fix optional arguments for %TypedArray%.from

Since ES6, optional arguments are treated the same as undefined. This
was recently cleaned up in https://github.com/tc39/ecma262/pull/1411.
The current Torque implementation of %TypedArray%.from incorrectly
interpreted the old (and confusing) language of a parameter being "not
present" as testing using arguments.length instead of testing directly
for undefined.

Bug: v8:10458
Change-Id: I055f1fa3be570a31a4f7369ba5b51b7d6b022f0a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2168674
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67454}
parent 910d2d79
...@@ -21,24 +21,22 @@ namespace typed_array { ...@@ -21,24 +21,22 @@ namespace typed_array {
...arguments): JSTypedArray { ...arguments): JSTypedArray {
try { try {
const source: JSAny = arguments[0]; const source: JSAny = arguments[0];
const mapfnObj: JSAny = arguments[1];
const thisArg = arguments[2];
// 1. Let C be the this value. // 1. Let C be the this value.
// 2. If IsConstructor(C) is false, throw a TypeError exception. // 2. If IsConstructor(C) is false, throw a TypeError exception.
const constructor = Cast<Constructor>(receiver) otherwise NotConstructor; const constructor = Cast<Constructor>(receiver) otherwise NotConstructor;
// 3. If mapfn is present and mapfn is not undefined, then // 3. If mapfn is undefined, then let mapping be false.
// 4. Else,
// a. If IsCallable(mapfn) is false, throw a TypeError exception. // a. If IsCallable(mapfn) is false, throw a TypeError exception.
// b. Let mapping be true. // b. Let mapping be true.
// 4. Else, let mapping be false. const mapping: bool = mapfnObj != Undefined;
const mapping: bool = arguments.length > 1;
const mapfnObj: JSAny = mapping ? arguments[1] : Undefined;
if (mapping && !Is<Callable>(mapfnObj)) deferred { if (mapping && !Is<Callable>(mapfnObj)) deferred {
ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfnObj); ThrowTypeError(MessageTemplate::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 // 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 // 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 // from a JSArray into a TypedArray, so we use that when possible. We only
...@@ -52,7 +50,7 @@ namespace typed_array { ...@@ -52,7 +50,7 @@ namespace typed_array {
let finalSource: JSAny; let finalSource: JSAny;
try { try {
// 6. Let usingIterator be ? GetMethod(source, @@iterator). // 5. Let usingIterator be ? GetMethod(source, @@iterator).
// TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports // TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
// labels. // labels.
const usingIterator = GetMethod(source, IteratorSymbolConstant()) const usingIterator = GetMethod(source, IteratorSymbolConstant())
...@@ -102,7 +100,7 @@ namespace typed_array { ...@@ -102,7 +100,7 @@ namespace typed_array {
finalSource = source; finalSource = source;
} }
label UseUserProvidedIterator { label UseUserProvidedIterator {
// 7. If usingIterator is not undefined, then // 6. If usingIterator is not undefined, then
// a. Let values be ? IterableToList(source, usingIterator). // a. Let values be ? IterableToList(source, usingIterator).
// b. Let len be the number of elements in values. // b. Let len be the number of elements in values.
const values: JSArray = IterableToList(source, usingIterator); const values: JSArray = IterableToList(source, usingIterator);
...@@ -112,13 +110,13 @@ namespace typed_array { ...@@ -112,13 +110,13 @@ namespace typed_array {
} }
} }
label IteratorIsUndefined { label IteratorIsUndefined {
// 8. NOTE: source is not an Iterable so assume it is already an // 7. NOTE: source is not an Iterable so assume it is already an
// array-like object. // array-like object.
// 9. Let arrayLike be ! ToObject(source). // 8. Let arrayLike be ! ToObject(source).
const arrayLike: JSReceiver = ToObject_Inline(context, source); const arrayLike: JSReceiver = ToObject_Inline(context, source);
// 10. Let len be ? ToLength(? Get(arrayLike, "length")). // 9. Let len be ? LengthOfArrayLike(arrayLike).
const length = GetLengthProperty(arrayLike); const length = GetLengthProperty(arrayLike);
try { try {
...@@ -136,7 +134,7 @@ namespace typed_array { ...@@ -136,7 +134,7 @@ namespace typed_array {
const finalLengthNum = Convert<Number>(finalLength); const finalLengthNum = Convert<Number>(finalLength);
// 7c/11. Let targetObj be ? TypedArrayCreate(C, «len»). // 6c/10. Let targetObj be ? TypedArrayCreate(C, «len»).
const targetObj = TypedArrayCreateByLength( const targetObj = TypedArrayCreateByLength(
constructor, finalLengthNum, kBuiltinNameFrom); constructor, finalLengthNum, kBuiltinNameFrom);
...@@ -155,32 +153,32 @@ namespace typed_array { ...@@ -155,32 +153,32 @@ namespace typed_array {
const accessor: TypedArrayAccessor = const accessor: TypedArrayAccessor =
GetTypedArrayAccessor(targetObj.elements_kind); GetTypedArrayAccessor(targetObj.elements_kind);
// 7d-7e and 12-13. // 6d-6e and 11-12.
// 12. Let k be 0. // 11. Let k be 0.
// 13. Repeat, while k < len // 12. Repeat, while k < len
for (let k: uintptr = 0; k < finalLength; k++) { for (let k: uintptr = 0; k < finalLength; k++) {
// 13a. Let Pk be ! ToString(k). // 12a. Let Pk be ! ToString(k).
const kNum = Convert<Number>(k); const kNum = Convert<Number>(k);
// 13b. Let kValue be ? Get(arrayLike, Pk). // 12b. Let kValue be ? Get(arrayLike, Pk).
const kValue: JSAny = GetProperty(finalSource, kNum); const kValue: JSAny = GetProperty(finalSource, kNum);
let mappedValue: JSAny; let mappedValue: JSAny;
// 13c. If mapping is true, then // 12c. If mapping is true, then
if (mapping) { if (mapping) {
// i. Let mappedValue be ? Call(mapfn, T, « kValue, k »). // i. Let mappedValue be ? Call(mapfn, T, « kValue, k »).
mappedValue = Call(context, mapfn, thisArg, kValue, kNum); mappedValue = Call(context, mapfn, thisArg, kValue, kNum);
} else { } else {
// 13d. Else, let mappedValue be kValue. // 12d. Else, let mappedValue be kValue.
mappedValue = kValue; mappedValue = kValue;
} }
// 13e. Perform ? Set(targetObj, Pk, mappedValue, true). // 12e. Perform ? Set(targetObj, Pk, mappedValue, true).
// Buffer may be detached during executing ToNumber/ToBigInt. // Buffer may be detached during executing ToNumber/ToBigInt.
accessor.StoreJSAny(context, targetObj, k, mappedValue) accessor.StoreJSAny(context, targetObj, k, mappedValue)
otherwise IfDetached; otherwise IfDetached;
// 13f. Set k to k + 1. (done by the loop). // 12f. Set k to k + 1. (done by the loop).
} }
return targetObj; return targetObj;
} }
......
// Copyright 2020 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.
var typedArrayConstructors = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Uint32Array,
Int32Array,
Uint8ClampedArray,
Float32Array,
Float64Array
];
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]);
}
}
for (var constructor of typedArrayConstructors) {
let ta = new constructor([1,2,3]);
assertArrayLikeEquals(constructor.from([1,2,3]), ta, constructor);
assertArrayLikeEquals(constructor.from([1,2,3], undefined),
ta, constructor);
assertArrayLikeEquals(constructor.from([1,2,3], undefined, undefined),
ta, constructor);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment