}
+void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode) {
+ // The value stays in r0, and is ultimately read by the resumed generator, as
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. r1
+ // will hold the generator object until the activation has been resumed.
+ VisitForStackValue(generator);
+ VisitForAccumulatorValue(value);
+ __ pop(r1);
+
+ // Check generator state.
+ Label wrong_state, done;
+ __ ldr(r3, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ __ cmp(r3, Operand(Smi::FromInt(0)));
+ __ b(le, &wrong_state);
+
+ // Load suspended function and context.
+ __ ldr(cp, FieldMemOperand(r1, JSGeneratorObject::kContextOffset));
+ __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset));
+
+ // Push holes for arguments to generator function.
+ __ ldr(r3, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r3,
+ FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ Move(r2, isolate()->factory()->the_hole_value());
+ Label push_argument_holes;
+ __ bind(&push_argument_holes);
+ __ push(r2);
+ __ sub(r3, r3, Operand(1));
+ __ b(vc, &push_argument_holes);
+
+ // Enter a new JavaScript frame, and initialize its slots as they were when
+ // the generator was suspended.
+ Label push_frame, resume_frame;
+ __ bind(&push_frame);
+ __ bl(&resume_frame);
+ __ jmp(&done);
+ __ bind(&resume_frame);
+ __ push(fp); // Caller's frame pointer.
+ __ mov(fp, sp);
+ __ push(cp); // Callee's context.
+ __ push(r4); // Callee's JS Function.
+
+ // Load the operand stack size.
+ __ ldr(r3, FieldMemOperand(r1, JSGeneratorObject::kOperandStackOffset));
+ __ ldr(r3, FieldMemOperand(r3, FixedArray::kLengthOffset));
+ __ SmiUntag(r3);
+
+ // If we are sending a value and there is no operand stack, we can jump back
+ // in directly.
+ if (resume_mode == JSGeneratorObject::SEND) {
+ Label slow_resume;
+ __ cmp(r3, Operand(0));
+ __ b(ne, &slow_resume);
+ __ ldr(r3, FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
+ __ ldr(r2, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
+ __ SmiUntag(r2);
+ __ add(r3, r3, r2);
+ __ mov(r2, Operand(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting)));
+ __ str(r2, FieldMemOperand(r1, JSGeneratorObject::kContinuationOffset));
+ __ Jump(r3);
+ __ bind(&slow_resume);
+ }
+
+ // Otherwise, we push holes for the operand stack and call the runtime to fix
+ // up the stack and the handlers.
+ Label push_operand_holes, call_resume;
+ __ bind(&push_operand_holes);
+ __ sub(r3, r3, Operand(1));
+ __ b(vs, &call_resume);
+ __ push(r2);
+ __ b(&push_operand_holes);
+ __ bind(&call_resume);
+ __ push(r1);
+ __ push(result_register());
+ __ Push(Smi::FromInt(resume_mode));
+ __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3);
+ // Not reached: the runtime call returns elsewhere.
+ __ stop("not-reached");
+
+ // Throw error if we attempt to operate on a running generator.
+ __ bind(&wrong_state);
+ __ push(r1);
+ __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
+
+ __ bind(&done);
+ context()->Plug(result_register());
+}
+
+
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
}
+void FullCodeGenerator::EmitGeneratorSend(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ EmitGeneratorResume(args->at(0), args->at(1), JSGeneratorObject::SEND);
+}
+
+
+void FullCodeGenerator::EmitGeneratorThrow(CallRuntime* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ ASSERT(args->length() == 2);
+ EmitGeneratorResume(args->at(0), args->at(1), JSGeneratorObject::THROW);
+}
+
+
void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
switch (expr->op()) {
case Token::COMMA:
INLINE_RUNTIME_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL)
#undef EMIT_INLINE_RUNTIME_CALL
+ // Platform-specific code for resuming generators.
+ void EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode);
+
// Platform-specific code for loading variables.
void EmitLoadGlobalCheckExtensions(Variable* var,
TypeofState typeof_state,
['[Generator].prototype.next', this]);
}
- // TODO(wingo): Implement.
+ return %_GeneratorSend(this, void 0);
}
function GeneratorObjectSend(value) {
['[Generator].prototype.send', this]);
}
- // TODO(wingo): Implement.
+ return %_GeneratorSend(this, value);
}
function GeneratorObjectThrow(exn) {
['[Generator].prototype.throw', this]);
}
- // TODO(wingo): Implement.
-}
-
-function GeneratorObjectClose() {
- if (!IS_GENERATOR(this)) {
- throw MakeTypeError('incompatible_method_receiver',
- ['[Generator].prototype.close', this]);
- }
-
- // TODO(wingo): Implement.
+ return %_GeneratorThrow(this, exn);
}
function SetUpGenerators() {
DONT_ENUM | DONT_DELETE | READ_ONLY,
["next", GeneratorObjectNext,
"send", GeneratorObjectSend,
- "throw", GeneratorObjectThrow,
- "close", GeneratorObjectClose]);
+ "throw", GeneratorObjectThrow]);
%SetProperty(GeneratorObjectPrototype, "constructor",
GeneratorFunctionPrototype, DONT_ENUM | DONT_DELETE | READ_ONLY);
%SetPrototype(GeneratorFunctionPrototype, $Function.prototype);
}
+// Support for generators.
+void HOptimizedGraphBuilder::GenerateGeneratorSend(CallRuntime* call) {
+ return Bailout("inlined runtime function: GeneratorSend");
+}
+
+
+void HOptimizedGraphBuilder::GenerateGeneratorThrow(CallRuntime* call) {
+ return Bailout("inlined runtime function: GeneratorThrow");
+}
+
+
#undef CHECK_BAILOUT
#undef CHECK_ALIVE
}
+void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode) {
+ // The value stays in eax, and is ultimately read by the resumed generator, as
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. ebx
+ // will hold the generator object until the activation has been resumed.
+ VisitForStackValue(generator);
+ VisitForAccumulatorValue(value);
+ __ pop(ebx);
+
+ // Check generator state.
+ Label wrong_state, done;
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ __ cmp(FieldOperand(ebx, JSGeneratorObject::kContinuationOffset),
+ Immediate(Smi::FromInt(0)));
+ __ j(less_equal, &wrong_state);
+
+ // Load suspended function and context.
+ __ mov(esi, FieldOperand(ebx, JSGeneratorObject::kContextOffset));
+ __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset));
+
+ // Push holes for arguments to generator function.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(edx,
+ FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ mov(ecx, isolate()->factory()->the_hole_value());
+ Label push_argument_holes;
+ __ bind(&push_argument_holes);
+ __ push(ecx);
+ __ sub(edx, Immediate(1));
+ __ j(not_carry, &push_argument_holes);
+
+ // Enter a new JavaScript frame, and initialize its slots as they were when
+ // the generator was suspended.
+ Label push_frame, resume_frame;
+ __ bind(&push_frame);
+ __ call(&resume_frame);
+ __ jmp(&done);
+ __ bind(&resume_frame);
+ __ push(ebp); // Caller's frame pointer.
+ __ mov(ebp, esp);
+ __ push(esi); // Callee's context.
+ __ push(edi); // Callee's JS Function.
+
+ // Load the operand stack size.
+ __ mov(edx, FieldOperand(ebx, JSGeneratorObject::kOperandStackOffset));
+ __ mov(edx, FieldOperand(edx, FixedArray::kLengthOffset));
+ __ SmiUntag(edx);
+
+ // If we are sending a value and there is no operand stack, we can jump back
+ // in directly.
+ if (resume_mode == JSGeneratorObject::SEND) {
+ Label slow_resume;
+ __ cmp(edx, Immediate(0));
+ __ j(not_zero, &slow_resume);
+ __ mov(edx, FieldOperand(edi, JSFunction::kCodeEntryOffset));
+ __ mov(ecx, FieldOperand(ebx, JSGeneratorObject::kContinuationOffset));
+ __ SmiUntag(ecx);
+ __ add(edx, ecx);
+ __ mov(FieldOperand(ebx, JSGeneratorObject::kContinuationOffset),
+ Immediate(Smi::FromInt(JSGeneratorObject::kGeneratorExecuting)));
+ __ jmp(edx);
+ __ bind(&slow_resume);
+ }
+
+ // Otherwise, we push holes for the operand stack and call the runtime to fix
+ // up the stack and the handlers.
+ Label push_operand_holes, call_resume;
+ __ bind(&push_operand_holes);
+ __ sub(edx, Immediate(1));
+ __ j(carry, &call_resume);
+ __ push(ecx);
+ __ jmp(&push_operand_holes);
+ __ bind(&call_resume);
+ __ push(ebx);
+ __ push(result_register());
+ __ Push(Smi::FromInt(resume_mode));
+ __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3);
+ // Not reached: the runtime call returns elsewhere.
+ __ Abort("Generator failed to resume.");
+
+ // Throw error if we attempt to operate on a running generator.
+ __ bind(&wrong_state);
+ __ push(ebx);
+ __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
+
+ __ bind(&done);
+ context()->Plug(result_register());
+}
+
+
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
// Error
cyclic_proto: ["Cyclic __proto__ value"],
code_gen_from_strings: ["%0"],
+ generator_running: ["Generator is already running"],
+ generator_finished: ["Generator has already finished"],
// TypeError
unexpected_token: ["Unexpected token ", "%0"],
unexpected_token_number: ["Unexpected number"],
symbol_to_string: ["Conversion from symbol to string"],
invalid_module_path: ["Module does not export '", "%0", "', or export is not itself a module"],
module_type_error: ["Module '", "%0", "' used improperly"],
- module_export_undefined: ["Export '", "%0", "' is not defined in module"],
+ module_export_undefined: ["Export '", "%0", "' is not defined in module"]
};
static const int kOperandStackOffset = kContinuationOffset + kPointerSize;
static const int kSize = kOperandStackOffset + kPointerSize;
+ // Resume mode, for use by runtime functions.
+ enum ResumeMode { SEND, THROW };
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSGeneratorObject);
};
}
+// Note that this function is the slow path for resuming generators. It is only
+// called if the suspended activation had operands on the stack, stack handlers
+// needing rewinding, or if the resume should throw an exception. The fast path
+// is handled directly in FullCodeGenerator::EmitGeneratorResume(), which is
+// inlined into GeneratorNext, GeneratorSend, and GeneratorThrow.
+// EmitGeneratorResumeResume is called in any case, as it needs to reconstruct
+// the stack frame and make space for arguments and operands.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ResumeJSGeneratorObject) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator_object, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
+ CONVERT_SMI_ARG_CHECKED(resume_mode_int, 2);
+ JavaScriptFrameIterator stack_iterator(isolate);
+ JavaScriptFrame *frame = stack_iterator.frame();
+
+ ASSERT_EQ(frame->function(), generator_object->function());
+
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+
+ Address pc = generator_object->function()->code()->instruction_start();
+ int offset = generator_object->continuation();
+ ASSERT(offset > 0);
+ frame->set_pc(pc + offset);
+ generator_object->set_continuation(JSGeneratorObject::kGeneratorExecuting);
+
+ if (generator_object->operand_stack()->length() != 0) {
+ // TODO(wingo): Copy operand stack. Rewind handlers.
+ UNIMPLEMENTED();
+ }
+
+ JSGeneratorObject::ResumeMode resume_mode =
+ static_cast<JSGeneratorObject::ResumeMode>(resume_mode_int);
+ switch (resume_mode) {
+ case JSGeneratorObject::SEND:
+ return *value;
+ case JSGeneratorObject::THROW:
+ return isolate->Throw(*value);
+ }
+
+ UNREACHABLE();
+ return isolate->ThrowIllegalOperation();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ThrowGeneratorStateError) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSGeneratorObject, generator, 0);
+ int continuation = generator->continuation();
+ const char *message = continuation == JSGeneratorObject::kGeneratorClosed ?
+ "generator_finished" : "generator_running";
+ Vector< Handle<Object> > argv = HandleVector<Object>(NULL, 0);
+ Handle<Object> error = isolate->factory()->NewError(message, argv);
+ return isolate->Throw(*error);
+}
+
+
MUST_USE_RESULT static MaybeObject* CharFromCode(Isolate* isolate,
Object* char_code) {
if (char_code->IsNumber()) {
/* Harmony generators */ \
F(CreateJSGeneratorObject, 0, 1) \
F(SuspendJSGeneratorObject, 1, 1) \
+ F(ResumeJSGeneratorObject, 3, 1) \
+ F(ThrowGeneratorStateError, 1, 1) \
\
/* Harmony modules */ \
F(IsJSModule, 1, 1) \
F(IsRegExpEquivalent, 2, 1) \
F(HasCachedArrayIndex, 1, 1) \
F(GetCachedArrayIndex, 1, 1) \
- F(FastAsciiArrayJoin, 2, 1)
+ F(FastAsciiArrayJoin, 2, 1) \
+ F(GeneratorSend, 2, 1) \
+ F(GeneratorThrow, 2, 1)
// ----------------------------------------------------------------------------
}
+void FullCodeGenerator::EmitGeneratorResume(Expression *generator,
+ Expression *value,
+ JSGeneratorObject::ResumeMode resume_mode) {
+ // The value stays in rax, and is ultimately read by the resumed generator, as
+ // if the CallRuntime(Runtime::kSuspendJSGeneratorObject) returned it. rbx
+ // will hold the generator object until the activation has been resumed.
+ VisitForStackValue(generator);
+ VisitForAccumulatorValue(value);
+ __ pop(rbx);
+
+ // Check generator state.
+ Label wrong_state, done;
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorExecuting <= 0);
+ STATIC_ASSERT(JSGeneratorObject::kGeneratorClosed <= 0);
+ __ SmiCompare(FieldOperand(rbx, JSGeneratorObject::kContinuationOffset),
+ Smi::FromInt(0));
+ __ j(less_equal, &wrong_state);
+
+ // Load suspended function and context.
+ __ movq(rsi, FieldOperand(rbx, JSGeneratorObject::kContextOffset));
+ __ movq(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset));
+
+ // Push holes for arguments to generator function.
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movsxlq(rdx,
+ FieldOperand(rdx,
+ SharedFunctionInfo::kFormalParameterCountOffset));
+ __ LoadRoot(rcx, Heap::kTheHoleValueRootIndex);
+ Label push_argument_holes;
+ __ bind(&push_argument_holes);
+ __ push(rcx);
+ __ subq(rdx, Immediate(1));
+ __ j(not_carry, &push_argument_holes);
+
+ // Enter a new JavaScript frame, and initialize its slots as they were when
+ // the generator was suspended.
+ Label push_frame, resume_frame;
+ __ bind(&push_frame);
+ __ call(&resume_frame);
+ __ jmp(&done);
+ __ bind(&resume_frame);
+ __ push(rbp); // Caller's frame pointer.
+ __ movq(rbp, rsp);
+ __ push(rsi); // Callee's context.
+ __ push(rdi); // Callee's JS Function.
+
+ // Load the operand stack size.
+ __ movq(rdx, FieldOperand(rbx, JSGeneratorObject::kOperandStackOffset));
+ __ movq(rdx, FieldOperand(rdx, FixedArray::kLengthOffset));
+ __ SmiToInteger32(rdx, rdx);
+
+ // If we are sending a value and there is no operand stack, we can jump back
+ // in directly.
+ if (resume_mode == JSGeneratorObject::SEND) {
+ Label slow_resume;
+ __ cmpq(rdx, Immediate(0));
+ __ j(not_zero, &slow_resume);
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
+ __ SmiToInteger64(rcx,
+ FieldOperand(rbx, JSGeneratorObject::kContinuationOffset));
+ __ addq(rdx, rcx);
+ __ Move(FieldOperand(rbx, JSGeneratorObject::kContinuationOffset),
+ Smi::FromInt(JSGeneratorObject::kGeneratorExecuting));
+ __ jmp(rdx);
+ __ bind(&slow_resume);
+ }
+
+ // Otherwise, we push holes for the operand stack and call the runtime to fix
+ // up the stack and the handlers.
+ Label push_operand_holes, call_resume;
+ __ bind(&push_operand_holes);
+ __ subq(rdx, Immediate(1));
+ __ j(carry, &call_resume);
+ __ push(rcx);
+ __ jmp(&push_operand_holes);
+ __ bind(&call_resume);
+ __ push(rbx);
+ __ push(result_register());
+ __ Push(Smi::FromInt(resume_mode));
+ __ CallRuntime(Runtime::kResumeJSGeneratorObject, 3);
+ // Not reached: the runtime call returns elsewhere.
+ __ Abort("Generator failed to resume.");
+
+ // Throw error if we attempt to operate on a running generator.
+ __ bind(&wrong_state);
+ __ push(rbx);
+ __ CallRuntime(Runtime::kThrowGeneratorStateError, 1);
+
+ __ bind(&done);
+ context()->Plug(result_register());
+}
+
+
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
"_GetCachedArrayIndex": true,
"_OneByteSeqStringSetChar": true,
"_TwoByteSeqStringSetChar": true,
+
+ // Only applicable to generators.
+ "_GeneratorSend": true,
+ "_GeneratorThrow": true
};
var currentlyUncallable = {
--- /dev/null
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-generators --expose-gc
+
+// Test generator iteration.
+
+var GeneratorFunction = (function*(){yield 1;}).__proto__.constructor;
+
+function TestGenerator(g, expected_values_for_next,
+ send_val, expected_values_for_send) {
+ function testNext(thunk) {
+ var iter = thunk();
+ for (var i = 0; i < expected_values_for_next.length; i++) {
+ assertEquals(expected_values_for_next[i], iter.next());
+ }
+ assertThrows(function() { iter.next(); }, Error);
+ }
+ function testSend(thunk) {
+ var iter = thunk();
+ for (var i = 0; i < expected_values_for_send.length; i++) {
+ assertEquals(expected_values_for_send[i], iter.send(send_val));
+ }
+ assertThrows(function() { iter.send(send_val); }, Error);
+ }
+ function testThrow(thunk) {
+ for (var i = 0; i < expected_values_for_next.length; i++) {
+ var iter = thunk();
+ for (var j = 0; j < i; j++) {
+ assertEquals(expected_values_for_next[j], iter.next());
+ }
+ function Sentinel() {}
+ assertThrows(function () { iter.throw(new Sentinel); }, Sentinel);
+ assertThrows(function () { iter.next(); }, Error);
+ }
+ }
+
+ testNext(g);
+ testSend(g);
+ testThrow(g);
+
+ if (g instanceof GeneratorFunction) {
+ testNext(function() { return new g(); });
+ testSend(function() { return new g(); });
+ testThrow(function() { return new g(); });
+ }
+}
+
+TestGenerator(function* g1() { },
+ [undefined],
+ "foo",
+ [undefined]);
+
+TestGenerator(function* g2() { yield 1; },
+ [1, undefined],
+ "foo",
+ [1, undefined]);
+
+TestGenerator(function* g3() { yield 1; yield 2; },
+ [1, 2, undefined],
+ "foo",
+ [1, 2, undefined]);
+
+TestGenerator(function* g4() { yield 1; yield 2; return 3; },
+ [1, 2, 3],
+ "foo",
+ [1, 2, 3]);
+
+TestGenerator(function* g5() { return 1; },
+ [1],
+ "foo",
+ [1]);
+
+TestGenerator(function* g6() { var x = yield 1; return x; },
+ [1, undefined],
+ "foo",
+ [1, "foo"]);
+
+TestGenerator(function* g7() { var x = yield 1; yield 2; return x; },
+ [1, 2, undefined],
+ "foo",
+ [1, 2, "foo"]);
+
+TestGenerator(function* g8() { for (var x = 0; x < 4; x++) { yield x; } },
+ [0, 1, 2, 3, undefined],
+ "foo",
+ [0, 1, 2, 3, undefined]);
+
+// Generator with arguments.
+TestGenerator(
+ function g9() {
+ return (function*(a, b, c, d) {
+ yield a; yield b; yield c; yield d;
+ })("fee", "fi", "fo", "fum");
+ },
+ ["fee", "fi", "fo", "fum", undefined],
+ "foo",
+ ["fee", "fi", "fo", "fum", undefined]);
+
+// Too few arguments.
+TestGenerator(
+ function g10() {
+ return (function*(a, b, c, d) {
+ yield a; yield b; yield c; yield d;
+ })("fee", "fi");
+ },
+ ["fee", "fi", undefined, undefined, undefined],
+ "foo",
+ ["fee", "fi", undefined, undefined, undefined]);
+
+// Too many arguments.
+TestGenerator(
+ function g11() {
+ return (function*(a, b, c, d) {
+ yield a; yield b; yield c; yield d;
+ })("fee", "fi", "fo", "fum", "I smell the blood of an Englishman");
+ },
+ ["fee", "fi", "fo", "fum", undefined],
+ "foo",
+ ["fee", "fi", "fo", "fum", undefined]);
+
+// The arguments object.
+TestGenerator(
+ function g12() {
+ return (function*(a, b, c, d) {
+ for (var i = 0; i < arguments.length; i++) {
+ yield arguments[i];
+ }
+ })("fee", "fi", "fo", "fum", "I smell the blood of an Englishman");
+ },
+ ["fee", "fi", "fo", "fum", "I smell the blood of an Englishman",
+ undefined],
+ "foo",
+ ["fee", "fi", "fo", "fum", "I smell the blood of an Englishman",
+ undefined]);
+
+// Access to captured free variables.
+TestGenerator(
+ function g13() {
+ return (function(a, b, c, d) {
+ return (function*() {
+ yield a; yield b; yield c; yield d;
+ })();
+ })("fee", "fi", "fo", "fum");
+ },
+ ["fee", "fi", "fo", "fum", undefined],
+ "foo",
+ ["fee", "fi", "fo", "fum", undefined]);
+
+// Abusing the arguments object.
+TestGenerator(
+ function g14() {
+ return (function*(a, b, c, d) {
+ arguments[0] = "Be he live";
+ arguments[1] = "or be he dead";
+ arguments[2] = "I'll grind his bones";
+ arguments[3] = "to make my bread";
+ yield a; yield b; yield c; yield d;
+ })("fee", "fi", "fo", "fum");
+ },
+ ["Be he live", "or be he dead", "I'll grind his bones", "to make my bread",
+ undefined],
+ "foo",
+ ["Be he live", "or be he dead", "I'll grind his bones", "to make my bread",
+ undefined]);
+
+// Abusing the arguments object: strict mode.
+TestGenerator(
+ function g15() {
+ return (function*(a, b, c, d) {
+ "use strict";
+ arguments[0] = "Be he live";
+ arguments[1] = "or be he dead";
+ arguments[2] = "I'll grind his bones";
+ arguments[3] = "to make my bread";
+ yield a; yield b; yield c; yield d;
+ })("fee", "fi", "fo", "fum");
+ },
+ ["fee", "fi", "fo", "fum", undefined],
+ "foo",
+ ["fee", "fi", "fo", "fum", undefined]);
+
+// GC.
+TestGenerator(function* g16() { yield "baz"; gc(); yield "qux"; },
+ ["baz", "qux", undefined],
+ "foo",
+ ["baz", "qux", undefined]);
+
+// Receivers.
+function TestReceivers() {
+ TestGenerator(
+ function g17() {
+ function* g() { yield this.x; yield this.y; }
+ var o = { start: g, x: 1, y: 2 };
+ return o.start();
+ },
+ [1, 2, undefined],
+ "foo",
+ [1, 2, undefined]);
+
+ TestGenerator(
+ function g18() {
+ function* g() { yield this.x; yield this.y; }
+ var iter = new g;
+ iter.x = 1;
+ iter.y = 2;
+ return iter;
+ },
+ [1, 2, undefined],
+ "foo",
+ [1, 2, undefined]);
+}
+// TODO(wingo): Enable this test. Currently accessing "this" doesn't work as
+// prior to generators, nothing needed to heap-allocate the receiver.
+// TestReceivers();
+
+function TestRecursion() {
+ function TestNextRecursion() {
+ function* g() { yield iter.next(); }
+ var iter = g();
+ return iter.next();
+ }
+ function TestSendRecursion() {
+ function* g() { yield iter.send(42); }
+ var iter = g();
+ return iter.next();
+ }
+ function TestThrowRecursion() {
+ function* g() { yield iter.throw(1); }
+ var iter = g();
+ return iter.next();
+ }
+ assertThrows(TestNextRecursion, Error);
+ assertThrows(TestSendRecursion, Error);
+ assertThrows(TestThrowRecursion, Error);
+}
+TestRecursion();
assertSame(GeneratorObjectPrototype,
Object.getPrototypeOf((function*(){yield 1}).prototype));
- var expected_property_names = ["next", "send", "throw", "close",
- "constructor"];
+ var expected_property_names = ["next", "send", "throw", "constructor"];
var found_property_names =
Object.getOwnPropertyNames(GeneratorObjectPrototype);
# TODO(wingo): Currently fails in no-snapshot mode, hence disabled for now.
harmony/generators-objects: SKIP
+# TODO(wingo): Resuming of iterators currently crashes in ARM.
+harmony/generators-iteration: SKIP if ($arch == arm || $arch == android_arm)
+
# Issue 1719: Slow to collect arrays over several contexts.
regress/regress-524: SKIP
# When that bug is fixed, revert the expectation to: