Implement the generator functions (#270)

Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
Robert Fancsik 2019-06-09 22:55:14 -04:00 committed by yichoi
commit 9166b2de38
23 changed files with 1016 additions and 79 deletions

View file

@ -119,6 +119,9 @@ class Node;
F(CallEvalFunction, 0, 0) \
F(CallFunctionInWithScope, 0, 0) \
F(BindingRestElement, 1, 0) \
F(GeneratorComplete, 0, 0) \
F(Yield, 0, 0) \
F(YieldDelegate, 1, 0) \
F(End, 0, 0)
enum Opcode {
@ -1470,6 +1473,68 @@ public:
#endif
};
class GeneratorComplete : public ByteCode {
public:
GeneratorComplete(const ByteCodeLOC& loc, bool isThrow = false)
: ByteCode(Opcode::GeneratorCompleteOpcode, loc)
, m_isThrow(isThrow)
{
}
bool m_isThrow;
#ifndef NDEBUG
void dump(const char* byteCodeStart)
{
printf("generator complete");
}
#endif
};
class Yield : public ByteCode {
public:
Yield(const ByteCodeLOC& loc, const size_t yieldIdx, const size_t dstIdx)
: ByteCode(Opcode::YieldOpcode, loc)
, m_yieldIdx(yieldIdx)
, m_dstIdx(dstIdx)
{
}
ByteCodeRegisterIndex m_yieldIdx;
ByteCodeRegisterIndex m_dstIdx;
#ifndef NDEBUG
void dump(const char* byteCodeStart)
{
printf("r%d <- yield r%d", m_dstIdx, m_yieldIdx);
}
#endif
};
class YieldDelegate : public ByteCode {
public:
YieldDelegate(const ByteCodeLOC& loc, const size_t iterIdx, const size_t valueIdx, const size_t dstIdx)
: ByteCode(Opcode::YieldDelegateOpcode, loc)
, m_iterIdx(iterIdx)
, m_valueIdx(valueIdx)
, m_dstIdx(dstIdx)
, m_endPosition(SIZE_MAX)
{
}
ByteCodeRegisterIndex m_iterIdx;
ByteCodeRegisterIndex m_valueIdx;
ByteCodeRegisterIndex m_dstIdx;
size_t m_endPosition;
#ifndef NDEBUG
void dump(const char* byteCodeStart)
{
printf("r%d r%d <- yield* r%d", m_valueIdx, m_dstIdx, m_iterIdx);
}
#endif
};
class NewOperation : public ByteCode {
public:
NewOperation(const ByteCodeLOC& loc, const size_t calleeIndex, const size_t argumentsStartIndex, const size_t argumentCount, const size_t resultIndex)
@ -1770,6 +1835,7 @@ public:
, m_forOfEndPosition(SIZE_MAX)
{
}
ByteCodeRegisterIndex m_registerIndex;
ByteCodeRegisterIndex m_iterRegisterIndex;
size_t m_forOfEndPosition;

View file

@ -210,6 +210,9 @@ ByteCodeBlock* ByteCodeGenerator::generateByteCode(Context* c, InterpretedCodeBl
block->pushCode(ReturnFunctionWithValue(ByteCodeLOC(SIZE_MAX), idx), &ctx, nullptr);
ctx.giveUpRegister();
} else {
if (codeBlock->isGenerator() == true) {
block->pushCode(GeneratorComplete(ByteCodeLOC(SIZE_MAX)), &ctx, nullptr);
}
block->pushCode(ReturnFunction(ByteCodeLOC(SIZE_MAX)), &ctx, nullptr);
}
}
@ -592,6 +595,19 @@ ByteCodeBlock* ByteCodeGenerator::generateByteCode(Context* c, InterpretedCodeBl
assignStackIndexIfNeeded(cd->m_dstIndex, stackBase, stackBaseWillBe, stackVariableSize);
break;
}
case YieldOpcode: {
Yield* cd = (Yield*)currentCode;
assignStackIndexIfNeeded(cd->m_yieldIdx, stackBase, stackBaseWillBe, stackVariableSize);
assignStackIndexIfNeeded(cd->m_dstIdx, stackBase, stackBaseWillBe, stackVariableSize);
break;
}
case YieldDelegateOpcode: {
YieldDelegate* cd = (YieldDelegate*)currentCode;
assignStackIndexIfNeeded(cd->m_iterIdx, stackBase, stackBaseWillBe, stackVariableSize);
assignStackIndexIfNeeded(cd->m_valueIdx, stackBase, stackBaseWillBe, stackVariableSize);
assignStackIndexIfNeeded(cd->m_dstIdx, stackBase, stackBaseWillBe, stackVariableSize);
break;
}
default:
break;
}

View file

