mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Implement the generator functions (#270)
Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
parent
06d13b14ac
commit
9166b2de38
23 changed files with 1016 additions and 79 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -123,5 +123,6 @@
|
|||
#include "VariableDeclaratorNode.h"
|
||||
#include "WhileStatementNode.h"
|
||||
#include "WithStatementNode.h"
|
||||
#include "YieldExpressionNode.h"
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ public:
|
|||
, m_scopeContext(scopeContext)
|
||||
{
|
||||
m_scopeContext->m_nodeType = node->type();
|
||||
m_scopeContext->m_isGenerator = isGenerator;
|
||||
}
|
||||
~FunctionNode()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
73
src/parser/ast/YieldExpressionNode.h
Normal file
73
src/parser/ast/YieldExpressionNode.h
Normal 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
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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())) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
140
src/runtime/GeneratorObject.cpp
Normal file
140
src/runtime/GeneratorObject.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
114
src/runtime/GeneratorObject.h
Normal file
114
src/runtime/GeneratorObject.h
Normal 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
|
||||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
64
src/runtime/GlobalObjectBuiltinGeneratorFunction.cpp
Normal file
64
src/runtime/GlobalObjectBuiltinGeneratorFunction.cpp
Normal 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)));
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
273
test/es2015/generator.js
Normal 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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue