[es6] implement spread calls
authorcaitpotter88 <caitpotter88@gmail.com>
Thu, 9 Apr 2015 19:37:14 +0000 (12:37 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 9 Apr 2015 19:37:19 +0000 (19:37 +0000)
BUG=v8:3018
R=
LOG=N

Review URL: https://codereview.chromium.org/938443002

Cr-Commit-Position: refs/heads/master@{#27714}

32 files changed:
BUILD.gn
src/arm/full-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/ast-numbering.cc
src/ast-value-factory.h
src/ast.h
src/bootstrapper.cc
src/compiler/ast-graph-builder.cc
src/compiler/ast-loop-assignment-analyzer.cc
src/flag-definitions.h
src/full-codegen.cc
src/full-codegen.h
src/harmony-spread.js [new file with mode: 0644]
src/hydrogen.cc
src/ia32/full-codegen-ia32.cc
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/prettyprinter.cc
src/runtime/runtime-classes.cc
src/runtime/runtime.h
src/typing.cc
src/x64/full-codegen-x64.cc
test/cctest/test-parsing.cc
test/js-perf-test/JSTests.json
test/js-perf-test/SpreadCalls/run.js [new file with mode: 0644]
test/js-perf-test/SpreadCalls/spreadcalls.js [new file with mode: 0644]
test/mjsunit/harmony/spread-call-new-class.js [new file with mode: 0644]
test/mjsunit/harmony/spread-call-new.js [new file with mode: 0644]
test/mjsunit/harmony/spread-call.js [new file with mode: 0644]
tools/gyp/v8.gyp

index 98987df839b12ce3be41f86433b8fb13902bdc73..4d0e3385557c3c8f8c74025cb08c6013f029ae88 100644 (file)
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -270,7 +270,8 @@ action("js2c_experimental") {
     "src/harmony-typedarray.js",
     "src/harmony-tostring.js",
     "src/harmony-regexp.js",
-    "src/harmony-reflect.js"
+    "src/harmony-reflect.js",
+    "src/harmony-spread.js"
   ]
 
   outputs = [
index 6ee8eb1cd68540b417adbabed51221313e3f1f93..40ada3902a4bb370171c5fc5de5c9c199852bc69 100644 (file)
@@ -3118,6 +3118,22 @@ void FullCodeGenerator::EmitLoadSuperConstructor() {
 }
 
 
+void FullCodeGenerator::EmitInitializeThisAfterSuper(
+    SuperReference* super_ref) {
+  Variable* this_var = super_ref->this_var()->var();
+  GetVar(r1, this_var);
+  __ CompareRoot(r1, Heap::kTheHoleValueRootIndex);
+  Label uninitialized_this;
+  __ b(eq, &uninitialized_this);
+  __ mov(r0, Operand(this_var->name()));
+  __ Push(r0);
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
+}
+
+
 void FullCodeGenerator::VisitCall(Call* expr) {
 #ifdef DEBUG
   // We want to verify that RecordJSReturnSite gets called on all paths
@@ -3341,18 +3357,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  SuperReference* super_ref = expr->expression()->AsSuperReference();
-  Variable* this_var = super_ref->this_var()->var();
-  GetVar(r1, this_var);
-  __ CompareRoot(r1, Heap::kTheHoleValueRootIndex);
-  Label uninitialized_this;
-  __ b(eq, &uninitialized_this);
-  __ mov(r0, Operand(this_var->name()));
-  __ Push(r0);
-  __ CallRuntime(Runtime::kThrowReferenceError, 1);
-  __ bind(&uninitialized_this);
-
-  EmitVariableAssignment(this_var, Token::INIT_CONST);
+  EmitInitializeThisAfterSuper(expr->expression()->AsSuperReference());
   context()->Plug(r0);
 }
 
@@ -4608,27 +4613,81 @@ void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
 }
 
 
+void FullCodeGenerator::EmitCallSuperWithSpread(CallRuntime* expr) {
+  // Assert: expr === CallRuntime("ReflectConstruct")
+  CallRuntime* call = expr->arguments()->at(0)->AsCallRuntime();
+  ZoneList<Expression*>* args = call->arguments();
+  DCHECK_EQ(3, args->length());
+
+  SuperReference* super_reference = args->at(0)->AsSuperReference();
+
+  // Load ReflectConstruct function
+  EmitLoadJSRuntimeFunction(call);
+
+  // Push the target function under the receiver.
+  __ ldr(ip, MemOperand(sp, 0));
+  __ push(ip);
+  __ str(r0, MemOperand(sp, kPointerSize));
+
+  // Push super
+  EmitLoadSuperConstructor();
+  __ Push(result_register());
+
+  // Push arguments array
+  VisitForStackValue(args->at(1));
+
+  // Push NewTarget
+  DCHECK(args->at(2)->IsVariableProxy());
+  VisitForStackValue(args->at(2));
+
+  EmitCallJSRuntimeFunction(call);
+
+  // Restore context register.
+  __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+  context()->DropAndPlug(1, r0);
+
+  EmitInitializeThisAfterSuper(super_reference);
+}
+
+
+void FullCodeGenerator::EmitLoadJSRuntimeFunction(CallRuntime* expr) {
+  // Push the builtins object as the receiver.
+  Register receiver = LoadDescriptor::ReceiverRegister();
+  __ ldr(receiver, GlobalObjectOperand());
+  __ ldr(receiver, FieldMemOperand(receiver, GlobalObject::kBuiltinsOffset));
+  __ push(receiver);
+
+  // Load the function from the receiver.
+  __ mov(LoadDescriptor::NameRegister(), Operand(expr->name()));
+  if (FLAG_vector_ics) {
+    __ mov(VectorLoadICDescriptor::SlotRegister(),
+           Operand(SmiFromSlot(expr->CallRuntimeFeedbackSlot())));
+    CallLoadIC(NOT_CONTEXTUAL);
+  } else {
+    CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
+  }
+}
+
+
+void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+
+  // Record source position of the IC call.
+  SetSourcePosition(expr->position());
+  CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
+  __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
+  __ CallStub(&stub);
+}
+
+
 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
 
   if (expr->is_jsruntime()) {
     Comment cmnt(masm_, "[ CallRuntime");
-    // Push the builtins object as the receiver.
-    Register receiver = LoadDescriptor::ReceiverRegister();
-    __ ldr(receiver, GlobalObjectOperand());
-    __ ldr(receiver, FieldMemOperand(receiver, GlobalObject::kBuiltinsOffset));
-    __ push(receiver);
-
-    // Load the function from the receiver.
-    __ mov(LoadDescriptor::NameRegister(), Operand(expr->name()));
-    if (FLAG_vector_ics) {
-      __ mov(VectorLoadICDescriptor::SlotRegister(),
-             Operand(SmiFromSlot(expr->CallRuntimeFeedbackSlot())));
-      CallLoadIC(NOT_CONTEXTUAL);
-    } else {
-      CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
-    }
+    EmitLoadJSRuntimeFunction(expr);
 
     // Push the target function under the receiver.
     __ ldr(ip, MemOperand(sp, 0));
@@ -4640,11 +4699,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
       VisitForStackValue(args->at(i));
     }
 
-    // Record source position of the IC call.
-    SetSourcePosition(expr->position());
-    CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
-    __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
-    __ CallStub(&stub);
+    EmitCallJSRuntimeFunction(expr);
 
     // Restore context register.
     __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
index 9feac938b5320c7d3e14b52e47d0f7aae9c5a9cd..987164a7d8c338f126777c3cfd09e8b749093a64 100644 (file)
@@ -2805,6 +2805,21 @@ void FullCodeGenerator::EmitLoadSuperConstructor() {
 }
 
 
+void FullCodeGenerator::EmitInitializeThisAfterSuper(
+    SuperReference* super_ref) {
+  Variable* this_var = super_ref->this_var()->var();
+  GetVar(x1, this_var);
+  Label uninitialized_this;
+  __ JumpIfRoot(x1, Heap::kTheHoleValueRootIndex, &uninitialized_this);
+  __ Mov(x0, Operand(this_var->name()));
+  __ Push(x0);
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
+}
+
+
 void FullCodeGenerator::VisitCall(Call* expr) {
 #ifdef DEBUG
   // We want to verify that RecordJSReturnSite gets called on all paths
@@ -3029,17 +3044,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  SuperReference* super_ref = expr->expression()->AsSuperReference();
-  Variable* this_var = super_ref->this_var()->var();
-  GetVar(x1, this_var);
-  Label uninitialized_this;
-  __ JumpIfRoot(x1, Heap::kTheHoleValueRootIndex, &uninitialized_this);
-  __ Mov(x0, Operand(this_var->name()));
-  __ Push(x0);
-  __ CallRuntime(Runtime::kThrowReferenceError, 1);
-  __ bind(&uninitialized_this);
-
-  EmitVariableAssignment(this_var, Token::INIT_CONST);
+  EmitInitializeThisAfterSuper(expr->expression()->AsSuperReference());
   context()->Plug(x0);
 }
 
@@ -4294,28 +4299,81 @@ void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
 }
 
 
+void FullCodeGenerator::EmitCallSuperWithSpread(CallRuntime* expr) {
+  // Assert: expr === CallRuntime("ReflectConstruct")
+  CallRuntime* call = expr->arguments()->at(0)->AsCallRuntime();
+  ZoneList<Expression*>* args = call->arguments();
+  DCHECK_EQ(3, args->length());
+
+  SuperReference* super_reference = args->at(0)->AsSuperReference();
+
+  // Load ReflectConstruct function
+  EmitLoadJSRuntimeFunction(call);
+
+  // Push the target function under the receiver.
+  __ Pop(x10);
+  __ Push(x0, x10);
+
+  // Push super
+  EmitLoadSuperConstructor();
+  __ Push(result_register());
+
+  // Push arguments array
+  VisitForStackValue(args->at(1));
+
+  // Push NewTarget
+  DCHECK(args->at(2)->IsVariableProxy());
+  VisitForStackValue(args->at(2));
+
+  EmitCallJSRuntimeFunction(call);
+
+  // Restore context register.
+  __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+  context()->DropAndPlug(1, x0);
+
+  EmitInitializeThisAfterSuper(super_reference);
+}
+
+
+void FullCodeGenerator::EmitLoadJSRuntimeFunction(CallRuntime* expr) {
+  // Push the builtins object as the receiver.
+  __ Ldr(x10, GlobalObjectMemOperand());
+  __ Ldr(LoadDescriptor::ReceiverRegister(),
+         FieldMemOperand(x10, GlobalObject::kBuiltinsOffset));
+  __ Push(LoadDescriptor::ReceiverRegister());
+
+  // Load the function from the receiver.
+  Handle<String> name = expr->name();
+  __ Mov(LoadDescriptor::NameRegister(), Operand(name));
+  if (FLAG_vector_ics) {
+    __ Mov(VectorLoadICDescriptor::SlotRegister(),
+           SmiFromSlot(expr->CallRuntimeFeedbackSlot()));
+    CallLoadIC(NOT_CONTEXTUAL);
+  } else {
+    CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
+  }
+}
+
+
+void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+
+  // Record source position of the IC call.
+  SetSourcePosition(expr->position());
+  CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
+  __ Peek(x1, (arg_count + 1) * kPointerSize);
+  __ CallStub(&stub);
+}
+
+
 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
 
   if (expr->is_jsruntime()) {
     Comment cmnt(masm_, "[ CallRunTime");
-    // Push the builtins object as the receiver.
-    __ Ldr(x10, GlobalObjectMemOperand());
-    __ Ldr(LoadDescriptor::ReceiverRegister(),
-           FieldMemOperand(x10, GlobalObject::kBuiltinsOffset));
-    __ Push(LoadDescriptor::ReceiverRegister());
-
-    // Load the function from the receiver.
-    Handle<String> name = expr->name();
-    __ Mov(LoadDescriptor::NameRegister(), Operand(name));
-    if (FLAG_vector_ics) {
-      __ Mov(VectorLoadICDescriptor::SlotRegister(),
-             SmiFromSlot(expr->CallRuntimeFeedbackSlot()));
-      CallLoadIC(NOT_CONTEXTUAL);
-    } else {
-      CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
-    }
+    EmitLoadJSRuntimeFunction(expr);
 
     // Push the target function under the receiver.
     __ Pop(x10);
@@ -4325,11 +4383,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
       VisitForStackValue(args->at(i));
     }
 
-    // Record source position of the IC call.
-    SetSourcePosition(expr->position());
-    CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
-    __ Peek(x1, (arg_count + 1) * kPointerSize);
-    __ CallStub(&stub);
+    EmitCallJSRuntimeFunction(expr);
 
     // Restore context register.
     __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
index f63919c05802f6e43b93587b7475f8734eb30feb..1d5420990fc8d150f1999d9cc8607653afdc7037 100644 (file)
@@ -362,6 +362,9 @@ void AstNumberingVisitor::VisitCompareOperation(CompareOperation* node) {
 }
 
 
+void AstNumberingVisitor::VisitSpread(Spread* node) { UNREACHABLE(); }
+
+
 void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) {
   IncrementNodeCount();
   DisableSelfOptimization();
index b34a55b2a69a55d91a99b6c3cd44b38608cda232..b5b5bd6983fc993c29171265269930bb0e28fbd6 100644 (file)
@@ -259,6 +259,10 @@ class AstValue : public ZoneObject {
   F(next, "next")                                                          \
   F(proto, "__proto__")                                                    \
   F(prototype, "prototype")                                                \
+  F(reflect_apply, "ReflectApply")                                         \
+  F(reflect_construct, "ReflectConstruct")                                 \
+  F(spread_arguments, "SpreadArguments")                                   \
+  F(spread_iterable, "SpreadIterable")                                     \
   F(this, "this")                                                          \
   F(throw_iterator_result_not_an_object, "ThrowIteratorResultNotAnObject") \
   F(to_string, "ToString")                                                 \
index 27e8f09a90dae879b1cf8557ab963683e158f06d..7bfa02178faabb6e09af3c3f8d8f303393da88d1 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -92,6 +92,7 @@ namespace internal {
   V(CountOperation)             \
   V(BinaryOperation)            \
   V(CompareOperation)           \
+  V(Spread)                     \
   V(ThisFunction)               \
   V(SuperReference)             \
   V(CaseClause)
@@ -2263,6 +2264,26 @@ class CompareOperation FINAL : public Expression {
 };
 
 
+class Spread FINAL : public Expression {
+ public:
+  DECLARE_NODE_TYPE(Spread)
+
+  Expression* expression() const { return expression_; }
+
+  static int num_ids() { return parent_num_ids(); }
+
+ protected:
+  Spread(Zone* zone, Expression* expression, int pos)
+      : Expression(zone, pos), expression_(expression) {}
+  static int parent_num_ids() { return Expression::num_ids(); }
+
+ private:
+  int local_id(int n) const { return base_id() + parent_num_ids() + n; }
+
+  Expression* expression_;
+};
+
+
 class Conditional FINAL : public Expression {
  public:
   DECLARE_NODE_TYPE(Conditional)
@@ -3475,6 +3496,10 @@ class AstNodeFactory FINAL BASE_EMBEDDED {
     return new (zone_) CompareOperation(zone_, op, left, right, pos);
   }
 
+  Spread* NewSpread(Expression* expression, int pos) {
+    return new (zone_) Spread(zone_, expression, pos);
+  }
+
   Conditional* NewConditional(Expression* condition,
                               Expression* then_expression,
                               Expression* else_expression,
index 2d9a13f19a6bc25a74d301c9e8ce07a58a9fc79e..518e5df2d062b3a675add30b6519c8ad347d7359 100644 (file)
@@ -1667,6 +1667,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_unicode_regexps)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_computed_property_names)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_rest_parameters)
 EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_reflect)
+EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_spreadcalls)
 
 
 void Genesis::InstallNativeFunctions_harmony_proxies() {
@@ -1695,6 +1696,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_sloppy)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_computed_property_names)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_rest_parameters)
+EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_spreadcalls)
 
 void Genesis::InitializeGlobal_harmony_regexps() {
   Handle<JSObject> builtins(native_context()->builtins());
@@ -1719,10 +1721,9 @@ void Genesis::InitializeGlobal_harmony_unicode_regexps() {
 
 
 void Genesis::InitializeGlobal_harmony_reflect() {
-  if (!FLAG_harmony_reflect) return;
   Handle<JSObject> builtins(native_context()->builtins());
   // Install references to functions of the Reflect object
-  {
+  if (FLAG_harmony_reflect || FLAG_harmony_spreadcalls) {
     Handle<JSFunction> apply =
         InstallFunction(builtins, "ReflectApply", JS_OBJECT_TYPE,
                         JSObject::kHeaderSize, MaybeHandle<JSObject>(),
@@ -1750,6 +1751,7 @@ void Genesis::InitializeGlobal_harmony_reflect() {
     construct->shared()->set_length(2);
   }
 
+  if (!FLAG_harmony_reflect) return;
   Handle<JSGlobalObject> global(JSGlobalObject::cast(
       native_context()->global_object()));
   Handle<String> reflect_string =
@@ -2329,6 +2331,8 @@ bool Genesis::InstallExperimentalNatives() {
   static const char* harmony_rest_parameters_natives[] = {NULL};
   static const char* harmony_reflect_natives[] = {"native harmony-reflect.js",
                                                   NULL};
+  static const char* harmony_spreadcalls_natives[] = {
+      "native harmony-spread.js", NULL};
 
   for (int i = ExperimentalNatives::GetDebuggerCount();
        i < ExperimentalNatives::GetBuiltinsCount(); i++) {
index 36f684070cbb9dab81bd91adbbe6283e55669584..2d320421f24a56da2a0851bacc8a1e25130bdad1 100644 (file)
@@ -2442,6 +2442,9 @@ void AstGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
 }
 
 
+void AstGraphBuilder::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
 void AstGraphBuilder::VisitThisFunction(ThisFunction* expr) {
   Node* value = GetFunctionClosure();
   ast_context()->ProduceValue(value);
index 4028b9412ba8e3647e02a8326ae531847057849e..a20f13f529d2f67f89c6d374813c23eb54c904f9 100644 (file)
@@ -195,6 +195,9 @@ void ALAA::VisitCompareOperation(CompareOperation* e) {
 }
 
 
+void ALAA::VisitSpread(Spread* e) { Visit(e->expression()); }
+
+
 void ALAA::VisitCaseClause(CaseClause* cc) {
   if (!cc->is_default()) Visit(cc->label());
   VisitStatements(cc->statements());
index da95b1e52425dbf4e36b3217afb7b592362ebbb0..bc73a4ea6fd4d997342cbc5fa9a9905e937c70dc 100644 (file)
@@ -192,7 +192,8 @@ DEFINE_IMPLICATION(es_staging, harmony)
   V(harmony_sloppy, "harmony features in sloppy mode")          \
   V(harmony_unicode_regexps, "harmony unicode regexps")         \
   V(harmony_rest_parameters, "harmony rest parameters")         \
-  V(harmony_reflect, "harmony Reflect API")
+  V(harmony_reflect, "harmony Reflect API")                     \
+  V(harmony_spreadcalls, "harmony spread-calls")
 
 // Features that are complete (but still behind --harmony/es-staging flag).
 #define HARMONY_STAGED(V)                                               \
index 327230e75d457eca7be084e9407941476bf1ad97..d70ab80d2cc7999ef5cb99d73078404dcfca28a7 100644 (file)
@@ -288,6 +288,9 @@ void BreakableStatementChecker::VisitCompareOperation(CompareOperation* expr) {
 }
 
 
+void BreakableStatementChecker::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
 void BreakableStatementChecker::VisitThisFunction(ThisFunction* expr) {
 }
 
@@ -1666,6 +1669,9 @@ void FullCodeGenerator::ExitTryBlock(int index) {
 }
 
 
+void FullCodeGenerator::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
 FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
     int* stack_depth, int* context_length) {
   // The macros used here must preserve the result register.
index d558ae6f224a9ceef5636322af81714aa75a571b..889ddda910eb4b35fb7b4d90c1e3900d9f74338c 100644 (file)
@@ -558,7 +558,8 @@ class FullCodeGenerator: public AstVisitor {
   F(RegExpConstructResult)                \
   F(GetFromCache)                         \
   F(NumberToString)                       \
-  F(DebugIsActive)
+  F(DebugIsActive)                        \
+  F(CallSuperWithSpread)
 
 #define GENERATOR_DECLARATION(Name) void Emit##Name(CallRuntime* call);
   FOR_EACH_FULL_CODE_INTRINSIC(GENERATOR_DECLARATION)
@@ -589,6 +590,10 @@ class FullCodeGenerator: public AstVisitor {
   // the given function info.
   void EmitNewClosure(Handle<SharedFunctionInfo> info, bool pretenure);
 
+  // Re-usable portions of CallRuntime
+  void EmitLoadJSRuntimeFunction(CallRuntime* expr);
+  void EmitCallJSRuntimeFunction(CallRuntime* expr);
+
   // Platform-specific support for compiling assignments.
 
   // Left-hand side can only be a property, a global or a (parameter or local)
@@ -686,6 +691,7 @@ class FullCodeGenerator: public AstVisitor {
   void EmitSetHomeObjectIfNeeded(Expression* initializer, int offset);
 
   void EmitLoadSuperConstructor();
+  void EmitInitializeThisAfterSuper(SuperReference* super_ref);
 
   void CallIC(Handle<Code> code,
               TypeFeedbackId id = TypeFeedbackId::None());
diff --git a/src/harmony-spread.js b/src/harmony-spread.js
new file mode 100644 (file)
index 0000000..48972fa
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2015 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.
+
+'use strict';
+
+function SpreadArguments() {
+  var count = %_ArgumentsLength();
+  var args = new InternalArray();
+
+  for (var i = 0; i < count; ++i) {
+    var array = %_Arguments(i);
+    var length = array.length;
+    for (var j = 0; j < length; ++j) {
+      args.push(array[j]);
+    }
+  }
+
+  return args;
+}
+
+
+function SpreadIterable(collection) {
+  if (IS_NULL_OR_UNDEFINED(collection)) {
+    throw MakeTypeError("not_iterable", [collection]);
+  }
+
+  var args = new InternalArray();
+  for (var value of collection) {
+    args.push(value);
+  }
+  return args;
+}
index d6cbc1444ad52eac67ab0f98ecd35f3a8e3943ae..6fc434cbae45cbbd6ecb7007e767b7cfa092e84f 100644 (file)
@@ -11013,6 +11013,9 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
 }
 
 
+void HOptimizedGraphBuilder::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
 HInstruction* HOptimizedGraphBuilder::BuildThisFunction() {
   // If we share optimized code between different closures, the
   // this-function is not a constant, except inside an inlined body.
index 1e578b41412e00625fe300bbdb781b2ae8fb57c8..4e7126ba3ffa91c962fa6704bb5d76d6959442a3 100644 (file)
@@ -3005,6 +3005,21 @@ void FullCodeGenerator::EmitLoadSuperConstructor() {
 }
 
 
+void FullCodeGenerator::EmitInitializeThisAfterSuper(
+    SuperReference* super_ref) {
+  Variable* this_var = super_ref->this_var()->var();
+  GetVar(ecx, this_var);
+  __ cmp(ecx, isolate()->factory()->the_hole_value());
+  Label uninitialized_this;
+  __ j(equal, &uninitialized_this);
+  __ push(Immediate(this_var->name()));
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
+}
+
+
 void FullCodeGenerator::VisitCall(Call* expr) {
 #ifdef DEBUG
   // We want to verify that RecordJSReturnSite gets called on all paths
@@ -3219,17 +3234,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  SuperReference* super_ref = expr->expression()->AsSuperReference();
-  Variable* this_var = super_ref->this_var()->var();
-  GetVar(ecx, this_var);
-  __ cmp(ecx, isolate()->factory()->the_hole_value());
-  Label uninitialized_this;
-  __ j(equal, &uninitialized_this);
-  __ push(Immediate(this_var->name()));
-  __ CallRuntime(Runtime::kThrowReferenceError, 1);
-  __ bind(&uninitialized_this);
-
-  EmitVariableAssignment(this_var, Token::INIT_CONST);
+  EmitInitializeThisAfterSuper(expr->expression()->AsSuperReference());
   context()->Plug(eax);
 }
 
@@ -4531,26 +4536,79 @@ void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
 }
 
 
+void FullCodeGenerator::EmitCallSuperWithSpread(CallRuntime* expr) {
+  // Assert: expr == CallRuntime("ReflectConstruct")
+  CallRuntime* call = expr->arguments()->at(0)->AsCallRuntime();
+  ZoneList<Expression*>* args = call->arguments();
+  DCHECK_EQ(3, args->length());
+
+  SuperReference* super_reference = args->at(0)->AsSuperReference();
+
+  // Load ReflectConstruct function
+  EmitLoadJSRuntimeFunction(call);
+
+  // Push the target function under the receiver
+  __ push(Operand(esp, 0));
+  __ mov(Operand(esp, kPointerSize), eax);
+
+  // Push super
+  EmitLoadSuperConstructor();
+  __ Push(result_register());
+
+  // Push arguments array
+  VisitForStackValue(args->at(1));
+
+  // Push NewTarget
+  DCHECK(args->at(2)->IsVariableProxy());
+  VisitForStackValue(args->at(2));
+
+  EmitCallJSRuntimeFunction(call);
+
+  // Restore context register.
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  context()->DropAndPlug(1, eax);
+
+  EmitInitializeThisAfterSuper(super_reference);
+}
+
+
+void FullCodeGenerator::EmitLoadJSRuntimeFunction(CallRuntime* expr) {
+  // Push the builtins object as receiver.
+  __ mov(eax, GlobalObjectOperand());
+  __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
+
+  // Load the function from the receiver.
+  __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0));
+  __ mov(LoadDescriptor::NameRegister(), Immediate(expr->name()));
+  if (FLAG_vector_ics) {
+    __ mov(VectorLoadICDescriptor::SlotRegister(),
+           Immediate(SmiFromSlot(expr->CallRuntimeFeedbackSlot())));
+    CallLoadIC(NOT_CONTEXTUAL);
+  } else {
+    CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
+  }
+}
+
+
+void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+
+  // Record source position of the IC call.
+  SetSourcePosition(expr->position());
+  CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
+  __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
+  __ CallStub(&stub);
+}
+
+
 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
 
   if (expr->is_jsruntime()) {
     Comment cmnt(masm_, "[ CallRuntime");
-    // Push the builtins object as receiver.
-    __ mov(eax, GlobalObjectOperand());
-    __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
-
-    // Load the function from the receiver.
-    __ mov(LoadDescriptor::ReceiverRegister(), Operand(esp, 0));
-    __ mov(LoadDescriptor::NameRegister(), Immediate(expr->name()));
-    if (FLAG_vector_ics) {
-      __ mov(VectorLoadICDescriptor::SlotRegister(),
-             Immediate(SmiFromSlot(expr->CallRuntimeFeedbackSlot())));
-      CallLoadIC(NOT_CONTEXTUAL);
-    } else {
-      CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
-    }
+    EmitLoadJSRuntimeFunction(expr);
 
     // Push the target function under the receiver.
     __ push(Operand(esp, 0));
@@ -4561,11 +4619,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
       VisitForStackValue(args->at(i));
     }
 
-    // Record source position of the IC call.
-    SetSourcePosition(expr->position());
-    CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
-    __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
-    __ CallStub(&stub);
+    EmitCallJSRuntimeFunction(expr);
 
     // Restore context register.
     __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
index 4412b8a98cdca5618ec0b7caf1a77caf246718b3..0dc013202f857d8398a72fb599c2b6daadc94ce1 100644 (file)
@@ -877,6 +877,7 @@ Parser::Parser(ParseInfo* info)
   set_allow_harmony_computed_property_names(
       FLAG_harmony_computed_property_names);
   set_allow_harmony_rest_params(FLAG_harmony_rest_parameters);
+  set_allow_harmony_spreadcalls(FLAG_harmony_spreadcalls);
   set_allow_strong_mode(FLAG_strong_mode);
   for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
        ++feature) {
@@ -4231,6 +4232,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
         allow_harmony_computed_property_names());
     reusable_preparser_->set_allow_harmony_rest_params(
         allow_harmony_rest_params());
+    reusable_preparser_->set_allow_harmony_spreadcalls(
+        allow_harmony_spreadcalls());
     reusable_preparser_->set_allow_strong_mode(allow_strong_mode());
   }
   PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
@@ -4349,7 +4352,10 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) {
   Expect(Token::MOD, CHECK_OK);
   // Allow "eval" or "arguments" for backward compatibility.
   const AstRawString* name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
-  ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
+  Scanner::Location spread_pos;
+  ZoneList<Expression*>* args = ParseArguments(&spread_pos, CHECK_OK);
+
+  DCHECK(!spread_pos.IsValid());
 
   if (extension_ != NULL) {
     // The extension structures are only accessible while parsing the
@@ -5614,4 +5620,121 @@ uint32_t Parser::ComputeTemplateLiteralHash(const TemplateLiteral* lit) {
 
   return running_hash;
 }
+
+
+ZoneList<v8::internal::Expression*>* Parser::PrepareSpreadArguments(
+    ZoneList<v8::internal::Expression*>* list) {
+  ZoneList<v8::internal::Expression*>* args =
+      new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
+  if (list->length() == 1) {
+    // Spread-call with single spread argument produces an InternalArray
+    // containing the values from the array.
+    //
+    // Function is called or constructed with the produced array of arguments
+    //
+    // EG: Apply(Func, Spread(spread0))
+    ZoneList<Expression*>* spread_list =
+        new (zone()) ZoneList<Expression*>(0, zone());
+    spread_list->Add(list->at(0)->AsSpread()->expression(), zone());
+    args->Add(
+        factory()->NewCallRuntime(ast_value_factory()->spread_iterable_string(),
+                                  NULL, spread_list, RelocInfo::kNoPosition),
+        zone());
+    return args;
+  } else {
+    // Spread-call with multiple arguments produces array literals for each
+    // sequences of unspread arguments, and converts each spread iterable to
+    // an Internal array. Finally, all of these produced arrays are flattened
+    // into a single InternalArray, containing the arguments for the call.
+    //
+    // EG: Apply(Func, Flatten([unspread0, unspread1], Spread(spread0),
+    //                         Spread(spread1), [unspread2, unspread3]))
+    int i = 0;
+    int n = list->length();
+    while (i < n) {
+      if (!list->at(i)->IsSpread()) {
+        ZoneList<v8::internal::Expression*>* unspread =
+            new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
+
+        // Push array of unspread parameters
+        while (i < n && !list->at(i)->IsSpread()) {
+          unspread->Add(list->at(i++), zone());
+        }
+        int literal_index = function_state_->NextMaterializedLiteralIndex();
+        args->Add(factory()->NewArrayLiteral(unspread, literal_index,
+                                             RelocInfo::kNoPosition),
+                  zone());
+
+        if (i == n) break;
+      }
+
+      // Push eagerly spread argument
+      ZoneList<v8::internal::Expression*>* spread_list =
+          new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
+      spread_list->Add(list->at(i++)->AsSpread()->expression(), zone());
+      args->Add(factory()->NewCallRuntime(
+                    ast_value_factory()->spread_iterable_string(), NULL,
+                    spread_list, RelocInfo::kNoPosition),
+                zone());
+    }
+
+    list = new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
+    list->Add(factory()->NewCallRuntime(
+                  ast_value_factory()->spread_arguments_string(), NULL, args,
+                  RelocInfo::kNoPosition),
+              zone());
+    return list;
+  }
+  UNREACHABLE();
+}
+
+
+Expression* Parser::SpreadCall(Expression* function,
+                               ZoneList<v8::internal::Expression*>* args,
+                               int pos) {
+  if (function->IsSuperReference()) {
+    // Super calls
+    args->InsertAt(0, function, zone());
+    args->Add(factory()->NewVariableProxy(scope_->new_target_var()), zone());
+    Expression* result = factory()->NewCallRuntime(
+        ast_value_factory()->reflect_construct_string(), NULL, args, pos);
+    args = new (zone()) ZoneList<Expression*>(0, zone());
+    args->Add(result, zone());
+    return factory()->NewCallRuntime(
+        ast_value_factory()->empty_string(),
+        Runtime::FunctionForId(Runtime::kInlineCallSuperWithSpread), args, pos);
+  } else {
+    if (function->IsProperty()) {
+      // Method calls
+      Variable* temp =
+          scope_->NewTemporary(ast_value_factory()->empty_string());
+      VariableProxy* obj = factory()->NewVariableProxy(temp);
+      Assignment* assign_obj = factory()->NewAssignment(
+          Token::ASSIGN, obj, function->AsProperty()->obj(),
+          RelocInfo::kNoPosition);
+      function = factory()->NewProperty(
+          assign_obj, function->AsProperty()->key(), RelocInfo::kNoPosition);
+      args->InsertAt(0, function, zone());
+      obj = factory()->NewVariableProxy(temp);
+      args->InsertAt(1, obj, zone());
+    } else {
+      // Non-method calls
+      args->InsertAt(0, function, zone());
+      args->InsertAt(1, factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
+                     zone());
+    }
+    return factory()->NewCallRuntime(
+        ast_value_factory()->reflect_apply_string(), NULL, args, pos);
+  }
+}
+
+
+Expression* Parser::SpreadCallNew(Expression* function,
+                                  ZoneList<v8::internal::Expression*>* args,
+                                  int pos) {
+  args->InsertAt(0, function, zone());
+
+  return factory()->NewCallRuntime(
+      ast_value_factory()->reflect_construct_string(), NULL, args, pos);
+}
 } }  // namespace v8::internal
index c8fb5611fee2a01a478023bce34946b7e916c38e..e1cceaf34283d698f93614e60c04b1778bd5ae7d 100644 (file)
@@ -811,6 +811,16 @@ class ParserTraits {
     return tag != NULL;
   }
 
+  V8_INLINE ZoneList<v8::internal::Expression*>* PrepareSpreadArguments(
+      ZoneList<v8::internal::Expression*>* list);
+  V8_INLINE void MaterializeUnspreadArgumentsLiterals(int count) {}
+  V8_INLINE Expression* SpreadCall(Expression* function,
+                                   ZoneList<v8::internal::Expression*>* args,
+                                   int pos);
+  V8_INLINE Expression* SpreadCallNew(Expression* function,
+                                      ZoneList<v8::internal::Expression*>* args,
+                                      int pos);
+
  private:
   Parser* parser_;
 };
@@ -1018,6 +1028,13 @@ class Parser : public ParserBase<ParserTraits> {
                                    Expression* tag);
   uint32_t ComputeTemplateLiteralHash(const TemplateLiteral* lit);
 
+  ZoneList<v8::internal::Expression*>* PrepareSpreadArguments(
+      ZoneList<v8::internal::Expression*>* list);
+  Expression* SpreadCall(Expression* function,
+                         ZoneList<v8::internal::Expression*>* args, int pos);
+  Expression* SpreadCallNew(Expression* function,
+                            ZoneList<v8::internal::Expression*>* args, int pos);
+
   Scanner scanner_;
   PreParser* reusable_preparser_;
   Scope* original_scope_;  // for ES5 function declarations in sloppy eval
@@ -1127,6 +1144,25 @@ Expression* ParserTraits::CloseTemplateLiteral(TemplateLiteralState* state,
                                                int start, Expression* tag) {
   return parser_->CloseTemplateLiteral(state, start, tag);
 }
+
+
+ZoneList<v8::internal::Expression*>* ParserTraits::PrepareSpreadArguments(
+    ZoneList<v8::internal::Expression*>* list) {
+  return parser_->PrepareSpreadArguments(list);
+}
+
+
+Expression* ParserTraits::SpreadCall(Expression* function,
+                                     ZoneList<v8::internal::Expression*>* args,
+                                     int pos) {
+  return parser_->SpreadCall(function, args, pos);
+}
+
+
+Expression* ParserTraits::SpreadCallNew(
+    Expression* function, ZoneList<v8::internal::Expression*>* args, int pos) {
+  return parser_->SpreadCallNew(function, args, pos);
+}
 } }  // namespace v8::internal
 
 #endif  // V8_PARSER_H_
index eebfb41c284661ab8f0c5e7f7342bdc3a7238227..15fca26788d56ad5a970d17bcbb834b3d796f244 100644 (file)
@@ -1069,7 +1069,10 @@ PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
   }
   // Allow "eval" or "arguments" for backward compatibility.
   ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
-  ParseArguments(ok);
+  Scanner::Location spread_pos;
+  ParseArguments(&spread_pos, ok);
+
+  DCHECK(!spread_pos.IsValid());
 
   return Expression::Default();
 }