@ -32,6 +32,7 @@
#include "runtime/ArrayObject.h"
#include "runtime/VMInstance.h"
#include "runtime/IteratorOperations.h"
#include "runtime/GeneratorObject.h"
#include "runtime/SpreadObject.h"
#include "parser/ScriptParser.h"
#include "util/Util.h"
@ -1309,6 +1310,40 @@ Value ByteCodeInterpreter::interpret(ExecutionState& state, ByteCodeBlock* byteC
NEXT_INSTRUCTION();
}
DEFINE_OPCODE(GeneratorComplete)
:
{
GeneratorComplete* code = (GeneratorComplete*)programCounter;
ec->generatorTarget()->asGeneratorObject()->setState((GeneratorState)(GeneratorState::CompletedReturn + !!code->m_isThrow));
ADD_PROGRAM_COUNTER(GeneratorComplete);
NEXT_INSTRUCTION();
}
DEFINE_OPCODE(Yield)
:
{
Yield* code = (Yield*)programCounter;
GeneratorObject* gen = ec->generatorTarget()->asGeneratorObject();
gen->setState(GeneratorState::SuspendedYield);
ADD_PROGRAM_COUNTER(Yield);
gen->setByteCodePosition((char*)programCounter - codeBuffer);
gen->setResumeValueIdx(code->m_dstIdx);
return code->m_yieldIdx == REGISTER_LIMIT ? Value() : registerFile[code->m_yieldIdx];
}
DEFINE_OPCODE(YieldDelegate)
:
{
Value result = yieldDelegateOperation(state, registerFile, programCounter, codeBuffer);
if (result.isEmpty() == true) {
NEXT_INSTRUCTION();
}
return result;
}
DEFINE_OPCODE(End)
:
{
@ -2240,7 +2275,10 @@ NEVER_INLINE void ByteCodeInterpreter::classOperation(ExecutionState& state, Cre
} else if (!superClass.isObject() || !superClass.asObject()->isFunctionObject() || !superClass.asObject()->asFunctionObject()->isConstructor()) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, errorMessage_Class_Extends_Value_Is_Not_Object_Nor_Null);
} else {
// TODO: throw TypeError superClass is when generator function
if (superClass.isObject() == true && superClass.asObject()->isGeneratorObject() == true) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, errorMessage_Class_Prototype_Is_Not_Object_Nor_Null);
}
protoParent = superClass.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().prototype)).value(state, Value());
if (!protoParent.isObject() && !protoParent.isNull()) {
@ -2410,6 +2448,88 @@ void ByteCodeInterpreter::spreadFunctionArguments(ExecutionState& state, const V
state.executionContext()->setOnGoingSuperCall(isOngoingSupercall);
}
Value ByteCodeInterpreter::yieldDelegateOperation(ExecutionState& state, Value* registerFile, size_t& programCounter, char* codeBuffer)
{
ASSERT(registerFile != nullptr);
ASSERT(codeBuffer != nullptr);
YieldDelegate* code = (YieldDelegate*)programCounter;
const Value iterator = registerFile[code->m_iterIdx].toObject(state);
GeneratorState resultState = GeneratorState::SuspendedYield;
Value nextResult;
bool done = true;
Value nextValue;
try {
nextResult = iteratorNext(state, iterator);
if (iterator.asObject()->isGeneratorObject() == true) {
resultState = iterator.asObject()->asGeneratorObject()->state();
}
} catch (const Value& v) {
resultState = GeneratorState::CompletedThrow;
nextValue = v;
}
switch (resultState) {
case GeneratorState::CompletedReturn: {
Value ret = iterator.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().stringReturn)).value(state, iterator);
nextValue = iteratorValue(state, nextResult);
if (ret.isUndefined() == true) {
return nextValue;
}
Value innerResult = FunctionObject::call(state, ret, iterator, 1, &nextValue);
if (innerResult.isObject() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "IteratorResult is not an object");
}
nextValue = iteratorValue(state, innerResult);
done = iteratorComplete(state, innerResult);
break;
}
case GeneratorState::SuspendedYield: {
done = iteratorComplete(state, nextResult);
nextValue = iteratorValue(state, nextResult);
break;
}
default: {
ASSERT(resultState == GeneratorState::CompletedThrow);
Value throwMethod = iterator.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().stringThrow)).value(state, iterator);
if (throwMethod.isUndefined() == false) {
Value innerResult;
innerResult = FunctionObject::call(state, throwMethod, iterator, 1, &nextValue);
if (innerResult.isObject() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "IteratorResult is not an object");
}
nextValue = iteratorValue(state, innerResult);
done = iteratorComplete(state, innerResult);
} else {
iteratorClose(state, iterator);
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "yield* violation");
}
}
}
// yield
registerFile[code->m_dstIdx] = nextValue;
if (done == true) {
programCounter = jumpTo(codeBuffer, code->m_endPosition);
return Value(Value::EmptyValue);
}
registerFile[code->m_valueIdx] = nextValue;
GeneratorObject* gen = state.executionContext()->generatorTarget()->asGeneratorObject();
gen->setState(GeneratorState::SuspendedYield);
ADD_PROGRAM_COUNTER(YieldDelegate);
gen->setByteCodePosition((char*)programCounter - codeBuffer);
return nextValue;
}
NEVER_INLINE void ByteCodeInterpreter::declareFunctionDeclarations(ExecutionState& state, DeclareFunctionDeclarations* code, LexicalEnvironment* lexicalEnvironment, Value* stackStorage)
{
InterpretedCodeBlock* cb = code->m_codeBlock;

View file

@ -89,6 +89,7 @@ public:
static bool binaryInOperation(ExecutionState& state, const Value& left, const Value& right);
static Value callFunctionInWithScope(ExecutionState& state, CallFunctionInWithScope* code, ExecutionContext* ec, LexicalEnvironment* env, Value* argv);
static void spreadFunctionArguments(ExecutionState& state, const Value* argv, const size_t argc, ValueVector& argVector);
static Value yieldDelegateOperation(ExecutionState& state, Value* registerFile, size_t& programCounter, char* codeBuffer);
static void declareFunctionDeclarations(ExecutionState& state, DeclareFunctionDeclarations* code, LexicalEnvironment* lexicalEnvironment, Value* stackStorage);
static void defineObjectGetter(ExecutionState& state, ObjectDefineGetter* code, Value* registerFile);

View file

@ -107,6 +107,7 @@ CodeBlock::CodeBlock(Context* ctx, const NativeFunctionInfo& info)
, m_isFunctionDeclarationWithSpecialBinding(false)
, m_isArrowFunctionExpression(false)
, m_isClassConstructor(false)
, m_isGenerator(false)
, m_isInWithScope(false)
, m_isEvalCodeInFunction(false)
, m_isBindedFunction(false)
@ -145,6 +146,7 @@ CodeBlock::CodeBlock(Context* ctx, AtomicString name, size_t argc, bool isStrict
, m_isFunctionDeclarationWithSpecialBinding(false)
, m_isArrowFunctionExpression(false)
, m_isClassConstructor(false)
, m_isGenerator(false)
, m_isInWithScope(false)
, m_isEvalCodeInFunction(false)
, m_isBindedFunction(false)
@ -201,6 +203,7 @@ CodeBlock::CodeBlock(ExecutionState& state, FunctionObject* targetFunction, Valu
, m_isFunctionDeclarationWithSpecialBinding(false)
, m_isArrowFunctionExpression(false)
, m_isClassConstructor(false)
, m_isGenerator(false)
, m_isInWithScope(false)
, m_isEvalCodeInFunction(false)
, m_isBindedFunction(true)
@ -258,6 +261,7 @@ InterpretedCodeBlock::InterpretedCodeBlock(Context* ctx, Script* script, StringV
m_isArrowFunctionExpression = false;
m_isStrict = scopeCtx->m_isStrict;
m_isClassConstructor = scopeCtx->m_isClassConstructor;
m_isGenerator = scopeCtx->m_isGenerator;
m_hasEval = scopeCtx->m_hasEval;
m_hasWith = scopeCtx->m_hasWith;
@ -334,6 +338,7 @@ InterpretedCodeBlock::InterpretedCodeBlock(Context* ctx, Script* script, StringV
m_isArrowFunctionExpression = scopeCtx->m_isArrowFunctionExpression;
m_isConstructor = !scopeCtx->m_isArrowFunctionExpression;
m_isClassConstructor = scopeCtx->m_isClassConstructor;
m_isGenerator = scopeCtx->m_isGenerator;
m_isFunctionNameExplicitlyDeclared = false;
m_isFunctionNameSaveOnHeap = false;
m_needsComplexParameterCopy = false;
@ -351,7 +356,7 @@ InterpretedCodeBlock::InterpretedCodeBlock(Context* ctx, Script* script, StringV
m_parametersInfomation[i].m_isDuplicated = false;
}
m_canUseIndexedVariableStorage = !hasEvalWithYield() && !m_inCatch && !m_inWith && !scopeCtx->m_hasArrowSuper && !m_shouldReparseArguments;
m_canUseIndexedVariableStorage = !hasEvalWithYield() && !m_inCatch && !m_inWith && !scopeCtx->m_hasArrowSuper && !m_shouldReparseArguments && !m_isGenerator;
m_canAllocateEnvironmentOnStack = m_canUseIndexedVariableStorage;
const ASTScopeContextNameInfoVector& innerIdentifiers = scopeCtx->m_names;

View file

@ -199,6 +199,11 @@ public:
return m_isClassConstructor;
}
bool isGenerator() const
{
return m_isGenerator;
}
bool needToLoadThisValue() const
{
return m_needToLoadThisValue;
@ -323,6 +328,7 @@ protected:
bool m_isFunctionDeclarationWithSpecialBinding : 1;
bool m_isArrowFunctionExpression : 1;
bool m_isClassConstructor : 1;
bool m_isGenerator : 1;
bool m_isInWithScope : 1;
bool m_isEvalCodeInFunction : 1;
bool m_isBindedFunction : 1;

View file

@ -123,5 +123,6 @@
#include "VariableDeclaratorNode.h"
#include "WhileStatementNode.h"
#include "WithStatementNode.h"
#include "YieldExpressionNode.h"
#endif

View file

@ -36,6 +36,7 @@ public:
, m_scopeContext(scopeContext)
{
m_scopeContext->m_nodeType = node->type();
m_scopeContext->m_isGenerator = isGenerator;
}
~FunctionNode()
{

View file

@ -484,6 +484,7 @@ struct ASTScopeContext : public gc {
bool m_inWith : 1;
bool m_isArrowFunctionExpression : 1;
bool m_isClassConstructor : 1;
bool m_isGenerator : 1;
bool m_hasArrowSuper : 1;
bool m_hasManyNumeralLiteral : 1;
bool m_needsSpecialInitialize : 1; // flag for fd in catch

View file

@ -40,7 +40,14 @@ public:
virtual ASTNodeType type() { return ASTNodeType::ReturnStatement; }
virtual void generateStatementByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context)
{
if (context->m_tryStatementScopeCount) {
ASSERT(codeBlock != nullptr);
ASSERT(context != nullptr);
if (codeBlock->m_codeBlock->isGenerator() == true) {
codeBlock->pushCode(GeneratorComplete(ByteCodeLOC(SIZE_MAX)), context, this);
}
if (context->m_tryStatementScopeCount != 0) {
if (m_argument) {
ByteCodeRegisterIndex index = m_argument->getRegister(codeBlock, context);
m_argument->generateExpressionByteCode(codeBlock, context, index);

View file

@ -40,6 +40,13 @@ public:
virtual ASTNodeType type() { return ASTNodeType::ThrowStatement; }
virtual void generateStatementByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context)
{
ASSERT(codeBlock != nullptr);
ASSERT(context != nullptr);
if (codeBlock->m_codeBlock->isGenerator() == true) {
codeBlock->pushCode(GeneratorComplete(ByteCodeLOC(SIZE_MAX), true), context, this);
}
context->getRegister();
auto r = m_argument->getRegister(codeBlock, context);
m_argument->generateExpressionByteCode(codeBlock, context, r);

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2019-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef YieldExpressionNode_h
#define YieldExpressionNode_h
#include "ExpressionNode.h"
namespace Escargot {
class YieldExpressionNode : public ExpressionNode {
public:
YieldExpressionNode(RefPtr<Node> argument, bool isDelegate)
: ExpressionNode()
, m_argument(argument)
, m_isDelegate(isDelegate)
{
}
virtual ASTNodeType type() { return ASTNodeType::YieldExpression; }
virtual void generateExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister)
{
if (m_argument == nullptr) {
codeBlock->pushCode(Yield(ByteCodeLOC(m_loc.index), REGISTER_LIMIT, dstRegister), context, this);
} else {
size_t argIdx = m_argument->getRegister(codeBlock, context);
m_argument->generateExpressionByteCode(codeBlock, context, argIdx);
if (m_isDelegate == true) {
size_t iteratorIdx = context->getRegister();
codeBlock->pushCode(GetIterator(ByteCodeLOC(m_loc.index), argIdx, iteratorIdx), context, this);
size_t loopStart = codeBlock->currentCodeSize();
size_t valueIdx = context->getRegister();
codeBlock->pushCode(YieldDelegate(ByteCodeLOC(m_loc.index), iteratorIdx, valueIdx, dstRegister), context, this);
size_t iterStepPos = codeBlock->lastCodePosition<YieldDelegate>();
context->giveUpRegister(); // for drop valueIdx
codeBlock->pushCode(Jump(ByteCodeLOC(m_loc.index), loopStart), context, this);
codeBlock->peekCode<YieldDelegate>(iterStepPos)->m_endPosition = codeBlock->currentCodeSize();
context->giveUpRegister(); // for drop iteratorIdx
} else {
codeBlock->pushCode(Yield(ByteCodeLOC(m_loc.index), argIdx, dstRegister), context, this);
}
context->giveUpRegister(); // for drop argIdx
}
}
private:
RefPtr<Node> m_argument;
bool m_isDelegate : 1;
};
}
#endif

View file

@ -3458,11 +3458,11 @@ public:
RefPtr<Node> exprNode;
ScanExpressionResult expr;
if (!this->context->allowYield && this->matchKeyword(YieldKeyword)) {
if (isParse) {
exprNode = this->parseYieldExpression();
if (this->context->allowYield == false && this->matchKeyword(YieldKeyword) == true) {
if (isParse == true) {
exprNode = this->yieldExpression<ParseAs(YieldExpressionNode)>();
} else {
expr = this->scanYieldExpression();
expr = this->yieldExpression<Scan>();
}
} else {
ALLOC_TOKEN(startToken);
@ -5681,86 +5681,65 @@ public:
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(options.params), method.get(), popScopeContext(node), isGenerator));
}
FunctionExpressionNode* parseGeneratorMethod()
PassRefPtr<FunctionExpressionNode> parseGeneratorMethod()
{
// TODO
ALLOC_TOKEN(token);
this->nextToken(token);
this->throwUnexpectedToken(token);
RELEASE_ASSERT_NOT_REACHED();
/*
MetaNode node = this->createNode();
const isGenerator = true;
const previousAllowYield = this->context.allowYield;
const bool previousAllowYield = this->context->allowYield;
this->expect(LeftParenthesis);
this->context.allowYield = true;
const params = this->parseFormalParameters();
this->context.allowYield = false;
const method = this->parsePropertyMethod(params);
this->context.allowYield = previousAllowYield;
pushScopeContext(AtomicString());
this->context->allowYield = true;
ParseFormalParametersResult formalParameters = this->parseFormalParameters();
this->context->allowYield = false;
// Note: Change it to parsePropertyMethod, if possible
RefPtr<Node> method = this->isolateCoverGrammar(&Parser::parseFunctionSourceElements);
this->context->allowYield = previousAllowYield;
return this->finalize(node, new Node.FunctionExpression(null, params.params, method, isGenerator));
*/
extractNamesFromFunctionParams(formalParameters.params);
scopeContexts.back()->m_paramsStart.index = node.index;
return this->finalize(node, new FunctionExpressionNode(AtomicString(), std::move(formalParameters.params), method.get(), popScopeContext(node), true));
}
// ECMA-262 14.4 Generator Function Definitions
Node* parseYieldExpression()
template <typename T, bool isParse>
T yieldExpression()
{
this->throwError("yield keyword is not supported yet");
RELEASE_ASSERT_NOT_REACHED();
/*
const node = this->createNode();
this->expectKeyword('yield');
MetaNode node = this->createNode();
this->expectKeyword(YieldKeyword);
let argument = null;
let delegate = false;
if (!this->hasLineTerminator) {
const previousAllowYield = this->context.allowYield;
this->context.allowYield = false;
delegate = this->match('*');
if (delegate) {
RefPtr<Node> exprNode;
bool delegate = false;
if (this->hasLineTerminator == false) {
const bool previousAllowYield = this->context->allowYield;
this->context->allowYield = false;
delegate = this->match(Multiply);
if (delegate == true) {
this->nextToken();
argument = this->assignmentExpression<Parse>();
if (isParse == true) {
exprNode = this->assignmentExpression<Parse>();
} else {
this->assignmentExpression<Scan>();
}
} else {
if (!this->match(';') && !this->match('}') && !this->match(')') && this->lookahead.type !== Token.EOF) {
argument = this->assignmentExpression<Parse>();
if (this->match(SemiColon) == false && this->match(RightBrace) == false && this->match(RightParenthesis) == false && this->lookahead.type != Token::EOFToken) {
if (isParse == true) {
exprNode = this->assignmentExpression<Parse>();
} else {
this->assignmentExpression<Scan>();
}
}
}
this->context.allowYield = previousAllowYield;
this->context->allowYield = previousAllowYield;
}
return this->finalize(node, new Node.YieldExpression(argument, delegate));
*/
}
ScanExpressionResult scanYieldExpression()
{
this->throwError("yield keyword is not supported yet");
RELEASE_ASSERT_NOT_REACHED();
/*
const node = this->createNode();
this->expectKeyword('yield');
let argument = null;
let delegate = false;
if (!this->hasLineTerminator) {
const previousAllowYield = this->context.allowYield;
this->context.allowYield = false;
delegate = this->match('*');
if (delegate) {
this->nextToken();
argument = this->assignmentExpression<Parse>();
} else {
if (!this->match(';') && !this->match('}') && !this->match(')') && this->lookahead.type !== Token.EOF) {
argument = this->assignmentExpression<Parse>();
}
}
this->context.allowYield = previousAllowYield;
if (isParse == true) {
return this->finalize(node, new YieldExpressionNode(exprNode, delegate));
}
return this->finalize(node, new Node.YieldExpression(argument, delegate));
*/
return ScanExpressionResult(ASTNodeType::YieldExpression);
}
// ECMA-262 14.5 Class Definitions
@ -5809,7 +5788,7 @@ public:
key = this->parseObjectPropertyKey();
value = this->parseSetterMethod();
}
} else if (lookaheadPropertyKey && token->type == Token::PunctuatorToken && *token->valueStringLiteral() == "*") {
} else if (lookaheadPropertyKey == true && token->type == Token::PunctuatorToken && token->valuePunctuatorKind == Multiply) {
kind = ClassElementNode::Kind::Method;
computed = this->match(LeftSquareBracket);
key = this->parseObjectPropertyKey();
@ -6181,6 +6160,7 @@ std::tuple<RefPtr<Node>, ASTScopeContext*> parseSingleFunction(::Escargot::Conte
parser.config.reparseArguments = codeBlock->shouldReparseArguments();
auto sc = new ASTScopeContext(codeBlock->isStrict());
parser.pushScopeContext(sc);
parser.context->allowYield = codeBlock->isGenerator() == false;
RefPtr<Node> nd;
if (codeBlock->isArrowFunctionExpression()) {
nd = parser.parseArrowFunctionSourceElements();

View file

@ -35,10 +35,11 @@ class ExecutionContext : public gc {
public:
ExecutionContext(Context* context, ExecutionContext* parent = nullptr, LexicalEnvironment* lexicalEnvironment = nullptr, bool inStrictMode = false)
: m_inStrictMode(inStrictMode)
, m_context(context)
: m_context(context)
, m_parent(parent)
, m_lexicalEnvironment(lexicalEnvironment)
, m_generatorTarget(nullptr)
, m_inStrictMode(inStrictMode)
, m_onGoingClassConstruction(false)
, m_onGoingSuperCall(false)
{
@ -59,6 +60,11 @@ public:
return m_lexicalEnvironment;
}
Object* generatorTarget()
{
return m_generatorTarget;
}
bool inStrictMode()
{
return m_inStrictMode;
@ -74,6 +80,12 @@ public:
return m_onGoingSuperCall;
}
void setGeneratorTarget(Object* target)
{
ASSERT(target != nullptr);
m_generatorTarget = target;
}
void setOnGoingClassConstruction(bool startClassConstruction)
{
m_onGoingClassConstruction = startClassConstruction;
@ -91,10 +103,11 @@ public:
Value getSuperConstructor(ExecutionState& state);
private:
bool m_inStrictMode : 1;
Context* m_context;
ExecutionContext* m_parent;
LexicalEnvironment* m_lexicalEnvironment;
Object* m_generatorTarget;
bool m_inStrictMode : 1;
bool m_onGoingClassConstruction : 1;
bool m_onGoingSuperCall : 1;
};

View file

@ -20,6 +20,7 @@
#include "Escargot.h"
#include "ArrayObject.h"
#include "FunctionObject.h"
#include "GeneratorObject.h"
#include "ExecutionContext.h"
#include "Context.h"
#include "parser/ScriptParser.h"
@ -437,7 +438,17 @@ Value FunctionObject::processCall(ExecutionState& state, const Value& receiverSr
record->setNewTarget(receiverSrc.asObject());
}
Value* registerFile = (Value*)alloca((registerSize + stackStorageSize + literalStorageSize) * sizeof(Value));
Value* registerFile;
if (UNLIKELY(m_codeBlock->isGenerator() == true)) {
if (isNewExpression == true) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "Generator cannot be invoked with 'new'");
}
registerFile = (Value*)GC_MALLOC((registerSize + stackStorageSize + literalStorageSize) * sizeof(Value));
} else {
registerFile = (Value*)alloca((registerSize + stackStorageSize + literalStorageSize) * sizeof(Value));
}
Value* stackStorage = registerFile + registerSize;
{
@ -573,6 +584,18 @@ Value FunctionObject::processCall(ExecutionState& state, const Value& receiverSr
}
}
if (UNLIKELY(m_codeBlock->isGenerator() == true)) {
ExecutionState* newState = new ExecutionState(ctx, &state, ec, registerFile);
if (UNLIKELY(m_codeBlock->usesArgumentsObject() == true)) {
generateArgumentsObject(*newState, record, stackStorage);
}
GeneratorObject* gen = new GeneratorObject(state, newState, blk);
ec->setGeneratorTarget(gen);
return gen;
}
ExecutionState newState(ctx, &state, ec, registerFile);
if (UNLIKELY(m_codeBlock->usesArgumentsObject())) {

View file

@ -95,6 +95,11 @@ public:
return m_codeBlock->isClassConstructor();
}
bool isGenerator()
{
return m_codeBlock->isGenerator();
}
bool isBuiltin()
{
return m_isBuiltin;
@ -157,14 +162,11 @@ public:
FunctionKind functionKind()
{
if (isClassConstructor()) {
if (isClassConstructor() == true) {
return FunctionKind::ClassConstructor;
}
/* TODO:
else if (isGenerator()) {
} else if (isGenerator() == true) {
return FunctionKind::Generator;
}
*/
return FunctionKind::Normal;
}

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2019-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "Escargot.h"
#include "GeneratorObject.h"
#include "IteratorOperations.h"
#include "interpreter/ByteCodeInterpreter.h"
#include "Context.h"
namespace Escargot {
GeneratorObject::GeneratorObject(ExecutionState& state)
: Object(state)
, m_executionState(nullptr)
, m_blk(nullptr)
, m_byteCodePosition(0)
, m_state(GeneratorState::Undefined)
, m_resumeValueIdx(UINT16_MAX)
{
Object::setPrototype(state, state.context()->globalObject()->generatorPrototype());
}
GeneratorObject::GeneratorObject(ExecutionState& state, ExecutionState* executionState, ByteCodeBlock* blk)
: Object(state)
, m_executionState(executionState)
, m_blk(blk)
, m_byteCodePosition(0)
, m_state(GeneratorState::SuspendedStart)
, m_resumeValueIdx(UINT16_MAX)
{
ASSERT(blk != nullptr);
Object::setPrototype(state, state.context()->globalObject()->generatorPrototype());
}
void* GeneratorObject::operator new(size_t size)
{
static bool typeInited = false;
static GC_descr descr;
if (typeInited == false) {
GC_word obj_bitmap[GC_BITMAP_SIZE(GeneratorObject)] = { 0 };
GC_set_bit(obj_bitmap, GC_WORD_OFFSET(GeneratorObject, m_executionState));
GC_set_bit(obj_bitmap, GC_WORD_OFFSET(GeneratorObject, m_blk));
descr = GC_make_descriptor(obj_bitmap, GC_WORD_LEN(GeneratorObject));
typeInited = true;
}
return GC_MALLOC_EXPLICITLY_TYPED(size, descr);
}
// https://www.ecma-international.org/ecma-262/6.0/#sec-generatorvalidate
GeneratorObject* generatorValidate(ExecutionState& state, const Value& generator)
{
if (generator.isObject() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, errorMessage_GlobalObject_ThisNotObject);
}
if (generator.asObject()->isGeneratorObject() == false) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, state.context()->staticStrings().Generator.string(), true, state.context()->staticStrings().next.string(), errorMessage_GlobalObject_CalledOnIncompatibleReceiver);
}
GeneratorObject* gen = generator.asObject()->asGeneratorObject();
ASSERT(gen != nullptr);
if (gen->state() == GeneratorState::Executing) {
ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "Generator is already running");
}
return gen;
}
// https://www.ecma-international.org/ecma-262/6.0/#sec-generatorresume
Value generatorResume(ExecutionState& state, const Value& generator, const Value& value)
{
GeneratorObject* gen = generatorValidate(state, generator);
ASSERT(gen != nullptr);
if (gen->state() >= GeneratorState::CompletedReturn) {
return createIterResultObject(state, Value(), true);
}
ASSERT(gen->state() == GeneratorState::SuspendedStart || gen->state() == SuspendedYield);
gen->setState(GeneratorState::Executing);
if (gen->resumeValueIdx() != UINT16_MAX) {
gen->executionState()->registerFile()[gen->resumeValueIdx()] = value;
}
Value result = ByteCodeInterpreter::interpret(*gen->executionState(), gen->block(), gen->byteCodePosition(), gen->executionState()->registerFile());
if (gen->state() >= GeneratorState::CompletedReturn) {
return createIterResultObject(state, result, true);
}
return createIterResultObject(state, result, false);
}
// https://www.ecma-international.org/ecma-262/6.0/#sec-generatorresume
Value generatorResumeAbrupt(ExecutionState& state, const Value& generator, const Value& value, GeneratorAbruptType type)
{
GeneratorObject* gen = generatorValidate(state, generator);
ASSERT(gen != nullptr);
if (gen->state() == GeneratorState::SuspendedStart) {
gen->setState(GeneratorState::CompletedReturn);
}
if (gen->state() >= GeneratorState::CompletedReturn) {
if (type == GeneratorAbruptType::Return) {
return createIterResultObject(state, value, true);
}
state.throwException(value);
}
ASSERT(gen->state() == GeneratorState::SuspendedYield);
gen->setState(GeneratorState::Executing);
const Value result = ByteCodeInterpreter::interpret(*gen->executionState(), gen->block(), gen->byteCodePosition(), gen->executionState()->registerFile());
gen->setState(type == GeneratorAbruptType::Return ? GeneratorState::CompletedReturn : GeneratorState::CompletedThrow);
if (type == GeneratorAbruptType::Throw) {
state.throwException(value);
}
return createIterResultObject(state, value, true);
}
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2019-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#ifndef __EscargotGeneratorObject__
#define __EscargotGeneratorObject__
#include "runtime/Object.h"
#include "interpreter/ByteCode.h"
namespace Escargot {
enum GeneratorState {
Undefined,
SuspendedStart,
SuspendedYield,
Executing,
CompletedReturn,
CompletedThrow,
};
enum GeneratorAbruptType {
Return,
Throw
};
class GeneratorObject : public Object {
public:
GeneratorObject(ExecutionState& state);
GeneratorObject(ExecutionState& state, ExecutionState* executionState, ByteCodeBlock* blk);
virtual const char* internalClassProperty() override
{
return "Generator";
}
virtual bool isGeneratorObject() const override
{
return true;
}
ExecutionState* executionState()
{
return m_executionState;
}
ByteCodeBlock* block()
{
return m_blk;
}
size_t byteCodePosition()
{
return m_byteCodePosition;
}
GeneratorState state()
{
return m_state;
}
uint16_t resumeValueIdx()
{
return m_resumeValueIdx;
}
void setState(GeneratorState state)
{
m_state = state;
}
void setByteCodePosition(size_t pos)
{
m_byteCodePosition = pos;
}
void setResumeValueIdx(uint16_t idx)
{
m_resumeValueIdx = idx;
}
void* operator new(size_t size);
void* operator new[](size_t size) = delete;
private:
ExecutionState* m_executionState;
ByteCodeBlock* m_blk;
size_t m_byteCodePosition;
GeneratorState m_state;
SmallValue m_resumeValue;
uint16_t m_resumeValueIdx;
};
GeneratorObject* generatorValidate(ExecutionState& state, const Value& generator);
Value generatorResume(ExecutionState& state, const Value& generator, const Value& value);
Value generatorResumeAbrupt(ExecutionState& state, const Value& generator, const Value& value, GeneratorAbruptType type);
}
#endif

View file

@ -146,6 +146,8 @@ public:
, m_weakMapPrototype(nullptr)
, m_weakSet(nullptr)
, m_weakSetPrototype(nullptr)
, m_generator(nullptr)
, m_generatorPrototype(nullptr)
{
m_objectPrototype = Object::createBuiltinObjectPrototype(state);
m_objectPrototype->markThisObjectDontNeedStructureTransitionTable(state);
@ -192,6 +194,7 @@ public:
installSet(state);
installWeakMap(state);
installWeakSet(state);
installGenerator(state);
installOthers(state);
}
@ -228,6 +231,7 @@ public:
void installSet(ExecutionState& state);
void installWeakMap(ExecutionState& state);
void installWeakSet(ExecutionState& state);
void installGenerator(ExecutionState& state);
void installOthers(ExecutionState& state);
Value eval(ExecutionState& state, const Value& arg);
@ -604,6 +608,11 @@ public:
return m_weakSetPrototype;
}
Object* generatorPrototype()
{
return m_generatorPrototype;
}
FunctionObject* eval()
{
return m_eval;
@ -765,6 +774,8 @@ private:
Object* m_weakMapPrototype;
FunctionObject* m_weakSet;
Object* m_weakSetPrototype;
FunctionObject* m_generator; // TODO: remove if become unnecessary
Object* m_generatorPrototype;
};
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2019-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "Escargot.h"
#include "GlobalObject.h"
#include "Context.h"
#include "VMInstance.h"
#include "GeneratorObject.h"
namespace Escargot {
static Value builtinGeneratorNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
{
return generatorResume(state, thisValue, argc > 0 ? argv[0] : Value());
}
static Value builtinGeneratorReturn(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
{
return generatorResumeAbrupt(state, thisValue, argc > 0 ? argv[0] : Value(), GeneratorAbruptType::Return);
}
static Value builtinGeneratorThrow(ExecutionState& state, Value thisValue, size_t argc, Value* argv, bool isNewExpression)
{
return generatorResumeAbrupt(state, thisValue, argc > 0 ? argv[0] : Value(), GeneratorAbruptType::Throw);
}
void GlobalObject::installGenerator(ExecutionState& state)
{
m_generator = new FunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().Generator, nullptr, 0, nullptr, NativeFunctionInfo::Strict));
m_generator->markThisObjectDontNeedStructureTransitionTable(state);
m_generator->setPrototype(state, m_functionPrototype);
m_generatorPrototype = m_objectPrototype;
m_generatorPrototype = new GeneratorObject(state);
m_generatorPrototype->setPrototype(state, m_iteratorPrototype);
m_generatorPrototype->markThisObjectDontNeedStructureTransitionTable(state);
m_generatorPrototype->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().constructor), ObjectPropertyDescriptor(m_generator, ObjectPropertyDescriptor::ConfigurablePresent));
m_generatorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().next),
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().next, builtinGeneratorNext, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_generatorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().stringReturn),
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().stringReturn, builtinGeneratorReturn, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_generatorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().stringThrow),
ObjectPropertyDescriptor(new FunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().stringThrow, builtinGeneratorThrow, 0, nullptr, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_generatorPrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, Value(state.context()->vmInstance()->globalSymbols().toStringTag)),
ObjectPropertyDescriptor(Value(state.context()->staticStrings().Generator.string()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
}
}

View file

@ -54,6 +54,7 @@ class MapObject;
class SetObject;
class WeakMapObject;
class WeakSetObject;
class GeneratorObject;
#define POINTER_VALUE_STRING_SYMBOL_TAG_IN_DATA 0x3
// finding what is type of PointerValue operation is used in SmallValue <-> Value and interpreter
@ -230,6 +231,11 @@ public:
return false;
}
virtual bool isGeneratorObject() const
{
return false;
}
virtual bool isCallable() const
{
return false;
@ -400,6 +406,12 @@ public:
return (WeakSetObject*)this;
}
GeneratorObject* asGeneratorObject()
{
ASSERT(isGeneratorObject() == true);
return (GeneratorObject*)this;
}
bool hasTag(const size_t tag) const
{
return tag == *((size_t*)(this));

View file

@ -371,6 +371,7 @@ namespace Escargot {
F(Set) \
F(WeakMap) \
F(WeakSet) \
F(Generator) \
F(Symbol) \
F(symbol) \
F(hasInstance) \

273
test/es2015/generator.js Normal file
View file

@ -0,0 +1,273 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function* generator1(i) {
yield i;
yield i + 10;
}
var gen = generator1(10);
assert(gen.next().value === 10);
assert(gen.next().value === 20);
// Simple example
function* idMaker() {
var index = 0;
while (index < index+1)
yield index++;
}
var gen = idMaker();
for (var i = 0; i < 10; i++) {
assert(gen.next().value === i);
}
var console = {log:print};
// with yield*
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i);
yield i + 10;
}
var gen = generator(10);
assert(gen.next().value === 10);
assert(gen.next().value === 11);
assert(gen.next().value === 12);
assert(gen.next().value === 13);
assert(gen.next().value === 20);
// Passing arguments into Generators
var counter = 0;
function logger (a,b) {
a;
b;
counter++;
}
function* logGenerator() {
counter++;
logger(1, yield);
assert(counter === 2);
counter++;
logger(2, yield);
assert(counter === 4);
counter++;
logger(3, yield);
assert(counter === 6);
counter++;
}
var gen = logGenerator();
gen.next();
assert(counter === 1);
gen.next('pretzel');
assert(counter === 3);
gen.next('california');
assert(counter === 5);
gen.next('mayonnaise');
assert(counter === 7);
// Return statement in a generator
function* yieldAndReturn() {
yield "Y";
return "R";
yield "unreachable";
}
var gen = yieldAndReturn()
assert(gen.next().value === "Y");
assert(gen.next().value === "R");
assert(gen.next().done === true);
// Generator as an object property
var someObj = {
*generator () {
yield 'a';
yield 'b';
}
}
var gen = someObj.generator();
assert(gen.next().value === 'a');
assert(gen.next().value === 'b');
assert(gen.next().done === true);
// Generator as an object methodSection
class Foo {
*generator () {
yield 1;
yield 2;
yield 3;
}
}
var f = new Foo ();
var gen = f.generator();
assert(gen.next().value === 1);
assert(gen.next().value === 2);
assert(gen.next().value === 3);
assert(gen.next().done === true);
// Generator as a computed property
class Foo {
*[Symbol.iterator] () {
yield 1;
yield 2;
}
}
var array = Array.from(new Foo);
assert(array.length === 2);
assert(array[0] === 1);
assert(array[1] === 2);
var SomeObj = {
*[Symbol.iterator] () {
yield 'a';
yield 'b';
}
}
array = Array.from(SomeObj);
assert(array.length === 2);
assert(array[0] === 'a');
assert(array[1] === 'b');
// Generators are not constructable
try {
function* f() {}
var obj = new f;
assert(false);
} catch (e) {
assert (e instanceof TypeError);
}
// Generator defined in an expression
var foo = function* () {
yield 10;
yield 20;
};
var bar = foo();
assert(bar.next().value);
// Delegating to another generator
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
var iterator = g2();
assert(iterator.next().value === 1);
assert(iterator.next().value === 2);
assert(iterator.next().value === 3);
assert(iterator.next().value === 4);
assert(iterator.next().value === 5);
assert(iterator.next().done === true);
// Other Iterable objects
function* g3() {
yield* [1, 2];
yield* '34';
yield* Array.from(arguments);
}
var iterator = g3(5, 6);
assert(iterator.next().value === 1); // {value: 1, done: false}
assert(iterator.next().value === 2); // {value: 2, done: false}
assert(iterator.next().value === "3"); // {value: "3", done: false}
assert(iterator.next().value === "4"); // {value: "4", done: false}
assert(iterator.next().value === 5); // {value: 5, done: false}
assert(iterator.next().value === 6); // {value: 6, done: false}
assert(iterator.next().done === true); // {value: undefined, done: true}
// The value of yield* expression itself
function* g4() {
yield* [1, 2, 3];
return 'foo';
}
var result;
function* g5() {
result = yield* g4();
}
var iterator = g5();
assert(iterator.next().value === 1);
assert(iterator.next().value === 2);
assert(iterator.next().value === 3);
assert(iterator.next().done === true);
assert(result === 'foo');
// %GeneratorPrototype%.return
function* g6() {
yield* [1, 2, 3];
return 'foo';
}
var iterator = g6();
assert(iterator.next().value === 1);
assert(iterator.return().done === true);
assert(iterator.next().done === true);
var iterator = g6();
assert(iterator.next().value === 1);
assert(iterator.return(5).value === 5);
assert(iterator.next().done === true);
// %GeneratorPrototype%.throw
var iterator = g6();
assert(iterator.next().value === 1);
try {
iterator.throw();
assert(false);
} catch (e) {
assert (e === undefined);
}
assert(iterator.next().done === true);
var iterator = g6();
assert(iterator.next().value === 1);
try {
iterator.throw(5);
assert(false);
} catch (e) {
assert (e === 5);
}
assert(iterator.next().done === true);