"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 = [
}
+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
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);
}
}
+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));
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));
}
+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
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);
}
}
+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);
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));
}
+void AstNumberingVisitor::VisitSpread(Spread* node) { UNREACHABLE(); }
+
+
void AstNumberingVisitor::VisitForInStatement(ForInStatement* node) {
IncrementNodeCount();
DisableSelfOptimization();
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") \
V(CountOperation) \
V(BinaryOperation) \
V(CompareOperation) \
+ V(Spread) \
V(ThisFunction) \
V(SuperReference) \
V(CaseClause)
};
+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)
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,
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() {
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());
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>(),
construct->shared()->set_length(2);
}
+ if (!FLAG_harmony_reflect) return;
Handle<JSGlobalObject> global(JSGlobalObject::cast(
native_context()->global_object()));
Handle<String> reflect_string =
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++) {
}
+void AstGraphBuilder::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
void AstGraphBuilder::VisitThisFunction(ThisFunction* expr) {
Node* value = GetFunctionClosure();
ast_context()->ProduceValue(value);
}
+void ALAA::VisitSpread(Spread* e) { Visit(e->expression()); }
+
+
void ALAA::VisitCaseClause(CaseClause* cc) {
if (!cc->is_default()) Visit(cc->label());
VisitStatements(cc->statements());
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) \
}
+void BreakableStatementChecker::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
void BreakableStatementChecker::VisitThisFunction(ThisFunction* expr) {
}
}
+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.
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)
// 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)
void EmitSetHomeObjectIfNeeded(Expression* initializer, int offset);
void EmitLoadSuperConstructor();
+ void EmitInitializeThisAfterSuper(SuperReference* super_ref);
void CallIC(Handle<Code> code,
TypeFeedbackId id = TypeFeedbackId::None());
--- /dev/null
+// 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;
+}
}
+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.
}
+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
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);
}
}
+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));
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));
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) {
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(
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
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
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_;
};
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
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_
}
// 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();
}
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
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_; }
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:
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);
bool allow_harmony_sloppy_;
bool allow_harmony_computed_property_names_;
bool allow_harmony_rest_params_;
+ bool allow_harmony_spreadcalls_;
bool allow_strong_mode_;
};
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_));
ExpressionTypeField::decode(code_) == kNoTemplateTagExpression;
}
+ bool IsSpreadExpression() const {
+ return TypeField::decode(code_) == kSpreadExpression;
+ }
+
PreParserExpression AsFunctionLiteral() { return *this; }
bool IsBinaryOperation() const {
kExpression,
kIdentifierExpression,
kStringLiteralExpression,
- kBinaryOperationExpression
+ kBinaryOperationExpression,
+ kSpreadExpression
};
enum Parenthesization {
: (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
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();
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; }
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_;
};
}
+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) {
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;
*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;
}
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.
// 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;
}
}
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;
}
+void CallPrinter::VisitSpread(Spread* node) {
+ Print("(...");
+ Find(node->expression(), true);
+ Print(")");
+}
+
+
void CallPrinter::VisitThisFunction(ThisFunction* node) {}
}
+void PrettyPrinter::VisitSpread(Spread* node) {
+ Print("(...");
+ Visit(node->expression());
+ Print(")");
+}
+
+
void PrettyPrinter::VisitThisFunction(ThisFunction* node) {
Print("<this-function>");
}
}
+void AstPrinter::VisitSpread(Spread* node) {
+ IndentedScope indent(this, "...");
+ Visit(node->expression());
+}
+
+
void AstPrinter::VisitThisFunction(ThisFunction* node) {
IndentedScope indent(this, "THIS-FUNCTION");
}
UNIMPLEMENTED();
return nullptr;
}
+
+
+RUNTIME_FUNCTION(Runtime_CallSuperWithSpread) {
+ UNIMPLEMENTED();
+ return nullptr;
+}
}
} // namespace v8::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) \
}
+void AstTyper::VisitSpread(Spread* expr) { UNREACHABLE(); }
+
+
void AstTyper::VisitThisFunction(ThisFunction* expr) {
}
}
+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
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);
}
}
+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));
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));
kAllowHarmonySloppy,
kAllowHarmonyUnicode,
kAllowHarmonyComputedPropertyNames,
- kAllowStrongMode
+ kAllowStrongMode,
+ kAllowHarmonySpreadCalls
};
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(
}
+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] = {
{"", ""},
"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"],
--- /dev/null
+// 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 });
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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);
+})();
--- /dev/null
+// 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);
+})();
--- /dev/null
+// 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);
+})();
'../../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',