From 697d39abff90510523f297bb8577d5c64322229f Mon Sep 17 00:00:00 2001 From: Mathias Bynens Date: Thu, 15 Mar 2018 17:51:01 +0100 Subject: [PATCH] =?UTF-8?q?[esnext]=20Implement=20Array.prototype.{flatten?= =?UTF-8?q?,flatMap}=20=F0=9F=A5=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Sathya Gunasekaran Reviewed-by: Benedikt Meurer Cr-Commit-Position: refs/heads/master@{#51967} --- src/bootstrapper.cc | 11 + src/builtins/builtins-array-gen.cc | 255 ++++++++++++++++++ src/builtins/builtins-definitions.h | 8 + src/code-stub-assembler.cc | 5 +- src/code-stub-assembler.h | 5 +- src/flag-definitions.h | 3 +- src/messages.h | 4 + .../AsyncGenerators.golden | 2 +- .../bytecode_expectations/ForAwaitOf.golden | 8 +- .../bytecode_expectations/ForOf.golden | 8 +- .../bytecode_expectations/ForOfLoop.golden | 18 +- .../bytecode_expectations/Generators.golden | 2 +- .../StandardForLoop.golden | 2 +- test/mjsunit/harmony/array-flatMap.js | 120 +++++++++ test/mjsunit/harmony/array-flatten.js | 38 +++ test/test262/testcfg.py | 5 +- tools/presubmit.py | 2 + 17 files changed, 469 insertions(+), 27 deletions(-) create mode 100644 test/mjsunit/harmony/array-flatMap.js create mode 100644 test/mjsunit/harmony/array-flatten.js diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 478d1ddcc3..a79496e47c 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -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 array_constructor(native_context()->array_function()); + Handle 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; diff --git a/src/builtins/builtins-array-gen.cc b/src/builtins/builtins-array-gen.cc index 68c9de86dd..5cb369c210 100644 --- a/src/builtins/builtins-array-gen.cc +++ b/src/builtins/builtins-array-gen.cc @@ -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 diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 9c761539d4..0f48c74ec4 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -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 */ \ diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc index 6716c79af5..55a214e799 100644 --- a/src/code-stub-assembler.cc +++ b/src/code-stub-assembler.cc @@ -6174,7 +6174,8 @@ TNode CodeStubAssembler::ToLength_Inline(SloppyTNode context, } TNode CodeStubAssembler::ToInteger_Inline( - TNode context, TNode input, ToIntegerTruncationMode mode) { + SloppyTNode context, SloppyTNode 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 CodeStubAssembler::HasProperty(SloppyTNode object, - SloppyTNode key, + SloppyTNode key, SloppyTNode context, HasPropertyLookupMode mode) { Label call_runtime(this, Label::kDeferred), return_true(this), diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h index 1bcc89503d..af43e9620e 100644 --- a/src/code-stub-assembler.h +++ b/src/code-stub-assembler.h @@ -1315,7 +1315,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { SloppyTNode input); // ES6 7.1.4 ToInteger ( argument ) - TNode ToInteger_Inline(TNode context, TNode input, + TNode ToInteger_Inline(SloppyTNode context, + SloppyTNode input, ToIntegerTruncationMode mode = kNoTruncation); TNode ToInteger(SloppyTNode context, SloppyTNode input, @@ -1910,7 +1911,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { enum HasPropertyLookupMode { kHasProperty, kForInHasProperty }; TNode HasProperty(SloppyTNode object, - SloppyTNode key, + SloppyTNode key, SloppyTNode context, HasPropertyLookupMode mode); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 6fb27db0d8..3993f793c5 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -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) \ diff --git a/src/messages.h b/src/messages.h index f8336d5cc1..d23e6cca17 100644 --- a/src/messages.h +++ b/src/messages.h @@ -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") \ diff --git a/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden b/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden index 17d3498738..df6b372358 100644 --- a/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden +++ b/test/cctest/interpreter/bytecode_expectations/AsyncGenerators.golden @@ -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), diff --git a/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden b/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden index 78faba0268..95400dacf8 100644 --- a/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden +++ b/test/cctest/interpreter/bytecode_expectations/ForAwaitOf.golden @@ -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), diff --git a/test/cctest/interpreter/bytecode_expectations/ForOf.golden b/test/cctest/interpreter/bytecode_expectations/ForOf.golden index d623382820..00f90714d3 100644 --- a/test/cctest/interpreter/bytecode_expectations/ForOf.golden +++ b/test/cctest/interpreter/bytecode_expectations/ForOf.golden @@ -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), diff --git a/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden b/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden index 31cf55dad5..1a0155da79 100644 --- a/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden +++ b/test/cctest/interpreter/bytecode_expectations/ForOfLoop.golden @@ -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), diff --git a/test/cctest/interpreter/bytecode_expectations/Generators.golden b/test/cctest/interpreter/bytecode_expectations/Generators.golden index 7401d4d1c0..2dc77a7986 100644 --- a/test/cctest/interpreter/bytecode_expectations/Generators.golden +++ b/test/cctest/interpreter/bytecode_expectations/Generators.golden @@ -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), diff --git a/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden b/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden index 4eeed57a9d..80609db7b8 100644 --- a/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden +++ b/test/cctest/interpreter/bytecode_expectations/StandardForLoop.golden @@ -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), diff --git a/test/mjsunit/harmony/array-flatMap.js b/test/mjsunit/harmony/array-flatMap.js new file mode 100644 index 0000000000..d129c32e03 --- /dev/null +++ b/test/mjsunit/harmony/array-flatMap.js @@ -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'] +); diff --git a/test/mjsunit/harmony/array-flatten.js b/test/mjsunit/harmony/array-flatten.js new file mode 100644 index 0000000000..7ca6218ed8 --- /dev/null +++ b/test/mjsunit/harmony/array-flatten.js @@ -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); diff --git a/test/test262/testcfg.py b/test/test262/testcfg.py index 1b16c318bb..31262f5478 100644 --- a/test/test262/testcfg.py +++ b/test/test262/testcfg.py @@ -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" diff --git a/tools/presubmit.py b/tools/presubmit.py index 917b6e2383..29469be758 100755 --- a/tools/presubmit.py +++ b/tools/presubmit.py @@ -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')