[esnext] Implement Array.prototype.{flatten,flatMap} 🥙

Proposal repo: https://tc39.github.io/proposal-flatMap/

Bug: v8:7220
Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I61661fc6d5c39d084ce5c96a9e150e5c26799e2d
Also-By: bmeurer@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/957043
Commit-Queue: Mathias Bynens <mathias@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51967}
This commit is contained in:
Mathias Bynens 2018-03-15 17:51:01 +01:00 committed by Commit Bot
parent f8fb4a5c74
commit 697d39abff
17 changed files with 469 additions and 27 deletions

View File

@ -4251,6 +4251,17 @@ void Genesis::InitializeGlobal_harmony_array_prototype_values() {
NONE);
}
void Genesis::InitializeGlobal_harmony_array_flatten() {
if (!FLAG_harmony_array_flatten) return;
Handle<JSFunction> array_constructor(native_context()->array_function());
Handle<JSObject> array_prototype(
JSObject::cast(array_constructor->instance_prototype()));
SimpleInstallFunction(array_prototype, "flatten",
Builtins::kArrayPrototypeFlatten, 0, false, DONT_ENUM);
SimpleInstallFunction(array_prototype, "flatMap",
Builtins::kArrayPrototypeFlatMap, 1, false, DONT_ENUM);
}
void Genesis::InitializeGlobal_harmony_promise_finally() {
if (!FLAG_harmony_promise_finally) return;

View File

@ -3845,5 +3845,260 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
}
}
namespace {
class ArrayFlattenAssembler : public CodeStubAssembler {
public:
explicit ArrayFlattenAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
// https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
Node* FlattenIntoArray(Node* context, Node* target, Node* source,
Node* source_length, Node* start, Node* depth,
Node* mapper_function = nullptr,
Node* this_arg = nullptr) {
CSA_ASSERT(this, IsJSReceiver(target));
CSA_ASSERT(this, IsJSReceiver(source));
CSA_ASSERT(this, IsNumberPositive(source_length));
CSA_ASSERT(this, IsNumberPositive(start));
CSA_ASSERT(this, IsNumber(depth));
// 1. Let targetIndex be start.
VARIABLE(var_target_index, MachineRepresentation::kTagged, start);
// 2. Let sourceIndex be 0.
VARIABLE(var_source_index, MachineRepresentation::kTagged, SmiConstant(0));
// 3. Repeat...
Label loop(this, {&var_target_index, &var_source_index}), done_loop(this);
Goto(&loop);
BIND(&loop);
{
Node* const source_index = var_source_index.value();
Node* const target_index = var_target_index.value();
// ...while sourceIndex < sourceLen
GotoIfNumberGreaterThanOrEqual(source_index, source_length, &done_loop);
// a. Let P be ! ToString(sourceIndex).
// b. Let exists be ? HasProperty(source, P).
CSA_ASSERT(this, SmiGreaterThanOrEqual(source_index, SmiConstant(0)));
Node* const exists =
HasProperty(source, source_index, context, kHasProperty);
// c. If exists is true, then
Label next(this);
GotoIfNot(IsTrue(exists), &next);
{
// i. Let element be ? Get(source, P).
Node* element = GetProperty(context, source, source_index);
// ii. If mapperFunction is present, then
if (mapper_function != nullptr) {
CSA_ASSERT(this, Word32Or(IsUndefined(mapper_function),
IsCallable(mapper_function)));
DCHECK_NOT_NULL(this_arg);
// 1. Set element to ? Call(mapperFunction, thisArg , « element,
// sourceIndex, source »).
element =
CallJS(CodeFactory::Call(isolate()), context, mapper_function,
this_arg, element, source_index, source);
}
// iii. Let shouldFlatten be false.
Label if_flatten_array(this), if_flatten_proxy(this, Label::kDeferred),
if_noflatten(this);
// iv. If depth > 0, then
GotoIfNumberGreaterThanOrEqual(SmiConstant(0), depth, &if_noflatten);
// 1. Set shouldFlatten to ? IsArray(element).
GotoIf(TaggedIsSmi(element), &if_noflatten);
GotoIf(IsJSArray(element), &if_flatten_array);
GotoIfNot(IsJSProxy(element), &if_noflatten);
Branch(IsTrue(CallRuntime(Runtime::kArrayIsArray, context, element)),
&if_flatten_proxy, &if_noflatten);
BIND(&if_flatten_array);
{
CSA_ASSERT(this, IsJSArray(element));
// 1. Let elementLen be ? ToLength(? Get(element, "length")).
Node* const element_length =
LoadObjectField(element, JSArray::kLengthOffset);
// 2. Set targetIndex to ? FlattenIntoArray(target, element,
// elementLen, targetIndex,
// depth - 1).
var_target_index.Bind(
CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
element_length, target_index, NumberDec(depth)));
Goto(&next);
}
BIND(&if_flatten_proxy);
{
CSA_ASSERT(this, IsJSProxy(element));
// 1. Let elementLen be ? ToLength(? Get(element, "length")).
Node* const element_length = ToLength_Inline(
context, GetProperty(context, element, LengthStringConstant()));
// 2. Set targetIndex to ? FlattenIntoArray(target, element,
// elementLen, targetIndex,
// depth - 1).
var_target_index.Bind(
CallBuiltin(Builtins::kFlattenIntoArray, context, target, element,
element_length, target_index, NumberDec(depth)));
Goto(&next);
}
BIND(&if_noflatten);
{
// 1. If targetIndex >= 2^53-1, throw a TypeError exception.
Label throw_error(this, Label::kDeferred);
GotoIfNumberGreaterThanOrEqual(
target_index, NumberConstant(kMaxSafeInteger), &throw_error);
// 2. Perform ? CreateDataPropertyOrThrow(target,
// ! ToString(targetIndex),
// element).
CallRuntime(Runtime::kCreateDataProperty, context, target,
target_index, element);
// 3. Increase targetIndex by 1.
var_target_index.Bind(NumberInc(target_index));
Goto(&next);
BIND(&throw_error);
ThrowTypeError(context, MessageTemplate::kFlattenPastSafeLength,
source_length, target_index);
}
}
BIND(&next);
// d. Increase sourceIndex by 1.
var_source_index.Bind(NumberInc(source_index));
Goto(&loop);
}
BIND(&done_loop);
return var_target_index.value();
}
};
} // namespace
// https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
TF_BUILTIN(FlattenIntoArray, ArrayFlattenAssembler) {
Node* const context = Parameter(Descriptor::kContext);
Node* const target = Parameter(Descriptor::kTarget);
Node* const source = Parameter(Descriptor::kSource);
Node* const source_length = Parameter(Descriptor::kSourceLength);
Node* const start = Parameter(Descriptor::kStart);
Node* const depth = Parameter(Descriptor::kDepth);
Return(
FlattenIntoArray(context, target, source, source_length, start, depth));
}
// https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
TF_BUILTIN(FlatMapIntoArray, ArrayFlattenAssembler) {
Node* const context = Parameter(Descriptor::kContext);
Node* const target = Parameter(Descriptor::kTarget);
Node* const source = Parameter(Descriptor::kSource);
Node* const source_length = Parameter(Descriptor::kSourceLength);
Node* const start = Parameter(Descriptor::kStart);
Node* const depth = Parameter(Descriptor::kDepth);
Node* const mapper_function = Parameter(Descriptor::kMapperFunction);
Node* const this_arg = Parameter(Descriptor::kThisArg);
Return(FlattenIntoArray(context, target, source, source_length, start, depth,
mapper_function, this_arg));
}
// https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatten
TF_BUILTIN(ArrayPrototypeFlatten, CodeStubAssembler) {
Node* const argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
CodeStubArguments args(this, argc);
Node* const context = Parameter(BuiltinDescriptor::kContext);
Node* const receiver = args.GetReceiver();
Node* const depth = args.GetOptionalArgumentValue(0);
// 1. Let O be ? ToObject(this value).
Node* const o = ToObject(context, receiver);
// 2. Let sourceLen be ? ToLength(? Get(O, "length")).
Node* const source_length =
ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
// 3. Let depthNum be 1.
VARIABLE(var_depth_num, MachineRepresentation::kTagged, SmiConstant(1));
// 4. If depth is not undefined, then
Label done(this);
GotoIf(IsUndefined(depth), &done);
{
// a. Set depthNum to ? ToInteger(depth).
var_depth_num.Bind(ToInteger_Inline(context, depth));
Goto(&done);
}
BIND(&done);
// 5. Let A be ? ArraySpeciesCreate(O, 0).
Node* const constructor =
CallRuntime(Runtime::kArraySpeciesConstructor, context, o);
Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context,
constructor, SmiConstant(0));
// 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
CallBuiltin(Builtins::kFlattenIntoArray, context, a, o, source_length,
SmiConstant(0), var_depth_num.value());
// 7. Return A.
args.PopAndReturn(a);
}
// https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap
TF_BUILTIN(ArrayPrototypeFlatMap, CodeStubAssembler) {
Node* const argc =
ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
CodeStubArguments args(this, argc);
Node* const context = Parameter(BuiltinDescriptor::kContext);
Node* const receiver = args.GetReceiver();
Node* const mapper_function = args.GetOptionalArgumentValue(0);
// 1. Let O be ? ToObject(this value).
Node* const o = ToObject(context, receiver);
// 2. Let sourceLen be ? ToLength(? Get(O, "length")).
Node* const source_length =
ToLength_Inline(context, GetProperty(context, o, LengthStringConstant()));
// 3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
Label if_not_callable(this, Label::kDeferred);
GotoIf(TaggedIsSmi(mapper_function), &if_not_callable);
GotoIfNot(IsCallable(mapper_function), &if_not_callable);
// 4. If thisArg is present, let T be thisArg; else let T be undefined.
Node* const t = args.GetOptionalArgumentValue(1);
// 5. Let A be ? ArraySpeciesCreate(O, 0).
Node* const constructor =
CallRuntime(Runtime::kArraySpeciesConstructor, context, o);
Node* const a = ConstructJS(CodeFactory::Construct(isolate()), context,
constructor, SmiConstant(0));
// 6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T).
CallBuiltin(Builtins::kFlatMapIntoArray, context, a, o, source_length,
SmiConstant(0), SmiConstant(1), mapper_function, t);
// 7. Return A.
args.PopAndReturn(a);
BIND(&if_not_callable);
{ ThrowTypeError(context, MessageTemplate::kMapperFunctionNonCallable); }
}
} // namespace internal
} // namespace v8