index 1ade9a7e6ff77becf07be9fb2f7b57a49cb224f4..2b61853b0c35db347cb8d4c970d422a1ed576240 100644 (file)
@@ -90,6 +90,7 @@ class ParserBase : public Traits {
         allow_harmony_sloppy_(false),
         allow_harmony_computed_property_names_(false),
         allow_harmony_rest_params_(false),
+        allow_harmony_spreadcalls_(false),
         allow_strong_mode_(false) {}
 
   // Getters that indicate whether certain syntactical constructs are
@@ -112,6 +113,7 @@ class ParserBase : public Traits {
   bool allow_harmony_rest_params() const {
     return allow_harmony_rest_params_;
   }
+  bool allow_harmony_spreadcalls() const { return allow_harmony_spreadcalls_; }
 
   bool allow_strong_mode() const { return allow_strong_mode_; }
 
@@ -143,6 +145,9 @@ class ParserBase : public Traits {
   void set_allow_harmony_rest_params(bool allow) {
     allow_harmony_rest_params_ = allow;
   }
+  void set_allow_harmony_spreadcalls(bool allow) {
+    allow_harmony_spreadcalls_ = allow;
+  }
   void set_allow_strong_mode(bool allow) { allow_strong_mode_ = allow; }
 
  protected:
@@ -574,7 +579,8 @@ class ParserBase : public Traits {
       ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends,
       bool is_static, bool* is_computed_name, bool* has_seen_constructor,
       bool* ok);
-  typename Traits::Type::ExpressionList ParseArguments(bool* ok);
+  typename Traits::Type::ExpressionList ParseArguments(
+      Scanner::Location* first_spread_pos, bool* ok);
   ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok);
   ExpressionT ParseYieldExpression(bool* ok);
   ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok);
@@ -686,6 +692,7 @@ class ParserBase : public Traits {
   bool allow_harmony_sloppy_;
   bool allow_harmony_computed_property_names_;
   bool allow_harmony_rest_params_;
+  bool allow_harmony_spreadcalls_;
   bool allow_strong_mode_;
 };
 
@@ -778,6 +785,10 @@ class PreParserExpression {
     return PreParserExpression(TypeField::encode(kExpression));
   }
 
+  static PreParserExpression Spread(PreParserExpression expression) {
+    return PreParserExpression(TypeField::encode(kSpreadExpression));
+  }
+
   static PreParserExpression FromIdentifier(PreParserIdentifier id) {
     return PreParserExpression(TypeField::encode(kIdentifierExpression) |
                                IdentifierTypeField::encode(id.type_));
@@ -906,6 +917,10 @@ class PreParserExpression {
            ExpressionTypeField::decode(code_) == kNoTemplateTagExpression;
   }
 
+  bool IsSpreadExpression() const {
+    return TypeField::decode(code_) == kSpreadExpression;
+  }
+
   PreParserExpression AsFunctionLiteral() { return *this; }
 
   bool IsBinaryOperation() const {
@@ -938,7 +953,8 @@ class PreParserExpression {
     kExpression,
     kIdentifierExpression,
     kStringLiteralExpression,
-    kBinaryOperationExpression
+    kBinaryOperationExpression,
+    kSpreadExpression
   };
 
   enum Parenthesization {
@@ -964,8 +980,8 @@ class PreParserExpression {
                : (IsIdentifier() && AsIdentifier().IsValidArrowParam());
   }
 
-  // The first four bits are for the Type and Parenthesization.
-  typedef BitField<Type, 0, 2> TypeField;
+  // The first five bits are for the Type and Parenthesization.
+  typedef BitField<Type, 0, 3> TypeField;
   typedef BitField<Parenthesization, TypeField::kNext, 2> ParenthesizationField;
 
   // The rest of the bits are interpreted depending on the value
@@ -1166,6 +1182,12 @@ class PreParserFactory {
                                  int pos) {
     return PreParserExpression::Default();
   }
+  PreParserExpression NewCallRuntime(const AstRawString* name,
+                                     const Runtime::Function* function,
+                                     PreParserExpressionList arguments,
+                                     int pos) {
+    return PreParserExpression::Default();
+  }
   PreParserStatement NewReturnStatement(PreParserExpression expression,
                                         int pos) {
     return PreParserStatement::Default();
@@ -1182,6 +1204,10 @@ class PreParserFactory {
     return PreParserExpression::Default();
   }
 
+  PreParserExpression NewSpread(PreParserExpression expression, int pos) {
+    return PreParserExpression::Spread(expression);
+  }
+
   // Return the object itself as AstVisitor and implement the needed
   // dummy method right in this class.
   PreParserFactory* visitor() { return this; }
@@ -1494,6 +1520,19 @@ class PreParserTraits {
                                         bool name_is_strict_reserved, int pos,
                                         bool* ok);
 
+  PreParserExpressionList PrepareSpreadArguments(PreParserExpressionList list) {
+    return list;
+  }
+
+  inline void MaterializeUnspreadArgumentsLiterals(int count);
+
+  inline PreParserExpression SpreadCall(PreParserExpression function,
+                                        PreParserExpressionList args, int pos);
+
+  inline PreParserExpression SpreadCallNew(PreParserExpression function,
+                                           PreParserExpressionList args,
+                                           int pos);
+
  private:
   PreParser* pre_parser_;
 };
@@ -1634,6 +1673,26 @@ void PreParserTraits::MaterializeTemplateCallsiteLiterals() {
 }
 
 
+void PreParserTraits::MaterializeUnspreadArgumentsLiterals(int count) {
+  for (int i = 0; i < count; ++i) {
+    pre_parser_->function_state_->NextMaterializedLiteralIndex();
+  }
+}
+
+
+PreParserExpression PreParserTraits::SpreadCall(PreParserExpression function,
+                                                PreParserExpressionList args,
+                                                int pos) {
+  return pre_parser_->factory()->NewCall(function, args, pos);
+}
+
+PreParserExpression PreParserTraits::SpreadCallNew(PreParserExpression function,
+                                                   PreParserExpressionList args,
+                                                   int pos) {
+  return pre_parser_->factory()->NewCallNew(function, args, pos);
+}
+
+
 PreParserStatementList PreParser::ParseEagerFunctionBody(
     PreParserIdentifier function_name, int pos, Variable* fvar,
     Token::Value fvar_init_op, FunctionKind kind, bool* ok) {
@@ -2303,18 +2362,42 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
 
 template <class Traits>
 typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments(
-    bool* ok) {
+    Scanner::Location* first_spread_arg_loc, bool* ok) {
   // Arguments ::
   //   '(' (AssignmentExpression)*[','] ')'
 
+  Scanner::Location spread_arg = Scanner::Location::invalid();
   typename Traits::Type::ExpressionList result =
       this->NewExpressionList(4, zone_);
   Expect(Token::LPAREN, CHECK_OK_CUSTOM(NullExpressionList));
   bool done = (peek() == Token::RPAREN);
+  bool was_unspread = false;
+  int unspread_sequences_count = 0;
   while (!done) {
+    bool is_spread = allow_harmony_spreadcalls() && (peek() == Token::ELLIPSIS);
+    int start_pos = peek_position();
+    if (is_spread) Consume(Token::ELLIPSIS);
+
     ExpressionT argument = this->ParseAssignmentExpression(
         true, CHECK_OK_CUSTOM(NullExpressionList));
+    if (is_spread) {
+      if (!spread_arg.IsValid()) {
+        spread_arg.beg_pos = start_pos;
+        spread_arg.end_pos = peek_position();
+      }
+      argument = factory()->NewSpread(argument, start_pos);
+    }
     result->Add(argument, zone_);
+
+    // unspread_sequences_count is the number of sequences of parameters which
+    // are not prefixed with a spread '...' operator.
+    if (is_spread) {
+      was_unspread = false;
+    } else if (!was_unspread) {
+      was_unspread = true;
+      unspread_sequences_count++;
+    }
+
     if (result->length() > Code::kMaxArguments) {
       ReportMessage("too_many_arguments");
       *ok = false;
@@ -2331,6 +2414,15 @@ typename Traits::Type::ExpressionList ParserBase<Traits>::ParseArguments(
     *ok = false;
     return this->NullExpressionList();
   }
+  *first_spread_arg_loc = spread_arg;
+
+  if (spread_arg.IsValid()) {
+    // Unspread parameter sequences are translated into array literals in the
+    // parser. Ensure that the number of materialized literals matches between
+    // the parser and preparser
+    Traits::MaterializeUnspreadArgumentsLiterals(unspread_sequences_count);
+  }
+
   return result;
 }
 
@@ -2645,7 +2737,9 @@ ParserBase<Traits>::ParseLeftHandSideExpression(bool* ok) {
             result->AsFunctionLiteral()->set_parenthesized();
           }
         }
-        typename Traits::Type::ExpressionList args = ParseArguments(CHECK_OK);
+        Scanner::Location spread_pos;
+        typename Traits::Type::ExpressionList args =
+            ParseArguments(&spread_pos, CHECK_OK);
 
         // Keep track of eval() calls since they disable all local variable
         // optimizations.
@@ -2655,7 +2749,13 @@ ParserBase<Traits>::ParseLeftHandSideExpression(bool* ok) {
         // These calls are marked as potentially direct eval calls. Whether
         // they are actually direct calls to eval is determined at run time.
         this->CheckPossibleEvalCall(result, scope_);
-        result = factory()->NewCall(result, args, pos);
+
+        if (spread_pos.IsValid()) {
+          args = Traits::PrepareSpreadArguments(args);
+          result = Traits::SpreadCall(result, args, pos);
+        } else {
+          result = factory()->NewCall(result, args, pos);
+        }
         if (fni_ != NULL) fni_->RemoveLastFunction();
         break;
       }
@@ -2709,9 +2809,16 @@ ParserBase<Traits>::ParseMemberWithNewPrefixesExpression(bool* ok) {
     }
     if (peek() == Token::LPAREN) {
       // NewExpression with arguments.
+      Scanner::Location spread_pos;
       typename Traits::Type::ExpressionList args =
-          this->ParseArguments(CHECK_OK);
-      result = factory()->NewCallNew(result, args, new_pos);
+          this->ParseArguments(&spread_pos, CHECK_OK);
+
+      if (spread_pos.IsValid()) {
+        args = Traits::PrepareSpreadArguments(args);
+        result = Traits::SpreadCallNew(result, args, new_pos);
+      } else {
+        result = factory()->NewCallNew(result, args, new_pos);
+      }
       // The expression can still continue with . or [ after the arguments.
       result = this->ParseMemberExpressionContinuation(result, CHECK_OK);
       return result;
index 165e71721228ce6abae4820d27c50c52d53072d2..78b73d6add58dfe9aa738e238787011dab6ceb30 100644 (file)
@@ -375,6 +375,13 @@ void CallPrinter::VisitCompareOperation(CompareOperation* node) {
 }
 
 
+void CallPrinter::VisitSpread(Spread* node) {
+  Print("(...");
+  Find(node->expression(), true);
+  Print(")");
+}
+
+
 void CallPrinter::VisitThisFunction(ThisFunction* node) {}
 
 
@@ -870,6 +877,13 @@ void PrettyPrinter::VisitCompareOperation(CompareOperation* node) {
 }
 
 
+void PrettyPrinter::VisitSpread(Spread* node) {
+  Print("(...");
+  Visit(node->expression());
+  Print(")");
+}
+
+
 void PrettyPrinter::VisitThisFunction(ThisFunction* node) {
   Print("<this-function>");
 }
@@ -1573,6 +1587,12 @@ void AstPrinter::VisitCompareOperation(CompareOperation* node) {
 }
 
 
+void AstPrinter::VisitSpread(Spread* node) {
+  IndentedScope indent(this, "...");
+  Visit(node->expression());
+}
+
+
 void AstPrinter::VisitThisFunction(ThisFunction* node) {
   IndentedScope indent(this, "THIS-FUNCTION");
 }
index 67d7e6939a634213440744b054796355d6e3bd44..9f56c7949b1c123f7a23083efad72023bcd767fc 100644 (file)
@@ -457,5 +457,11 @@ RUNTIME_FUNCTION(Runtime_DefaultConstructorCallSuper) {
   UNIMPLEMENTED();
   return nullptr;
 }
+
+
+RUNTIME_FUNCTION(Runtime_CallSuperWithSpread) {
+  UNIMPLEMENTED();
+  return nullptr;
+}
 }
 }  // namespace v8::internal
index a051fd0276e7c7b6fb91db539266dc8ea654d9c4..879e22efd7e11523ddd84da486cf5cdfdacef640 100644 (file)
@@ -70,7 +70,8 @@ namespace internal {
   F(StoreKeyedToSuper_Strict, 4, 1)           \
   F(StoreKeyedToSuper_Sloppy, 4, 1)           \
   F(HandleStepInForDerivedConstructors, 1, 1) \
-  F(DefaultConstructorCallSuper, 0, 1)
+  F(DefaultConstructorCallSuper, 0, 1)        \
+  F(CallSuperWithSpread, 0, 1)
 
 
 #define FOR_EACH_INTRINSIC_COLLECTIONS(F) \
index 1598c09b59b77f23402f418d3fce0d5263df2727..79ec0dde57b57595b19ec2b5eba143db64d68ae4 100644 (file)
@@ -769,6 +769,9 @@ void AstTyper::VisitCompareOperation(CompareOperation* expr) {
 }
 
 
+void AstTyper::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
 void AstTyper::VisitThisFunction(ThisFunction* expr) {
 }
 
index 34fbd81015c9fe70689402cf1ed8cb557f371a63..9c7a41f44906a2e4c326d9b52d40f9fbc496899d 100644 (file)
@@ -3008,6 +3008,21 @@ void FullCodeGenerator::EmitLoadSuperConstructor() {
 }
 
 
+void FullCodeGenerator::EmitInitializeThisAfterSuper(
+    SuperReference* super_ref) {
+  Variable* this_var = super_ref->this_var()->var();
+  GetVar(rcx, this_var);
+  __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
+  Label uninitialized_this;
+  __ j(equal, &uninitialized_this);
+  __ Push(this_var->name());
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
+}
+
+
 void FullCodeGenerator::VisitCall(Call* expr) {
 #ifdef DEBUG
   // We want to verify that RecordJSReturnSite gets called on all paths
@@ -3222,17 +3237,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  SuperReference* super_ref = expr->expression()->AsSuperReference();
-  Variable* this_var = super_ref->this_var()->var();
-  GetVar(rcx, this_var);
-  __ CompareRoot(rcx, Heap::kTheHoleValueRootIndex);
-  Label uninitialized_this;
-  __ j(equal, &uninitialized_this);
-  __ Push(this_var->name());
-  __ CallRuntime(Runtime::kThrowReferenceError, 1);
-  __ bind(&uninitialized_this);
-
-  EmitVariableAssignment(this_var, Token::INIT_CONST);
+  EmitInitializeThisAfterSuper(expr->expression()->AsSuperReference());
   context()->Plug(rax);
 }
 
@@ -4553,26 +4558,80 @@ void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
 }
 
 
+void FullCodeGenerator::EmitCallSuperWithSpread(CallRuntime* expr) {
+  // Assert: expr === CallRuntime("ReflectConstruct")
+  CallRuntime* call = expr->arguments()->at(0)->AsCallRuntime();
+  ZoneList<Expression*>* args = call->arguments();
+  DCHECK_EQ(3, args->length());
+
+  SuperReference* super_reference = args->at(0)->AsSuperReference();
+
+  // Load ReflectConstruct function
+  EmitLoadJSRuntimeFunction(call);
+
+  // Push the target function under the receiver.
+  __ Push(Operand(rsp, 0));
+  __ movp(Operand(rsp, kPointerSize), rax);
+
+  // Push super
+  EmitLoadSuperConstructor();
+  __ Push(result_register());
+
+  // Push arguments array
+  VisitForStackValue(args->at(1));
+
+  // Push NewTarget
+  DCHECK(args->at(2)->IsVariableProxy());
+  VisitForStackValue(args->at(2));
+
+  EmitCallJSRuntimeFunction(call);
+
+  // Restore context register.
+  __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+  context()->DropAndPlug(1, rax);
+
+  EmitInitializeThisAfterSuper(super_reference);
+}
+
+
+void FullCodeGenerator::EmitLoadJSRuntimeFunction(CallRuntime* expr) {
+  // Push the builtins object as receiver.
+  __ movp(rax, GlobalObjectOperand());
+  __ Push(FieldOperand(rax, GlobalObject::kBuiltinsOffset));
+
+  // Load the function from the receiver.
+  __ movp(LoadDescriptor::ReceiverRegister(), Operand(rsp, 0));
+  __ Move(LoadDescriptor::NameRegister(), expr->name());
+  if (FLAG_vector_ics) {
+    __ Move(VectorLoadICDescriptor::SlotRegister(),
+            SmiFromSlot(expr->CallRuntimeFeedbackSlot()));
+    CallLoadIC(NOT_CONTEXTUAL);
+  } else {
+    CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
+  }
+}
+
+
+void FullCodeGenerator::EmitCallJSRuntimeFunction(CallRuntime* expr) {
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+
+  // Record source position of the IC call.
+  SetSourcePosition(expr->position());
+  CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
+  __ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
+  __ CallStub(&stub);
+}
+
+
 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
 
   if (expr->is_jsruntime()) {
     Comment cmnt(masm_, "[ CallRuntime");
-    // Push the builtins object as receiver.
-    __ movp(rax, GlobalObjectOperand());
-    __ Push(FieldOperand(rax, GlobalObject::kBuiltinsOffset));
 
-    // Load the function from the receiver.
-    __ movp(LoadDescriptor::ReceiverRegister(), Operand(rsp, 0));
-    __ Move(LoadDescriptor::NameRegister(), expr->name());
-    if (FLAG_vector_ics) {
-      __ Move(VectorLoadICDescriptor::SlotRegister(),
-              SmiFromSlot(expr->CallRuntimeFeedbackSlot()));
-      CallLoadIC(NOT_CONTEXTUAL);
-    } else {
-      CallLoadIC(NOT_CONTEXTUAL, expr->CallRuntimeFeedbackId());
-    }
+    EmitLoadJSRuntimeFunction(expr);
 
     // Push the target function under the receiver.
     __ Push(Operand(rsp, 0));
@@ -4583,11 +4642,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
       VisitForStackValue(args->at(i));
     }
 
-    // Record source position of the IC call.
-    SetSourcePosition(expr->position());
-    CallFunctionStub stub(isolate(), arg_count, NO_CALL_FUNCTION_FLAGS);
-    __ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
-    __ CallStub(&stub);
+    EmitCallJSRuntimeFunction(expr);
 
     // Restore context register.
     __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
index 1f880c53ce4b285264679e073a8141bd8c9c27ad..33e2d3940708f4c10f46ffebf8ccd695c4908ff4 100644 (file)
@@ -1380,7 +1380,8 @@ enum ParserFlag {
   kAllowHarmonySloppy,
   kAllowHarmonyUnicode,
   kAllowHarmonyComputedPropertyNames,
-  kAllowStrongMode
+  kAllowStrongMode,
+  kAllowHarmonySpreadCalls
 };
 
 
@@ -1403,6 +1404,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
   parser->set_allow_harmony_classes(flags.Contains(kAllowHarmonyClasses));
   parser->set_allow_harmony_rest_params(
       flags.Contains(kAllowHarmonyRestParameters));
+  parser->set_allow_harmony_spreadcalls(
+      flags.Contains(kAllowHarmonySpreadCalls));
   parser->set_allow_harmony_sloppy(flags.Contains(kAllowHarmonySloppy));
   parser->set_allow_harmony_unicode(flags.Contains(kAllowHarmonyUnicode));
   parser->set_allow_harmony_computed_property_names(
@@ -5104,6 +5107,52 @@ TEST(RestParametersDuplicateEvalArguments) {
 }
 
 
+TEST(SpreadCall) {
+  const char* context_data[][2] = {{"function fn() { 'use strict';} fn(", ");"},
+                                   {"function fn() {} fn(", ");"},
+                                   {NULL, NULL}};
+
+  const char* data[] = {
+      "...([1, 2, 3])", "...'123', ...'456'", "...new Set([1, 2, 3]), 4",
+      "1, ...[2, 3], 4", "...Array(...[1,2,3,4])", "...NaN",
+      "0, 1, ...[2, 3, 4], 5, 6, 7, ...'89'",
+      "0, 1, ...[2, 3, 4], 5, 6, 7, ...'89', 10",
+      "...[0, 1, 2], 3, 4, 5, 6, ...'7', 8, 9",
+      "...[0, 1, 2], 3, 4, 5, 6, ...'7', 8, 9, ...[10]", NULL};
+
+  static const ParserFlag always_flags[] = {kAllowHarmonySpreadCalls};
+
+  RunParserSyncTest(context_data, data, kSuccess, NULL, 0, always_flags,
+                    arraysize(always_flags));
+}
+
+
+TEST(SpreadCallErrors) {
+  const char* context_data[][2] = {{"function fn() { 'use strict';} fn(", ");"},
+                                   {"function fn() {} fn(", ");"},
+                                   {NULL, NULL}};
+
+  const char* data[] = {"(...[1, 2, 3])", "......[1,2,3]", NULL};
+
+  static const ParserFlag always_flags[] = {kAllowHarmonySpreadCalls};
+
+  RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
+                    arraysize(always_flags));
+}
+
+
+TEST(BadRestSpread) {
+  const char* context_data[][2] = {{"function fn() { 'use strict';", "} fn();"},
+                                   {"function fn() { ", "} fn();"},
+                                   {NULL, NULL}};
+  const char* data[] = {"return ...[1,2,3];",     "var ...x = [1,2,3];",
+                        "var [...x,] = [1,2,3];", "var [...x, y] = [1,2,3];",
+                        "var {...x} = [1,2,3];",  "var { x } = {x: ...[1,2,3]}",
+                        NULL};
+  RunParserSyncTest(context_data, data, kError, NULL, 0, NULL, 0);
+}
+
+
 TEST(LexicalScopingSloppyMode) {
   const char* context_data[][2] = {
       {"", ""},
index a703642390db8fd032cbe383d34b89b412485a5c..daae67de59595729e3a1cbb4d5009c4218b3cf5a 100644 (file)
@@ -7,6 +7,21 @@
   "total": true,
   "resources": ["base.js"],
   "tests": [
+    {
+      "name": "SpreadCalls",
+      "path": ["SpreadCalls"],
+      "main": "run.js",
+      "resources": ["spreadcalls.js"],
+      "flags": ["--harmony-spreadcalls"],
+      "run_count": 5,
+      "units": "score",
+      "results_regexp": "^%s\\-SpreadCalls\\(Score\\): (.+)$",
+      "tests": [
+        {"name": "Call-Sum"},
+        {"name": "CallMethod-Sum"},
+        {"name": "CallNew-Sum"}
+      ]
+    },
     {
       "name": "Classes",
       "path": ["Classes"],
diff --git a/test/js-perf-test/SpreadCalls/run.js b/test/js-perf-test/SpreadCalls/run.js
new file mode 100644 (file)
index 0000000..55e0c86
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2014 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.
+
+
+load('../base.js');
+load('spreadcalls.js');
+
+
+var success = true;
+
+function PrintResult(name, result) {
+  print(name + '-SpreadCalls(Score): ' + result);
+}
+
+
+function PrintError(name, error) {
+  PrintResult(name, error);
+  success = false;
+}
+
+
+BenchmarkSuite.config.doWarmup = undefined;
+BenchmarkSuite.config.doDeterministic = undefined;
+
+BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+                           NotifyError: PrintError });
diff --git a/test/js-perf-test/SpreadCalls/spreadcalls.js b/test/js-perf-test/SpreadCalls/spreadcalls.js
new file mode 100644 (file)
index 0000000..794c53f
--- /dev/null
@@ -0,0 +1,77 @@
+// Copyright 2014 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.
+
+new BenchmarkSuite('Call', [1000], [
+  new Benchmark('Call-Sum', false, false, 0,
+                CallSum, CallSumSetup,
+                CallSumTearDown),
+]);
+
+new BenchmarkSuite('CallMethod', [1000], [
+  new Benchmark('CallMethod-Sum', false, false, 0,
+                CallMethodSum, CallSumSetup, CallMethodSumTearDown),
+]);
+
+new BenchmarkSuite('CallNew', [1000], [
+  new Benchmark('CallNew-Sum', false, false, 0,
+                CallNewSum, CallSumSetup,
+                CallNewSumTearDown),
+]);
+
+var result;
+var objectToSpread;
+
+function sum() {
+  var result = arguments[0];
+  for (var i = 1; i < arguments.length; ++i) {
+    result += arguments[i];
+  }
+  return result;
+}
+
+function CallSumSetup() {
+  result = undefined;
+  objectToSpread = [];
+  for (var i = 0; i < 100; ++i) objectToSpread.push(i + 1);
+}
+
+function CallSum() {
+  result = sum(...objectToSpread);
+}
+
+function CallSumTearDown() {
+  var expected = 100 * (100 + 1) / 2;
+  return result === expected;
+}
+
+// ----------------------------------------------------------------------------
+
+var O = { sum: sum };
+function CallMethodSum() {
+  result = O.sum(...objectToSpread);
+}
+
+function CallMethodSumTearDown() {
+  var expected = 100 * (100 + 1) / 2;
+  return result === expected;
+}
+
+// ----------------------------------------------------------------------------
+
+function Sum() {
+  var result = arguments[0];
+  for (var i = 1; i < arguments.length; ++i) {
+    result += arguments[i];
+  }
+  return this.sum = result;
+}
+
+function CallNewSum() {
+  result = new Sum(...objectToSpread);
+}
+
+function CallNewSumTearDown() {
+  var expected = 100 * (100 + 1) / 2;
+  return result instanceof Sum && result.sum === expected;
+}
diff --git a/test/mjsunit/harmony/spread-call-new-class.js b/test/mjsunit/harmony/spread-call-new-class.js
new file mode 100644 (file)
index 0000000..fcd0a21
--- /dev/null
@@ -0,0 +1,92 @@
+// Copyright 2015 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-spreadcalls --harmony-sloppy --harmony-rest-parameters
+
+
+(function testConstructClassStrict() {
+  "use strict";
+  class Base {
+    constructor(...args) {
+      this.baseArgs = args;
+    }
+    method() { return this.baseArgs; }
+  }
+
+  class Child extends Base {
+    constructor(...args) {
+      super(...args);
+      this.childArgs = args;
+    }
+  }
+
+  class Child2 extends Base {
+    constructor(...args) {
+      super("extra", ...args);
+      this.childArgs = args;
+    }
+  }
+
+  var c = new Base(...[1, 2, 3]);
+  assertInstanceof(c, Base);
+  assertEquals([1, 2, 3], c.method());
+  assertEquals([1, 2, 3], c.baseArgs);
+
+  c = new Child(...[1, 2, 3]);
+  assertInstanceof(c, Child);
+  assertInstanceof(c, Base);
+  assertEquals([1, 2, 3], c.method());
+  assertEquals([1, 2, 3], c.baseArgs);
+  assertEquals([1, 2, 3], c.childArgs);
+
+  c = new Child2(...[1, 2, 3]);
+  assertInstanceof(c, Child2);
+  assertInstanceof(c, Base);
+  assertEquals(["extra", 1, 2, 3], c.method());
+  assertEquals(["extra", 1, 2, 3], c.baseArgs);
+  assertEquals([1, 2, 3], c.childArgs);
+})();
+
+
+(function testConstructSloppy() {
+  class Base {
+    constructor(...args) {
+      this.baseArgs = args;
+    }
+    method() { return this.baseArgs; }
+  }
+
+  class Child extends Base {
+    constructor(...args) {
+      super(...args);
+      this.childArgs = args;
+    }
+  }
+
+  class Child2 extends Base {
+    constructor(...args) {
+      super("extra", ...args);
+      this.childArgs = args;
+    }
+  }
+
+  var c = new Base(...[1, 2, 3]);
+  assertInstanceof(c, Base);
+  assertEquals([1, 2, 3], c.method());
+  assertEquals([1, 2, 3], c.baseArgs);
+
+  c = new Child(...[1, 2, 3]);
+  assertInstanceof(c, Child);
+  assertInstanceof(c, Base);
+  assertEquals([1, 2, 3], c.method());
+  assertEquals([1, 2, 3], c.baseArgs);
+  assertEquals([1, 2, 3], c.childArgs);
+
+  c = new Child2(...[1, 2, 3]);
+  assertInstanceof(c, Child2);
+  assertInstanceof(c, Base);
+  assertEquals(["extra", 1, 2, 3], c.method());
+  assertEquals(["extra", 1, 2, 3], c.baseArgs);
+  assertEquals([1, 2, 3], c.childArgs);
+})();
diff --git a/test/mjsunit/harmony/spread-call-new.js b/test/mjsunit/harmony/spread-call-new.js
new file mode 100644 (file)
index 0000000..78f873e
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2015 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-spreadcalls
+
+(function testNonConstructorStrict() {
+  "use strict";
+  assertThrows(function() {
+    return new Math.cos(...[1,2,3]);
+  }, TypeError);
+
+  assertThrows(function() {
+    var CallNull = null;
+    return new CallNull(...[1,2,3]);
+  }, TypeError);
+})();
+
+
+(function testNonConstructorSloppy() {
+  assertThrows(function() {
+    return new Math.cos(...[1,2,3]);
+  }, TypeError);
+
+  assertThrows(function() {
+    var CallNull = null;
+    return new CallNull(...[1,2,3]);
+  }, TypeError);
+})();
+
+
+(function testConstructStrict() {
+  "use strict";
+  function TestClass(a, b, c) {
+    this.wasCalled = true;
+    this.args = [a, b, c];
+  }
+  TestClass.prototype.method = function() {
+    return this.args;
+  }
+
+  assertInstanceof(new TestClass(...[1, 2, 3]), TestClass);
+  assertEquals([1, 2, 3], (new TestClass(...[1, 2, 3])).method());
+  assertEquals([1, 2, 3], (new TestClass(...[1, 2, 3])).args);
+  assertTrue((new TestClass(...[1, 2, 3])).wasCalled);
+})();
+
+
+(function testConstructSloppy() {
+  function TestClass(a, b, c) {
+    this.wasCalled = true;
+    this.args = [a, b, c];
+  }
+  TestClass.prototype.method = function() {
+    return this.args;
+  }
+
+  assertInstanceof(new TestClass(...[1, 2, 3]), TestClass);
+  assertEquals([1, 2, 3], (new TestClass(...[1, 2, 3])).method());
+  assertEquals([1, 2, 3], (new TestClass(...[1, 2, 3])).args);
+  assertTrue((new TestClass(...[1, 2, 3])).wasCalled);
+})();
diff --git a/test/mjsunit/harmony/spread-call.js b/test/mjsunit/harmony/spread-call.js
new file mode 100644 (file)
index 0000000..e10a3ec
--- /dev/null
@@ -0,0 +1,374 @@
+// Copyright 2015 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-spreadcalls
+
+(function testSpreadCallsStrict() {
+  "use strict"
+  function countArgs() { return arguments.length; }
+
+  // Test this argument
+  function returnThis() { return this; }
+  assertEquals(void 0, returnThis(..."test"));
+
+  // Test argument counting with different iterables
+  assertEquals(0, countArgs(...""));
+  assertEquals(4, countArgs(..."test"));
+  assertEquals(4, countArgs(..."tes", ..."t"));
+  assertEquals(4, countArgs("t", ..."es", "t"));
+  assertEquals(4, countArgs("tes", ..."t!!"));
+
+  assertEquals(1, countArgs(...[1]));
+  assertEquals(2, countArgs(...[1, 2]));
+  assertEquals(3, countArgs(...[1, 2, 3]));
+  assertEquals(4, countArgs(...[1, 2, 3, 4]));
+  assertEquals(5, countArgs(...[1, 2, 3, 4, 5]));
+  assertEquals(6, countArgs(...[1, 2, 3, 4, 5, 6]));
+
+  assertEquals(1, countArgs(...new Set([1])));
+  assertEquals(2, countArgs(...new Set([1, 2])));
+  assertEquals(3, countArgs(...new Set([1, 2, 3])));
+  assertEquals(4, countArgs(...new Set([1, 2, 3, 4])));
+  assertEquals(5, countArgs(...new Set([1, 2, 3, 4, 5])));
+  assertEquals(6, countArgs(...new Set([1, 2, 3, 4, 5, 6])));
+
+  assertEquals(3, countArgs(...(function*(){ yield 1; yield 2; yield 3; })()));
+
+  // Test values
+  function sum() {
+    var sum = arguments[0];
+    for (var i = 1; i < arguments.length; ++i) {
+      sum += arguments[i];
+    }
+    return sum;
+  }
+
+  assertEquals(void 0, sum(...""));
+  assertEquals(void 0, sum(...[]));
+  assertEquals(void 0, sum(...new Set));
+  assertEquals(void 0, sum(...(function*() { })()));
+
+  assertEquals("test", sum(..."test"));
+  assertEquals(10, sum(...[1, 2, 3, 4]));
+  assertEquals(10, sum(...[1, 2, 3], 4));
+  assertEquals(10, sum(1, ...[2, 3], 4));
+  assertEquals(10, sum(1, ...[2, 3], ...[4]));
+  assertEquals(10, sum(1, 2, ...[3, 4]));
+  assertEquals(10, sum(...new Set([1, 2, 3, 4])));
+  assertEquals(10, sum(...(function*() {
+    yield 1;
+    yield 2;
+    yield 3;
+    yield 4;
+  })()));
+
+  // nested spreads
+  function makeArray() {
+    var result = [];
+    for (var i = 0; i < arguments.length; ++i) {
+      result.push(arguments[i]);
+    }
+    return result;
+  }
+  assertEquals(10, sum(...makeArray(...[1, 2, 3, 4])));
+  assertEquals("test!!!", sum(...makeArray(..."test!!!")));
+
+  // Interleaved spread/unspread args
+  assertEquals(36, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
+  assertEquals(45, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
+
+  // Methods
+  var O = {
+    returnThis: returnThis,
+    countArgs: countArgs,
+    sum: sum,
+    makeArray: makeArray,
+
+    nested: {
+      returnThis: returnThis,
+      countArgs: countArgs,
+      sum: sum,
+      makeArray: makeArray
+    }
+  };
+
+  // Test this argument
+  assertEquals(O, O.returnThis(..."test"));
+  assertEquals(O, O["returnThis"](..."test"));
+  assertEquals(O.nested, O.nested.returnThis(..."test"));
+  assertEquals(O.nested, O.nested["returnThis"](..."test"));
+
+  // Test argument counting with different iterables
+  assertEquals(0, O.countArgs(...""));
+  assertEquals(4, O.countArgs(..."test"));
+  assertEquals(4, O.countArgs(..."tes", ..."t"));
+  assertEquals(4, O.countArgs("t", ..."es", "t"));
+  assertEquals(4, O.countArgs("tes", ..."t!!"));
+
+  assertEquals(1, O.countArgs(...[1]));
+  assertEquals(2, O.countArgs(...[1, 2]));
+  assertEquals(3, O.countArgs(...[1, 2, 3]));
+  assertEquals(4, O.countArgs(...[1, 2, 3, 4]));
+  assertEquals(5, O.countArgs(...[1, 2, 3, 4, 5]));
+  assertEquals(6, O.countArgs(...[1, 2, 3, 4, 5, 6]));
+
+  assertEquals(1, O.countArgs(...new Set([1])));
+  assertEquals(2, O.countArgs(...new Set([1, 2])));
+  assertEquals(3, O.countArgs(...new Set([1, 2, 3])));
+  assertEquals(4, O.countArgs(...new Set([1, 2, 3, 4])));
+  assertEquals(5, O.countArgs(...new Set([1, 2, 3, 4, 5])));
+  assertEquals(6, O.countArgs(...new Set([1, 2, 3, 4, 5, 6])));
+
+  assertEquals(3, O.countArgs(
+      ...(function*(){ yield 1; yield 2; yield 3; })()));
+
+  // Test values
+  assertEquals(void 0, O.sum(...""));
+  assertEquals(void 0, O.sum(...[]));
+  assertEquals(void 0, O.sum(...new Set));
+  assertEquals(void 0, O.sum(...(function*() { })()));
+
+  assertEquals("test", O.sum(..."test"));
+  assertEquals(10, O.sum(...[1, 2, 3, 4]));
+  assertEquals(10, O.sum(...[1, 2, 3], 4));
+  assertEquals(10, O.sum(1, ...[2, 3], 4));
+  assertEquals(10, O.sum(1, ...[2, 3], ...[4]));
+  assertEquals(10, O.sum(1, 2, ...[3, 4]));
+  assertEquals(10, O.sum(...new Set([1, 2, 3, 4])));
+  assertEquals(10, O.sum(...(function*() {
+    yield 1;
+    yield 2;
+    yield 3;
+    yield 4;
+  })()));
+
+  // nested spreads
+  assertEquals(10, O.sum(...O.makeArray(...[1, 2, 3, 4])));
+  assertEquals("test!!!", O.sum(...O.makeArray(..."test!!!")));
+
+  // Interleaved spread/unspread args
+  assertEquals(36, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
+  assertEquals(45, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
+})();
+
+
+(function testSpreadCallsSloppy() {
+  // Test this argument
+  function returnThis() { return this; }
+  assertEquals(this, returnThis(..."test"));
+
+  function countArgs() { return arguments.length; }
+
+  // Test argument counting with different iterables
+  assertEquals(0, countArgs(...""));
+  assertEquals(4, countArgs(..."test"));
+  assertEquals(4, countArgs(..."tes", ..."t"));
+  assertEquals(4, countArgs("t", ..."es", "t"));
+  assertEquals(4, countArgs("tes", ..."t!!"));
+
+  assertEquals(1, countArgs(...[1]));
+  assertEquals(2, countArgs(...[1, 2]));
+  assertEquals(3, countArgs(...[1, 2, 3]));
+  assertEquals(4, countArgs(...[1, 2, 3, 4]));
+  assertEquals(5, countArgs(...[1, 2, 3, 4, 5]));
+  assertEquals(6, countArgs(...[1, 2, 3, 4, 5, 6]));
+
+  assertEquals(1, countArgs(...new Set([1])));
+  assertEquals(2, countArgs(...new Set([1, 2])));
+  assertEquals(3, countArgs(...new Set([1, 2, 3])));
+  assertEquals(4, countArgs(...new Set([1, 2, 3, 4])));
+  assertEquals(5, countArgs(...new Set([1, 2, 3, 4, 5])));
+  assertEquals(6, countArgs(...new Set([1, 2, 3, 4, 5, 6])));
+
+  assertEquals(3, countArgs(...(function*(){
+    yield 1;
+    yield 2;
+    yield 3;
+  })()));
+
+  // Test values
+  function sum() {
+    var sum = arguments[0];
+    for (var i = 1; i < arguments.length; ++i) {
+      sum += arguments[i];
+    }
+    return sum;
+  }
+
+  assertEquals(void 0, sum(...""));
+  assertEquals(void 0, sum(...[]));
+  assertEquals(void 0, sum(...new Set));
+  assertEquals(void 0, sum(...(function*() { })()));
+
+  assertEquals("test", sum(..."test"));
+  assertEquals(10, sum(...[1, 2, 3, 4]));
+  assertEquals(10, sum(...[1, 2, 3], 4));
+  assertEquals(10, sum(1, ...[2, 3], 4));
+  assertEquals(10, sum(1, ...[2, 3], ...[4]));
+  assertEquals(10, sum(1, 2, ...[3, 4]));
+  assertEquals(10, sum(...new Set([1, 2, 3, 4])));
+  assertEquals(10, sum(...(function*() {
+    yield 1;
+    yield 2;
+    yield 3;
+    yield 4;
+  })()));
+
+  // nested spreads
+  function makeArray() {
+    var result = [];
+    for (var i = 0; i < arguments.length; ++i) {
+      result.push(arguments[i]);
+    }
+    return result;
+  }
+  assertEquals(10, sum(...makeArray(...[1, 2, 3, 4])));
+  assertEquals("test!!!", sum(...makeArray(..."test!!!")));
+
+  // Interleaved spread/unspread args
+  assertEquals(36, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
+  assertEquals(45, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
+
+  // Methods
+  var O = {
+    returnThis: returnThis,
+    countArgs: countArgs,
+    sum: sum,
+    makeArray: makeArray,
+
+    nested: {
+      returnThis: returnThis,
+      countArgs: countArgs,
+      sum: sum,
+      makeArray: makeArray
+    }
+  };
+
+  // Test this argument
+  assertEquals(O, O.returnThis(..."test"));
+  assertEquals(O, O["returnThis"](..."test"));
+  assertEquals(O.nested, O.nested.returnThis(..."test"));
+  assertEquals(O.nested, O.nested["returnThis"](..."test"));
+
+  // Test argument counting with different iterables
+  assertEquals(0, O.countArgs(...""));
+  assertEquals(4, O.countArgs(..."test"));
+  assertEquals(4, O.countArgs(..."tes", ..."t"));
+  assertEquals(4, O.countArgs("t", ..."es", "t"));
+  assertEquals(4, O.countArgs("tes", ..."t!!"));
+
+  assertEquals(1, O.countArgs(...[1]));
+  assertEquals(2, O.countArgs(...[1, 2]));
+  assertEquals(3, O.countArgs(...[1, 2, 3]));
+  assertEquals(4, O.countArgs(...[1, 2, 3, 4]));
+  assertEquals(5, O.countArgs(...[1, 2, 3, 4, 5]));
+  assertEquals(6, O.countArgs(...[1, 2, 3, 4, 5, 6]));
+
+  assertEquals(1, O.countArgs(...new Set([1])));
+  assertEquals(2, O.countArgs(...new Set([1, 2])));
+  assertEquals(3, O.countArgs(...new Set([1, 2, 3])));
+  assertEquals(4, O.countArgs(...new Set([1, 2, 3, 4])));
+  assertEquals(5, O.countArgs(...new Set([1, 2, 3, 4, 5])));
+  assertEquals(6, O.countArgs(...new Set([1, 2, 3, 4, 5, 6])));
+
+  assertEquals(3, O.countArgs(...(function*(){
+    yield 1;
+    yield 2;
+    yield 3;
+  })()));
+
+  // Test values
+  assertEquals(void 0, O.sum(...""));
+  assertEquals(void 0, O.sum(...[]));
+  assertEquals(void 0, O.sum(...new Set));
+  assertEquals(void 0, O.sum(...(function*() { })()));
+
+  assertEquals("test", O.sum(..."test"));
+  assertEquals(10, O.sum(...[1, 2, 3, 4]));
+  assertEquals(10, O.sum(...[1, 2, 3], 4));
+  assertEquals(10, O.sum(1, ...[2, 3], 4));
+  assertEquals(10, O.sum(1, ...[2, 3], ...[4]));
+  assertEquals(10, O.sum(1, 2, ...[3, 4]));
+  assertEquals(10, O.sum(...new Set([1, 2, 3, 4])));
+  assertEquals(10, O.sum(...(function*() {
+    yield 1;
+    yield 2;
+    yield 3;
+    yield 4;
+  })()));
+
+  // nested spreads
+  assertEquals(10, O.sum(...O.makeArray(...[1, 2, 3, 4])));
+  assertEquals("test!!!", O.sum(...O.makeArray(..."test!!!")));
+
+  // Interleaved spread/unspread args
+  assertEquals(36, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
+  assertEquals(45, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
+})();
+
+
+(function testSpreadEvaluationOrder() {
+  "use strict";
+  var log = "";
+  function* gen() { log += "X"; yield 0; log += "Y"; }
+  function a() { log += "A"; }
+  function b() { log += "B"; return gen(); }
+  function* c() { log += 'C1'; yield 1; log += 'C2'; }
+  function d() { log += "D"; }
+  function e() { log += "E"; }
+  function fn() {
+    var args = [];
+    for (var i = 0; i < arguments.length; ++i) args.push(arguments[i]);
+    return args;
+  }
+
+  var result = fn(a(), ...b(), d());
+  assertEquals([undefined, 0, undefined], result);
+  assertEquals("ABXYD", log);
+
+  log = "";
+  result = fn(...b(), d());
+  assertEquals([0, undefined], result);
+  assertEquals("BXYD", log);
+
+  log = "";
+  result = fn(a(), ...b());
+  assertEquals([undefined, 0], result);
+  assertEquals("ABXY", log);
+
+  log = "";
+  result = fn(a(), ...b(), ...c(), d(), e());
+  assertEquals([undefined, 0, 1, undefined, undefined], result);
+  assertEquals("ABXYC1C2DE", log);
+
+  log = "";
+  result = fn(a(), ...b(), ...c(), d(), e(), ...b(), ...c());
+  assertEquals([undefined, 0, 1, undefined, undefined, 0, 1], result);
+  assertEquals("ABXYC1C2DEBXYC1C2", log);
+})();
+
+
+(function testCustomArrayPrototypeIterator() {
+  var origIterator =
+      Object.getOwnPropertyDescriptor(Array.prototype, Symbol.iterator);
+  Object.defineProperty(Array.prototype, Symbol.iterator, {
+    value: function*() {
+      yield 1;
+      yield 2;
+      yield 3;
+    },
+    configurable: true
+  });
+  function returnCountStrict() { 'use strict'; return arguments.length; }
+  function returnCountSloppy() { return arguments.length; }
+
+  assertEquals(3, returnCountStrict(...[1]));
+  assertEquals(4, returnCountStrict(1, ...[2]));
+  assertEquals(5, returnCountStrict(1, ...[2], 3));
+  assertEquals(3, returnCountSloppy(...[1]));
+  assertEquals(4, returnCountSloppy(1, ...[2]));
+  assertEquals(5, returnCountSloppy(1, ...[2], 3));
+
+  Object.defineProperty(Array.prototype, Symbol.iterator, origIterator);
+})();
index 853d99ac97b9f1a2a6ef0edbaab96eb40901e738..31890fa713137e216dfb61edfb7c84bea259d7fe 100644 (file)
           '../../src/harmony-tostring.js',
           '../../src/harmony-typedarray.js',
           '../../src/harmony-regexp.js',
-          '../../src/harmony-reflect.js'
+          '../../src/harmony-reflect.js',
+          '../../src/harmony-spread.js'
         ],
         'libraries_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries.bin',
         'libraries_experimental_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries-experimental.bin',