View File

@ -367,6 +367,14 @@ namespace internal {
TFJ(ArrayPrototypeValues, 0) \
/* ES6 #sec-%arrayiteratorprototype%.next */ \
TFJ(ArrayIteratorPrototypeNext, 0) \
/* https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray */ \
TFS(FlattenIntoArray, kTarget, kSource, kSourceLength, kStart, kDepth) \
TFS(FlatMapIntoArray, kTarget, kSource, kSourceLength, kStart, kDepth, \
kMapperFunction, kThisArg) \
/* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatten */ \
TFJ(ArrayPrototypeFlatten, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */ \
TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
\
/* ArrayBuffer */ \
/* ES #sec-arraybuffer-constructor */ \

View File

@ -6174,7 +6174,8 @@ TNode<Number> CodeStubAssembler::ToLength_Inline(SloppyTNode<Context> context,
}
TNode<Number> CodeStubAssembler::ToInteger_Inline(
TNode<Context> context, TNode<Object> input, ToIntegerTruncationMode mode) {
SloppyTNode<Context> context, SloppyTNode<Object> input,
ToIntegerTruncationMode mode) {
Builtins::Name builtin = (mode == kNoTruncation)
? Builtins::kToInteger
: Builtins::kToInteger_TruncateMinusZero;
@ -10022,7 +10023,7 @@ void CodeStubAssembler::BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true,
}
TNode<Oddball> CodeStubAssembler::HasProperty(SloppyTNode<HeapObject> object,
SloppyTNode<Name> key,
SloppyTNode<Object> key,
SloppyTNode<Context> context,
HasPropertyLookupMode mode) {
Label call_runtime(this, Label::kDeferred), return_true(this),

View File

@ -1315,7 +1315,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
SloppyTNode<Object> input);
// ES6 7.1.4 ToInteger ( argument )
TNode<Number> ToInteger_Inline(TNode<Context> context, TNode<Object> input,
TNode<Number> ToInteger_Inline(SloppyTNode<Context> context,
SloppyTNode<Object> input,
ToIntegerTruncationMode mode = kNoTruncation);
TNode<Number> ToInteger(SloppyTNode<Context> context,
SloppyTNode<Object> input,
@ -1910,7 +1911,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
enum HasPropertyLookupMode { kHasProperty, kForInHasProperty };
TNode<Oddball> HasProperty(SloppyTNode<HeapObject> object,
SloppyTNode<Name> key,
SloppyTNode<Object> key,
SloppyTNode<Context> context,
HasPropertyLookupMode mode);

View File

@ -212,7 +212,8 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields)
V(harmony_array_prototype_values, "harmony Array.prototype.values") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_class_fields, "harmony fields in class literals") \
V(harmony_static_fields, "harmony static fields in class literals")
V(harmony_static_fields, "harmony static fields in class literals") \
V(harmony_array_flatten, "harmony Array.prototype.flat{ten,Map}")
// Features that are complete (but still behind --harmony/es-staging flag).
#define HARMONY_STAGED(V) \

View File

@ -343,6 +343,7 @@ class ErrorUtils : public AllStatic {
T(IteratorSymbolNonCallable, "Found non-callable @@iterator") \
T(IteratorValueNotAnObject, "Iterator value % is not an entry object") \
T(LanguageID, "Language ID should be string or object.") \
T(MapperFunctionNonCallable, "flatMap mapper function is not callable") \
T(MethodCalledOnWrongObject, \
"Method % called on a non-object or on a wrong type of object.") \
T(MethodInvokedOnNullOrUndefined, \
@ -643,6 +644,9 @@ class ErrorUtils : public AllStatic {
T(NoCatchOrFinally, "Missing catch or finally after try") \
T(NotIsvar, "builtin %%IS_VAR: not a variable") \
T(ParamAfterRest, "Rest parameter must be last formal parameter") \
T(FlattenPastSafeLength, \
"Flattening % elements on an array-like of length % " \
"is disallowed, as the total surpasses 2**53-1") \
T(PushPastSafeLength, \
"Pushing % elements on an array-like of length % " \
"is disallowed, as the total surpasses 2**53-1") \

View File

@ -367,7 +367,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(18),
B(LdaConstant), U8(15),
B(Star), R(19),

View File

@ -124,7 +124,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(19),
B(LdaConstant), U8(12),
B(Star), R(20),
@ -378,7 +378,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(19),
B(LdaConstant), U8(12),
B(Star), R(20),
@ -654,7 +654,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(19),
B(LdaConstant), U8(12),
B(Star), R(20),
@ -886,7 +886,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(17),
B(LdaConstant), U8(10),
B(Star), R(18),

View File

@ -86,7 +86,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
@ -220,7 +220,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(13),
B(LdaConstant), U8(8),
B(Star), R(14),
@ -366,7 +366,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
@ -502,7 +502,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(11),
B(LdaConstant), U8(10),
B(Star), R(12),

View File

@ -90,7 +90,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(14),
B(LdaConstant), U8(7),
B(Star), R(15),
@ -261,7 +261,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(14),
B(LdaConstant), U8(11),
B(Star), R(15),
@ -408,7 +408,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(12),
B(LdaConstant), U8(9),
B(Star), R(13),
@ -503,7 +503,7 @@ bytecodes: [
B(JumpIfUndefined), U8(6),
B(Ldar), R(6),
B(JumpIfNotNull), U8(16),
B(LdaSmi), I8(76),
B(LdaSmi), I8(77),
B(Star), R(18),
B(LdaConstant), U8(4),
B(Star), R(19),
@ -559,7 +559,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(17),
B(LdaConstant), U8(9),
B(Star), R(18),
@ -713,7 +713,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(16),
B(LdaConstant), U8(10),
B(Star), R(17),
@ -882,7 +882,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(15),
B(LdaConstant), U8(13),
B(Star), R(16),
@ -1037,7 +1037,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(21),
B(LdaConstant), U8(7),
B(Star), R(22),
@ -1253,7 +1253,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(20),
B(LdaConstant), U8(8),
B(Star), R(21),

View File

@ -204,7 +204,7 @@ bytecodes: [
B(TestTypeOf), U8(6),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), I16(146),
B(Wide), B(LdaSmi), I16(147),
B(Star), R(14),
B(LdaConstant), U8(14),
B(Star), R(15),

View File

@ -231,7 +231,7 @@ bytecodes: [
B(JumpIfUndefined), U8(6),
B(Ldar), R(3),
B(JumpIfNotNull), U8(16),
B(LdaSmi), I8(76),
B(LdaSmi), I8(77),
B(Star), R(4),
B(LdaConstant), U8(1),
B(Star), R(5),

View File

@ -0,0 +1,120 @@
// Copyright 2018 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.
// Flags: --harmony-array-flatten --allow-natives-syntax
assertEquals(Array.prototype.flatMap.length, 1);
assertEquals(Array.prototype.flatMap.name, 'flatMap');
assertEquals(
[1, 2, 3, 4].flatMap((element) => [element, element ** 2]),
[1, 1, 2, 4, 3, 9, 4, 16]
);
assertEquals(
[1, 2, 3, 4].flatMap((element) => [[element, element ** 2]]),
[[1, 1], [2, 4], [3, 9], [4, 16]]
);
const elements = new Set([
-Infinity,
-1,
-0,
+0,
+1,
Infinity,
null,
undefined,
true,
false,
'',
'foo',
/./,
[],
{},
Object.create(null),
new Proxy({}, {}),
Symbol(),
x => x ** 2,
String
]);
for (const value of elements) {
assertEquals(
[value].flatMap((element) => [element, element]),
[value, value]
);
}
const array = [42];
assertEquals(
[array].flatMap((element) => [element, element]),
[array, array]
);
const nonCallables = new Set([
-Infinity,
-1,
-0,
+0,
+1,
Infinity,
null,
undefined,
true,
false,
'',
'foo',
/./,
[],
{},
Object.create(null),
new Proxy({}, {}),
Symbol(),
]);
for (const nonCallable of nonCallables) {
assertThrows(() => {
[].flatMap(nonCallable);
}, TypeError);
}
const object = {
foo: 42,
get length() {
object.foo = 0;
}
};
const result = [object].flatMap((element) => [element, element]);
%HeapObjectVerify(result);
assertEquals(result, [object, object]);
assertEquals(result[0].foo, 42);
assertThrows(() => {
Array.prototype.flatMap.call(null, (element) => element);
}, TypeError);
assertThrows(() => {
Array.prototype.flatMap.call(undefined, (element) => element);
}, TypeError);
assertEquals(
Array.prototype.flatMap.call(
{
length: 1,
0: 'a',
1: 'b',
},
(element) => element
),
['a']
);
assertEquals(
Array.prototype.flatMap.call(
{
length: 2,
0: 'a',
1: 'b',
},
(element) => element
),
['a', 'b']
);

View File

@ -0,0 +1,38 @@
// Copyright 2018 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.
// Flags: --harmony-array-flatten
assertEquals(Array.prototype.flatten.length, 0);
assertEquals(Array.prototype.flatten.name, 'flatten');
const input = [1, [2], [[3]]];
assertEquals(input.flatten(), [1, 2, [3]]);
assertEquals(input.flatten(1), [1, 2, [3]]);
assertEquals(input.flatten(true), [1, 2, [3]]);
assertEquals(input.flatten(undefined), [1, 2, [3]]);
assertEquals(input.flatten(-Infinity), [1, [2], [[3]]]);
assertEquals(input.flatten(-1), [1, [2], [[3]]]);
assertEquals(input.flatten(-0), [1, [2], [[3]]]);
assertEquals(input.flatten(0), [1, [2], [[3]]]);
assertEquals(input.flatten(false), [1, [2], [[3]]]);
assertEquals(input.flatten(null), [1, [2], [[3]]]);
assertEquals(input.flatten(''), [1, [2], [[3]]]);
assertEquals(input.flatten('foo'), [1, [2], [[3]]]);
assertEquals(input.flatten(/./), [1, [2], [[3]]]);
assertEquals(input.flatten([]), [1, [2], [[3]]]);
assertEquals(input.flatten({}), [1, [2], [[3]]]);
assertEquals(
input.flatten(new Proxy({}, {})), [1, [2], [[3]]]);
assertEquals(input.flatten((x) => x), [1, [2], [[3]]]);
assertEquals(
input.flatten(String), [1, [2], [[3]]]);
assertEquals(input.flatten(2), [1, 2, 3]);
assertEquals(input.flatten(Infinity), [1, 2, 3]);
assertThrows(() => { input.flatten(Symbol()); }, TypeError);
assertThrows(() => { input.flatten(Object.create(null)); }, TypeError);

View File

@ -50,11 +50,12 @@ FEATURE_FLAGS = {
'class-fields-public': '--harmony-public-fields',
'optional-catch-binding': '--harmony-optional-catch-binding',
'class-fields-private': '--harmony-private-fields',
'Array.prototype.flatten': '--harmony-array-flatten',
'Array.prototype.flatMap': '--harmony-array-flatten',
'numeric-separator-literal': '--harmony-numeric-separator',
}
SKIPPED_FEATURES = set(['Array.prototype.flatten',
'Array.prototype.flatMap'])
SKIPPED_FEATURES = set([])
DATA = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
ARCHIVE = DATA + ".tar"

View File

@ -60,7 +60,9 @@ LINT_RULES = """
-build/header_guard
-build/include_what_you_use
-readability/fn_size
-readability/multiline_comment
-runtime/references
-whitespace/comments
""".split()
LINT_OUTPUT_PATTERN = re.compile(r'^.+[:(]\d+[:)]|^Done processing')