[ES6] Implement LLInt/Baseline Support for ES6 Generators and enable this feature
https://bugs.webkit.org/show_bug.cgi?id=150792
Reviewed by Saam Barati.
.:
* Source/cmake/OptionsWin.cmake:
* Source/cmake/WebKitFeatures.cmake:
Source/JavaScriptCore:
This patch implements basic functionality of ES6 Generators in LLInt and Baseline tiers.
While the implementation has some inefficient part, the implementation covers edge cases.
Later, we will make this efficient.
https://bugs.webkit.org/show_bug.cgi?id=151545
https://bugs.webkit.org/show_bug.cgi?id=151546
https://bugs.webkit.org/show_bug.cgi?id=151547
https://bugs.webkit.org/show_bug.cgi?id=151552
https://bugs.webkit.org/show_bug.cgi?id=151560
https://bugs.webkit.org/show_bug.cgi?id=151586
To encourage DFG / FTL later, we take the following design.
1. Use switch_imm to jump to the save/resume points.
Instead of saving / restoring instruction pointer to resume from it, we use switch_imm to jump to the resume point.
This limits one entry point to a given generator function. This design makes inlining easy.
The generated code becomes the following.
function @generatorNext(@generator, @generatorState, @generatorValue, @generatorResumeMode)
{
switch (@generatorState) {
case Initial:
...
initial sequence.
...
op_save(Yield_0); // op_save contains *virtual* jump to Yield_0.
// CFG shows a jump edge to Yield_0 point, but it won't be actually used.
return ...;
case Yield_0:
op_resume();
if (@generatorResumeMode == Throw)
...
else if (@generatorResumeMode == Return)
...
...
// sentValue is a value sent from a caller by `generator.next(sentValue)`.
sentValue = @generatorValue;
...
op_save(Yield_1);
return ...;
case Yield_1:
op_resume();
if (@generatorResumeMode == Throw)
...
else if (@generatorResumeMode == Return)
...
...
sentValue = @generatorValue;
...
...
}
}
Resume sequence should not be emitted per yield.
This should be done in https://bugs.webkit.org/show_bug.cgi?id=151552.
2. Store live frame registers to GeneratorFrame
To save and resume generator's state, we save all the live registers in GeneratorFrame.
And when resuming, we refill registers with saved ones.
Since saved register contains scope register, |this| etc., the environment including the scope chain will be recovered automatically.
While saving and resuming callee registers, we don't save parameter registers.
These registers will be used to control generator's resume behavior.
We perform BytecodeLivenessAnalysis in CodeBlock to determine actually *def*ined registers at that resume point.
3. GeneratorFunction will evaluate parameters before generating Generator
Generator's parameter should be evaluated before entering Generator's body. For example,
function hello() { ... }
function *gen(a, b = hello())
{
yield b;
}
let g = gen(20); // Now, hello should be called.
To enable this, we evaluate parameters in GeneratorFunction, and after that, we create a Generator and return it.
This can be explained by the following pseudo code.
function *gen(a, b = hello())
{
// This is generator.
return {
@generatorNext: function (@generator, @generatorState, @generatorValue, @generatorResumeMode)
{
...
}
}
}
4. op_save seems similar to conditional jump
We won't jump to elsewhere from op_save actually. But we add a *virtual* jump edge (flow) from op_save to the point so called *merge point*.
We construct the CFG as follows,
(global generator switch) -> (initial sequence) -> (op_save) ----+-> (merge point) -> (next sequence)*
| | |
| v |
| (op_ret) |
| |
+------------------------------------------->(op_resume)--+
By constructing such a graph,
1. Since we have a flow from (op_save) to (merge point), at merge point, we can *use* locals that are defined before (op_save)
2. op_save should claim that it does not define anything. And claim that it *use*s locals that are used in (merge point).
3. at op_resume, we see *use*d locals at merge point and define all of them.
We can do the above things in use-def analysis because use-def analysis is backward analysis.
And after analyzing use-def chains, in op_save / op_resume, we only save / resume live registers at the head of merge point.
* API/JSScriptRef.cpp:
(parseScript):
* CMakeLists.txt:
* Configurations/FeatureDefines.xcconfig:
* DerivedSources.make:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/BuiltinExecutables.cpp:
(JSC::createExecutableInternal):
* builtins/GeneratorPrototype.js: Added.
(generatorResume):
(next):
(return):
(throw):
* bytecode/BytecodeBasicBlock.cpp:
(JSC::isBranch):
* bytecode/BytecodeList.json:
* bytecode/BytecodeLivenessAnalysis.cpp:
(JSC::stepOverInstruction):
(JSC::computeLocalLivenessForBytecodeOffset):
(JSC::BytecodeLivenessAnalysis::runLivenessFixpoint):
(JSC::BytecodeLivenessAnalysis::computeFullLiveness):
(JSC::BytecodeLivenessAnalysis::computeKills):
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::CodeBlock):
(JSC::CodeBlock::finishCreation):
(JSC::CodeBlock::shrinkToFit):
(JSC::CodeBlock::validate):
* bytecode/CodeBlock.h:
(JSC::CodeBlock::numCalleeLocals):
(JSC::CodeBlock::liveCalleeLocalsAtYield):
* bytecode/EvalCodeCache.h:
(JSC::EvalCodeCache::tryGet):
(JSC::EvalCodeCache::getSlow):
(JSC::EvalCodeCache::isCacheable):
* bytecode/ExecutableInfo.h:
(JSC::ExecutableInfo::ExecutableInfo):
(JSC::ExecutableInfo::generatorThisMode):
(JSC::ExecutableInfo::superBinding):
(JSC::ExecutableInfo::parseMode):
(JSC::ExecutableInfo::isArrowFunction): Deleted.
* bytecode/PreciseJumpTargets.cpp:
(JSC::getJumpTargetsForBytecodeOffset):
* bytecode/UnlinkedCodeBlock.cpp:
(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::parseMode):
(JSC::UnlinkedCodeBlock::generatorThisMode):
(JSC::UnlinkedCodeBlock::superBinding):
(JSC::UnlinkedCodeBlock::isArrowFunction): Deleted.
* bytecode/UnlinkedFunctionExecutable.cpp:
(JSC::generateUnlinkedFunctionCodeBlock):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
(JSC::UnlinkedFunctionExecutable::unlinkedCodeBlockFor):
* bytecode/UnlinkedFunctionExecutable.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::initializeParameters):
(JSC::BytecodeGenerator::newRegister):
(JSC::BytecodeGenerator::reclaimFreeRegisters):
(JSC::BytecodeGenerator::createVariable):
(JSC::BytecodeGenerator::emitCreateThis):
(JSC::BytecodeGenerator::emitNewFunctionExpressionCommon):
(JSC::BytecodeGenerator::emitNewFunctionExpression):
(JSC::BytecodeGenerator::emitNewArrowFunctionExpression):
(JSC::BytecodeGenerator::emitNewFunction):
(JSC::BytecodeGenerator::emitIteratorNextWithValue):
(JSC::BytecodeGenerator::emitYieldPoint):
(JSC::BytecodeGenerator::emitSave):
(JSC::BytecodeGenerator::emitResume):
(JSC::BytecodeGenerator::emitYield):
(JSC::BytecodeGenerator::emitDelegateYield):
(JSC::BytecodeGenerator::emitGeneratorStateChange):
(JSC::BytecodeGenerator::emitGeneratorStateLabel):
(JSC::BytecodeGenerator::beginGenerator):
(JSC::BytecodeGenerator::endGenerator):
(JSC::BytecodeGenerator::emitNewFunctionInternal): Deleted.
(JSC::BytecodeGenerator::emitNewFunctionCommon): Deleted.
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::generatorThisMode):
(JSC::BytecodeGenerator::superBinding):
(JSC::BytecodeGenerator::generatorRegister):
(JSC::BytecodeGenerator::generatorStateRegister):
(JSC::BytecodeGenerator::generatorValueRegister):
(JSC::BytecodeGenerator::generatorResumeModeRegister):
(JSC::BytecodeGenerator::parseMode):
(JSC::BytecodeGenerator::registerFor):
(JSC::BytecodeGenerator::makeFunction):
* bytecompiler/NodesCodegen.cpp:
(JSC::ThisNode::emitBytecode):
(JSC::emitHomeObjectForCallee):
(JSC::emitSuperBaseForCallee):
(JSC::ReturnNode::emitBytecode):
(JSC::FunctionNode::emitBytecode):
(JSC::YieldExprNode::emitBytecode):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::ByteCodeParser):
(JSC::DFG::ByteCodeParser::inlineCall):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::handlePutById):
* dfg/DFGForAllKills.h:
(JSC::DFG::forAllKilledOperands):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::forAllLocalsLiveInBytecode):
* dfg/DFGOSREntrypointCreationPhase.cpp:
(JSC::DFG::OSREntrypointCreationPhase::run):
* dfg/DFGVariableEventStream.cpp:
(JSC::DFG::VariableEventStream::reconstruct):
* ftl/FTLForOSREntryJITCode.cpp:
(JSC::FTL::ForOSREntryJITCode::initializeEntryBuffer):
* ftl/FTLForOSREntryJITCode.h:
* ftl/FTLOSREntry.cpp:
(JSC::FTL::prepareOSREntry):
* ftl/FTLState.cpp:
(JSC::FTL::State::State):
* heap/MarkedBlock.h:
(JSC::MarkedBlock::isAtom):
(JSC::MarkedBlock::isLiveCell):
* interpreter/Interpreter.cpp:
(JSC::eval):
(JSC::Interpreter::dumpRegisters):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::frameRegisterCountFor):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emitNewFuncCommon):
(JSC::JIT::emit_op_new_func):
(JSC::JIT::emit_op_new_generator_func):
(JSC::JIT::emitNewFuncExprCommon):
(JSC::JIT::emit_op_new_func_exp):
(JSC::JIT::emit_op_new_generator_func_exp):
(JSC::JIT::emit_op_save):
(JSC::JIT::emit_op_resume):
* jit/JITOperations.cpp:
(JSC::operationNewFunctionCommon):
* jit/JITOperations.h:
* llint/LLIntEntrypoint.cpp:
(JSC::LLInt::frameRegisterCountFor):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::traceFunctionPrologue):
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter.asm:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createYield):
(JSC::ASTBuilder::createFunctionMetadata):
(JSC::ASTBuilder::propagateArgumentsUse):
* parser/Nodes.cpp:
(JSC::FunctionMetadataNode::FunctionMetadataNode):
* parser/Nodes.h:
* parser/Parser.cpp:
(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseInner):
(JSC::Parser<LexerType>::parseGeneratorFunctionSourceElements):
(JSC::Parser<LexerType>::parseFunctionBody):
(JSC::stringForFunctionMode):
(JSC::Parser<LexerType>::createGeneratorParameters):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseAssignmentExpression):
(JSC::Parser<LexerType>::parseYieldExpression):
(JSC::Parser<LexerType>::parsePropertyMethod):
(JSC::Parser<LexerType>::parseFunctionExpression):
* parser/Parser.h:
(JSC::Scope::Scope):
(JSC::Scope::setSourceParseMode):
(JSC::Scope::hasArguments):
(JSC::Scope::collectFreeVariables):
(JSC::Scope::setIsFunction):
(JSC::Scope::setIsGeneratorFunction):
(JSC::Scope::setIsGenerator):
(JSC::parse):
* parser/ParserModes.h:
(JSC::isFunctionParseMode):
(JSC::isModuleParseMode):
(JSC::isProgramParseMode):
* parser/SourceCodeKey.h: Added.
(JSC::SourceCodeKey::SourceCodeKey):
(JSC::SourceCodeKey::isHashTableDeletedValue):
(JSC::SourceCodeKey::hash):
(JSC::SourceCodeKey::length):
(JSC::SourceCodeKey::isNull):
(JSC::SourceCodeKey::string):
(JSC::SourceCodeKey::operator==):
(JSC::SourceCodeKeyHash::hash):
(JSC::SourceCodeKeyHash::equal):
(JSC::SourceCodeKeyHashTraits::isEmptyValue):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createYield):
(JSC::SyntaxChecker::createFunctionMetadata):
(JSC::SyntaxChecker::operatorStackPop):
* runtime/CodeCache.cpp:
(JSC::CodeCache::getGlobalCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):
* runtime/CodeCache.h:
(JSC::SourceCodeKey::SourceCodeKey): Deleted.
(JSC::SourceCodeKey::isHashTableDeletedValue): Deleted.
(JSC::SourceCodeKey::hash): Deleted.
(JSC::SourceCodeKey::length): Deleted.
(JSC::SourceCodeKey::isNull): Deleted.
(JSC::SourceCodeKey::string): Deleted.
(JSC::SourceCodeKey::operator==): Deleted.
(JSC::SourceCodeKeyHash::hash): Deleted.
(JSC::SourceCodeKeyHash::equal): Deleted.
(JSC::SourceCodeKeyHashTraits::isEmptyValue): Deleted.
* runtime/CommonIdentifiers.h:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/Completion.cpp:
(JSC::checkSyntax):
(JSC::checkModuleSyntax):
* runtime/Executable.cpp:
(JSC::ScriptExecutable::newCodeBlockFor):
(JSC::ProgramExecutable::checkSyntax):
* runtime/Executable.h:
* runtime/FunctionConstructor.cpp:
(JSC::constructFunction):
(JSC::constructFunctionSkippingEvalEnabledCheck):
* runtime/FunctionConstructor.h:
* runtime/GeneratorFrame.cpp: Added.
(JSC::GeneratorFrame::GeneratorFrame):
(JSC::GeneratorFrame::finishCreation):
(JSC::GeneratorFrame::createStructure):
(JSC::GeneratorFrame::create):
(JSC::GeneratorFrame::save):
(JSC::GeneratorFrame::resume):
(JSC::GeneratorFrame::visitChildren):
* runtime/GeneratorFrame.h: Added.
(JSC::GeneratorFrame::locals):
(JSC::GeneratorFrame::localAt):
(JSC::GeneratorFrame::offsetOfLocals):
(JSC::GeneratorFrame::allocationSizeForLocals):
* runtime/GeneratorFunctionConstructor.cpp: Added.
(JSC::GeneratorFunctionConstructor::GeneratorFunctionConstructor):
(JSC::GeneratorFunctionConstructor::finishCreation):
(JSC::callGeneratorFunctionConstructor):
(JSC::constructGeneratorFunctionConstructor):
(JSC::GeneratorFunctionConstructor::getCallData):
(JSC::GeneratorFunctionConstructor::getConstructData):
* runtime/GeneratorFunctionConstructor.h: Added.
(JSC::GeneratorFunctionConstructor::create):
(JSC::GeneratorFunctionConstructor::createStructure):
* runtime/GeneratorFunctionPrototype.cpp: Added.
(JSC::GeneratorFunctionPrototype::GeneratorFunctionPrototype):
(JSC::GeneratorFunctionPrototype::finishCreation):
* runtime/GeneratorFunctionPrototype.h: Added.
(JSC::GeneratorFunctionPrototype::create):
(JSC::GeneratorFunctionPrototype::createStructure):
* runtime/GeneratorPrototype.cpp: Copied from Source/JavaScriptCore/ftl/FTLForOSREntryJITCode.cpp.
(JSC::GeneratorPrototype::finishCreation):
(JSC::GeneratorPrototype::getOwnPropertySlot):
* runtime/GeneratorPrototype.h: Copied from Source/JavaScriptCore/ftl/FTLForOSREntryJITCode.cpp.
(JSC::GeneratorPrototype::create):
(JSC::GeneratorPrototype::createStructure):
(JSC::GeneratorPrototype::GeneratorPrototype):
* runtime/GeneratorThisMode.h: Added.
* runtime/JSFunction.cpp:
(JSC::JSFunction::getOwnPropertySlot):
* runtime/JSGeneratorFunction.cpp: Added.
(JSC::JSGeneratorFunction::JSGeneratorFunction):
(JSC::JSGeneratorFunction::createImpl):
(JSC::JSGeneratorFunction::create):
(JSC::JSGeneratorFunction::createWithInvalidatedReallocationWatchpoint):
* runtime/JSGeneratorFunction.h: Added.
(JSC::JSGeneratorFunction::allocationSize):
(JSC::JSGeneratorFunction::createStructure):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::generatorFunctionPrototype):
(JSC::JSGlobalObject::generatorPrototype):
(JSC::JSGlobalObject::generatorFunctionStructure):
* runtime/ModuleLoaderObject.cpp:
(JSC::moduleLoaderObjectParseModule):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
* tests/es6.yaml:
* tests/es6/generators_yield_star_generic_iterables.js:
(iterator.next):
(iterable.Symbol.iterator):
(__createIterableObject):
* tests/es6/generators_yield_star_instances_of_iterables.js:
(iterator.next):
(iterable.Symbol.iterator):
(__createIterableObject):
* tests/es6/generators_yield_star_iterator_closing.js:
(iterator.next):
(iterable.Symbol.iterator):
(__createIterableObject):
* tests/es6/generators_yield_star_iterator_closing_via_throw.js:
(iterator.next):
(iterable.Symbol.iterator):
(__createIterableObject):
* tests/stress/generator-arguments-from-function.js: Added.
(shouldBe):
(test):
* tests/stress/generator-arguments.js: Added.
(shouldBe):
(g1):
* tests/stress/generator-class-methods-syntax.js: Added.
(testSyntax):
(testSyntaxError):
(testSyntaxError.Cocoa):
(testSyntax.Cocoa.prototype.ok):
(testSyntax.Cocoa):
(testSyntax.Cocoa.ok):
* tests/stress/generator-class-methods.js: Added.
(shouldBe):
(prototype.gen):
(staticGen):
(shouldBe.g.next):
* tests/stress/generator-eval-this.js: Added.
(shouldBe):
(shouldThrow):
(B):
(A):
(C.prototype.generator):
(C):
(TypeError):
* tests/stress/generator-function-constructor.js: Added.
(shouldBe):
(generatorFunctionConstructor):
* tests/stress/generator-function-name.js: Added.
(shouldBe):
(ok):
* tests/stress/generator-methods-with-non-generator.js: Added.
(shouldThrow):
* tests/stress/generator-relations.js: Added.
(shouldBe):
(generatorFunction):
* tests/stress/generator-return-before-first-call.js: Added.
(shouldBe):
(shouldBeIteratorResult):
* tests/stress/generator-return.js: Added.
(shouldBe):
(shouldBeIteratorResult):
* tests/stress/generator-this.js: Added.
(shouldBe):
(shouldThrow):
(gen):
(shouldBe.g.next):
* tests/stress/generator-throw-before-first-call.js: Added.
(unreachable):
(gen):
(catch):
* tests/stress/generator-throw.js: Added.
(shouldBe):
(shouldBeIteratorResult):
* tests/stress/generator-with-new-target.js: Added.
(shouldBe):
(gen):
* tests/stress/generator-with-super.js: Added.
(shouldThrow):
(test):
(B.prototype.gen):
(B):
(A.prototype.gen):
(A):
* tests/stress/generator-yield-star.js: Added.
(shouldBe):
(shouldThrow):
(prototype.call):
(Arrays):
(Arrays.prototype.Symbol.iterator):
(Iterator.prototype.next):
(Iterator.prototype.string_appeared_here):
(Iterator.prototype.Symbol.iterator):
(Iterator):
(gen):
Source/WebCore:
* Configurations/FeatureDefines.xcconfig:
Source/WebKit/mac:
* Configurations/FeatureDefines.xcconfig:
Source/WebKit2:
* Configurations/FeatureDefines.xcconfig:
Source/WTF:
* wtf/FastBitVector.h:
(WTF::FastBitVector::forEachSetBit):
* wtf/FeatureDefines.h:
Tools:
* Scripts/webkitperl/FeatureList.pm:
WebKitLibraries:
* win/tools/vsprops/FeatureDefines.props:
* win/tools/vsprops/FeatureDefinesCairo.props:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@192937 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/tests/stress/generator-arguments-from-function.js b/Source/JavaScriptCore/tests/stress/generator-arguments-from-function.js
new file mode 100644
index 0000000..7ca5879
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-arguments-from-function.js
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: ${String(actual)}`);
+}
+
+function *gen(a, b, c)
+{
+ function test()
+ {
+ return arguments;
+ }
+
+ return test;
+}
+
+let g = gen(1, 2, 3);
+let {value: func} = g.next();
+shouldBe(func().length, 0);
diff --git a/Source/JavaScriptCore/tests/stress/generator-arguments.js b/Source/JavaScriptCore/tests/stress/generator-arguments.js
new file mode 100644
index 0000000..b26478f
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-arguments.js
@@ -0,0 +1,107 @@
+function shouldBe(actual, expected)
+{
+ if (actual !== expected)
+ throw new Error(`bad value: ${String(actual)}`);
+}
+
+(function () {
+ function *g1(a, b, c)
+ {
+ yield arguments;
+ yield arguments;
+ }
+
+ var g = g1(0, 1, 2);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+
+ function *g2(a, b, c)
+ {
+ yield arguments;
+ yield arguments;
+ a = yield a;
+ yield arguments;
+ b = yield b;
+ yield arguments;
+ c = yield c;
+ yield arguments;
+ }
+ var g = g2(0, 1, 2);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(g.next().value, 0);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":42,"1":1,"2":2}`);
+ shouldBe(g.next().value, 1);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":42,"1":42,"2":2}`);
+ shouldBe(g.next().value, 2);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":42,"1":42,"2":42}`);
+}());
+
+(function () {
+ function *g1(a, b, c)
+ {
+ "use strict";
+ yield arguments;
+ yield arguments;
+ }
+
+ var g = g1(0, 1, 2);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+
+ function *g2(a, b, c)
+ {
+ "use strict";
+ yield arguments;
+ yield arguments;
+ a = yield a;
+ yield arguments;
+ b = yield b;
+ yield arguments;
+ c = yield c;
+ yield arguments;
+ }
+ var g = g2(0, 1, 2);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(g.next().value, 0);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(g.next().value, 1);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(g.next().value, 2);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":0,"1":1,"2":2}`);
+}());
+
+(function () {
+ "use strict";
+ function *g1(a, b, c)
+ {
+ yield arguments;
+ yield arguments;
+ }
+
+ var g = g1(0, 1, 2);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+
+ function *g2(a, b, c)
+ {
+ yield arguments;
+ yield arguments;
+ a = yield a;
+ yield arguments;
+ b = yield b;
+ yield arguments;
+ c = yield c;
+ yield arguments;
+ }
+ var g = g2(0, 1, 2);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(JSON.stringify(g.next().value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(g.next().value, 0);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(g.next().value, 1);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":0,"1":1,"2":2}`);
+ shouldBe(g.next().value, 2);
+ shouldBe(JSON.stringify(g.next(42).value), `{"0":0,"1":1,"2":2}`);
+}());
diff --git a/Source/JavaScriptCore/tests/stress/generator-class-methods-syntax.js b/Source/JavaScriptCore/tests/stress/generator-class-methods-syntax.js
new file mode 100644
index 0000000..2465ceb
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-class-methods-syntax.js
@@ -0,0 +1,48 @@
+function testSyntax(script) {
+ try {
+ eval(script);
+ } catch (error) {
+ if (error instanceof SyntaxError)
+ throw new Error("Bad error: " + String(error));
+ }
+}
+
+function testSyntaxError(script, message) {
+ var error = null;
+ try {
+ eval(script);
+ } catch (e) {
+ error = e;
+ }
+ if (!error)
+ throw new Error("Expected syntax error not thrown");
+
+ if (String(error) !== message)
+ throw new Error("Bad error: " + String(error));
+}
+
+testSyntaxError(`
+class Cocoa {
+ *constructor()
+ {
+ }
+}
+`, `SyntaxError: Cannot declare a generator named 'constructor'.`);
+
+testSyntax(`
+class Cocoa {
+ *ok()
+ {
+ yield 42;
+ }
+}
+`);
+
+testSyntax(`
+class Cocoa {
+ static *ok()
+ {
+ yield 42;
+ }
+}
+`);
diff --git a/Source/JavaScriptCore/tests/stress/generator-class-methods.js b/Source/JavaScriptCore/tests/stress/generator-class-methods.js
new file mode 100644
index 0000000..21f5a54
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-class-methods.js
@@ -0,0 +1,62 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+class A {
+ *gen()
+ {
+ yield this;
+ yield this;
+ return 42;
+ }
+
+ static *staticGen()
+ {
+ yield this;
+ yield this;
+ return 42;
+ }
+}
+{
+ let a = new A();
+ let g = a.gen();
+ shouldBe(g.next().value, a);
+ shouldBe(g.next().value, a);
+ shouldBe(g.next().value, 42);
+ shouldBe(g.next().done, true);
+}
+{
+ let a = new A();
+ shouldThrow(() => {
+ let g = new a.gen();
+ }, `TypeError: function is not a constructor (evaluating 'new a.gen()')`);
+}
+
+{
+ let g = A.staticGen();
+ shouldBe(g.next().value, A);
+ shouldBe(g.next().value, A);
+ shouldBe(g.next().value, 42);
+ shouldBe(g.next().done, true);
+}
+{
+ shouldThrow(() => {
+ let g = new A.staticGen();
+ }, `TypeError: function is not a constructor (evaluating 'new A.staticGen()')`);
+}
diff --git a/Source/JavaScriptCore/tests/stress/generator-eval-this.js b/Source/JavaScriptCore/tests/stress/generator-eval-this.js
new file mode 100644
index 0000000..c9bd78d
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-eval-this.js
@@ -0,0 +1,65 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+function *gen() {
+ yield eval("this");
+}
+
+var g = new gen();
+shouldThrow(() => {
+ g.next().value;
+}, `ReferenceError: Cannot access uninitialized variable.`);
+
+class B { }
+
+(function() {
+ eval('this');
+ eval('this');
+}());
+
+class A extends B {
+ constructor()
+ {
+ return eval('this');
+ }
+}
+
+shouldThrow(() => {
+ new A();
+}, `ReferenceError: Cannot access uninitialized variable.`);
+
+class C {
+ *generator()
+ {
+ yield eval('this');
+ }
+}
+
+shouldThrow(() => {
+ let c = new C();
+ let g = new c.generator();
+ g.next();
+}, `TypeError: function is not a constructor (evaluating 'new c.generator()')`);
+
+(function () {
+ let c = new C();
+ let g = c.generator();
+ shouldBe(g.next().value, c);
+}());
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-constructor.js b/Source/JavaScriptCore/tests/stress/generator-function-constructor.js
new file mode 100644
index 0000000..2696ae5
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-function-constructor.js
@@ -0,0 +1,9 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+var global = (new Function("return this"))();
+shouldBe(typeof global.GeneratorFunction, 'undefined');
+var generatorFunctionConstructor = (function *() { }).constructor;
+shouldBe(generatorFunctionConstructor.__proto__, Function);
+shouldBe(generatorFunctionConstructor.prototype.constructor, generatorFunctionConstructor);
diff --git a/Source/JavaScriptCore/tests/stress/generator-function-name.js b/Source/JavaScriptCore/tests/stress/generator-function-name.js
new file mode 100644
index 0000000..6f7736c
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-function-name.js
@@ -0,0 +1,12 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+var ok = function *generator()
+{
+ yield generator;
+};
+
+var g = ok();
+shouldBe(g.next().value, ok);
diff --git a/Source/JavaScriptCore/tests/stress/generator-methods-with-non-generator.js b/Source/JavaScriptCore/tests/stress/generator-methods-with-non-generator.js
new file mode 100644
index 0000000..4561d93
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-methods-with-non-generator.js
@@ -0,0 +1,31 @@
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+function *gen() {
+}
+var g = gen();
+
+shouldThrow(() => {
+ g.next.call({});
+}, `TypeError: |this| should be a generator`);
+
+
+shouldThrow(() => {
+ g.throw.call({});
+}, `TypeError: |this| should be a generator`);
+
+shouldThrow(() => {
+ g.return.call({});
+}, `TypeError: |this| should be a generator`);
diff --git a/Source/JavaScriptCore/tests/stress/generator-relations.js b/Source/JavaScriptCore/tests/stress/generator-relations.js
new file mode 100644
index 0000000..efa4dc8
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-relations.js
@@ -0,0 +1,30 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function *generatorFunction() {
+}
+let generator = generatorFunction();
+
+shouldBe(generator instanceof generatorFunction, true);
+shouldBe(typeof generator.__proto__, 'object');
+shouldBe(generator.__proto__, generatorFunction.prototype);
+
+let GeneratorPrototype = generator.__proto__.__proto__;
+
+let GeneratorFunctionPrototype = generatorFunction.__proto__;
+let GeneratorFunction = generatorFunction.__proto__.constructor;
+shouldBe(GeneratorFunction.prototype, GeneratorFunctionPrototype);
+shouldBe(generatorFunction instanceof GeneratorFunction, true);
+shouldBe(GeneratorFunction.__proto__, Function);
+shouldBe(GeneratorFunctionPrototype.__proto__, Function.prototype);
+
+shouldBe(GeneratorFunctionPrototype.prototype, GeneratorPrototype);
+shouldBe(GeneratorPrototype.constructor, GeneratorFunctionPrototype);
+
+let arrayIterator = [][Symbol.iterator]();
+let ArrayIteratorPrototype = arrayIterator.__proto__;
+let IteratorPrototype = ArrayIteratorPrototype.__proto__;
+
+shouldBe(IteratorPrototype, GeneratorPrototype.__proto__);
diff --git a/Source/JavaScriptCore/tests/stress/generator-return-before-first-call.js b/Source/JavaScriptCore/tests/stress/generator-return-before-first-call.js
new file mode 100644
index 0000000..ce6debc
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-return-before-first-call.js
@@ -0,0 +1,23 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function shouldBeIteratorResult(actual, { value, done })
+{
+ shouldBe(actual.value, value);
+ shouldBe(actual.done, done);
+}
+
+function unreachable()
+{
+ throw new Error("NG");
+}
+
+function *gen()
+{
+ unreachable();
+}
+
+var g = gen();
+shouldBeIteratorResult(g.return("Hello"), { value: "Hello", done: true });
diff --git a/Source/JavaScriptCore/tests/stress/generator-return.js b/Source/JavaScriptCore/tests/stress/generator-return.js
new file mode 100644
index 0000000..7f569a0
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-return.js
@@ -0,0 +1,133 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldBeIteratorResult(actual, { value, done })
+{
+ shouldBe(actual.value, value);
+ shouldBe(actual.done, done);
+}
+
+function unreachable()
+{
+ throw new Error('unreachable');
+}
+
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+(function () {
+ function *gen() {
+ yield yield 20;
+ yield 42;
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(0), { value: 20, done: false });
+ shouldBeIteratorResult(g.return(20), { value: 20, done: true });
+ shouldBeIteratorResult(g.return(20), { value: 20, done: true });
+ shouldBeIteratorResult(g.next(42), { value: undefined, done: true });
+ }
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ shouldBeIteratorResult(g.next(42), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ }
+}());
+
+(function () {
+ function *gen() {
+ return 42;
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: 42, done: true });
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ }
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ }
+}());
+
+(function () {
+ function *gen() {
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ }
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ }
+}());
+
+(function () {
+ function *gen() {
+ try {
+ yield 42;
+ } finally {
+ return 400;
+ }
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: 42, done: false });
+ shouldBeIteratorResult(g.return(0), { value: 400, done: true });
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ }
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ }
+}());
+
+
+(function () {
+ function *gen() {
+ try {
+ yield 42;
+ } finally {
+ throw new Error("thrown");
+ }
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: 42, done: false });
+ shouldThrow(() => g.return(0), `Error: thrown`);
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ }
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ }
+}());
diff --git a/Source/JavaScriptCore/tests/stress/generator-this.js b/Source/JavaScriptCore/tests/stress/generator-this.js
new file mode 100644
index 0000000..880af6e
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-this.js
@@ -0,0 +1,67 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+
+var global = new Function('return this')();
+
+(function () {
+ function *gen() {
+ yield this;
+ }
+
+ {
+ let g = gen();
+ shouldBe(g.next().value, global);
+ }
+ {
+ let g = new gen();
+ shouldThrow(() => {
+ g.next();
+ }, `ReferenceError: Cannot access uninitialized variable.`);
+ }
+ {
+ let thisObject = {};
+ let g = gen.call(thisObject);
+ shouldBe(g.next().value, thisObject);
+ }
+}());
+
+(function () {
+ function *gen() {
+ "use strict";
+ yield this;
+ }
+
+ {
+ let g = gen();
+ shouldBe(g.next().value, undefined);
+ }
+ {
+ let g = new gen();
+ shouldThrow(() => {
+ g.next();
+ }, `ReferenceError: Cannot access uninitialized variable.`);
+ }
+ {
+ let thisObject = {};
+ let g = gen.call(thisObject);
+ shouldBe(g.next().value, thisObject);
+ }
+}());
diff --git a/Source/JavaScriptCore/tests/stress/generator-throw-before-first-call.js b/Source/JavaScriptCore/tests/stress/generator-throw-before-first-call.js
new file mode 100644
index 0000000..e46cb4f
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-throw-before-first-call.js
@@ -0,0 +1,20 @@
+function unreachable()
+{
+ throw new Error("NG");
+}
+
+function *gen()
+{
+ unreachable();
+}
+
+var g = gen();
+var error = new Error("OK");
+var thrown = null;
+try {
+ g.throw(error);
+} catch (e) {
+ thrown = e;
+}
+if (thrown !== error)
+ unreachable();
diff --git a/Source/JavaScriptCore/tests/stress/generator-throw.js b/Source/JavaScriptCore/tests/stress/generator-throw.js
new file mode 100644
index 0000000..ce2269f
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-throw.js
@@ -0,0 +1,132 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldBeIteratorResult(actual, { value, done })
+{
+ shouldBe(actual.value, value);
+ shouldBe(actual.done, done);
+}
+
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+class CallSite {
+ constructor()
+ {
+ this.count = 0;
+ }
+
+ call()
+ {
+ return this.count++;
+ }
+}
+
+(function () {
+ function *gen() {
+ yield yield 20;
+ yield 42;
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(0), { value: 20, done: false });
+ shouldThrow(() => g.throw(20), `20`);
+ shouldThrow(() => g.throw(20), `20`);
+ shouldBeIteratorResult(g.next(42), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ }
+ {
+ let g = gen();
+ shouldThrow(() => g.throw(42), `42`);
+ shouldBeIteratorResult(g.next(42), { value: undefined, done: true });
+ shouldBeIteratorResult(g.return(42), { value: 42, done: true });
+ shouldThrow(() => g.throw(42), `42`);
+ }
+}());
+
+(function () {
+ function *gen() {
+ return 42;
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: 42, done: true });
+ shouldThrow(() => g.throw(0), `0`);
+ }
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldThrow(() => g.throw(42), `42`);
+ }
+}());
+
+(function () {
+ function *gen() {
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldThrow(() => g.throw(0), `0`);
+ }
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.return(0), { value: 0, done: true });
+ shouldBeIteratorResult(g.next(), { value: undefined, done: true });
+ shouldThrow(() => g.throw(42), `42`);
+ }
+}());
+
+(function () {
+ let site = new CallSite();
+ function *gen() {
+ try {
+ yield 42;
+ } catch (e) {
+ shouldBe(e, 0);
+ site.call();
+ }
+ return 42;
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: 42, done: false });
+ shouldBeIteratorResult(g.throw(0), { value: 42, done: true });
+ shouldBe(site.count, 1);
+ }
+}());
+
+(function () {
+ function *gen() {
+ try {
+ yield 42;
+ } finally {
+ return 42;
+ }
+ }
+
+ {
+ let g = gen();
+ shouldBeIteratorResult(g.next(), { value: 42, done: false });
+ shouldBeIteratorResult(g.throw(0), { value: 42, done: true });
+ shouldThrow(() => g.throw(0), `0`);
+ }
+}());
diff --git a/Source/JavaScriptCore/tests/stress/generator-with-new-target.js b/Source/JavaScriptCore/tests/stress/generator-with-new-target.js
new file mode 100644
index 0000000..0d3b2df
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-with-new-target.js
@@ -0,0 +1,15 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function *gen()
+{
+ yield new.target;
+}
+
+var g = gen();
+shouldBe(g.next().value, undefined);
+
+var g2 = new gen();
+shouldBe(g.next().value, undefined);
diff --git a/Source/JavaScriptCore/tests/stress/generator-with-super.js b/Source/JavaScriptCore/tests/stress/generator-with-super.js
new file mode 100644
index 0000000..9e53c79
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-with-super.js
@@ -0,0 +1,82 @@
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+(function () {
+ function test() {
+ return eval('super');
+ }
+
+ shouldThrow(() => test(), `SyntaxError: super is only valid inside functions.`);
+}());
+
+(function () {
+ class B {
+ gen() {
+ return 42;
+ }
+ }
+
+ class A extends B {
+ *gen() {
+ return eval('super.gen()');
+ }
+ }
+
+ let a = new A();
+ shouldThrow(() => {
+ a.gen().next();
+ }, `SyntaxError: super is only valid inside functions.`);
+}());
+
+(function () {
+ class B {
+ gen() {
+ return 42;
+ }
+ }
+
+ class A extends B {
+ *gen() {
+ yield super.gen();
+ }
+ }
+
+ let a = new A();
+ shouldBe(a.gen().next().value, 42);
+}());
+
+(function () {
+ class B {
+ gen() {
+ return 42;
+ }
+ }
+
+ class A extends B {
+ *gen() {
+ yield super.gen();
+ }
+ }
+
+ let a = new A();
+ shouldThrow(() => {
+ new a.gen();
+ }, `TypeError: function is not a constructor (evaluating 'new a.gen()')`);
+}());
diff --git a/Source/JavaScriptCore/tests/stress/generator-yield-star.js b/Source/JavaScriptCore/tests/stress/generator-yield-star.js
new file mode 100644
index 0000000..45abefa
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/generator-yield-star.js
@@ -0,0 +1,326 @@
+function shouldBe(actual, expected) {
+ if (actual !== expected)
+ throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, errorMessage) {
+ var errorThrown = false;
+ var error = null;
+ try {
+ func();
+ } catch (e) {
+ errorThrown = true;
+ error = e;
+ }
+ if (!errorThrown)
+ throw new Error('not thrown');
+ if (String(error) !== errorMessage)
+ throw new Error(`bad error: ${String(error)}`);
+}
+
+class CallSite {
+ constructor()
+ {
+ this.count = 0;
+ }
+
+ call()
+ {
+ return this.count++;
+ }
+}
+
+(function () {
+ class Arrays {
+ constructor()
+ {
+ this.first = [ 0, 1, 2, 3 ];
+ this.second = [ 4, 5, 6, 7 ];
+ }
+
+ *[Symbol.iterator]()
+ {
+ yield * this.first;
+ yield * this.second;
+ }
+ }
+
+ var arrays = new Arrays;
+ let i = 0;
+ for (let value of arrays)
+ shouldBe(i++, value);
+}());
+
+(function () {
+ // throw should be propagated.
+ let c1 = new CallSite;
+ class Iterator {
+ next(value)
+ {
+ return { value, done: false };
+ }
+
+ 'throw'(value) {
+ shouldBe(value, 42);
+ c1.call();
+ throw new Error("OK");
+ }
+
+ [Symbol.iterator]()
+ {
+ return this;
+ }
+ }
+
+ function *gen()
+ {
+ let iter = new Iterator();
+ yield * iter;
+ }
+
+ let g = gen();
+ shouldBe(g.next(0).value, undefined);
+ shouldBe(g.next(1).value, 1);
+ shouldBe(g.next(2).value, 2);
+ shouldThrow(() => {
+ g.throw(42);
+ }, `Error: OK`);
+ shouldThrow(() => {
+ g.throw(44);
+ }, `44`);
+ shouldBe(c1.count, 1);
+}());
+
+(function () {
+ // No `throw` method.
+ let c1 = new CallSite;
+ class Iterator {
+ next(value)
+ {
+ return { value, done: false };
+ }
+
+ 'return'(value)
+ {
+ shouldBe(value, undefined);
+ c1.call();
+ return { value, done: true };
+ }
+
+ [Symbol.iterator]()
+ {
+ return this;
+ }
+ }
+
+ function *gen()
+ {
+ let iter = new Iterator();
+ yield * iter;
+ }
+
+ let g = gen();
+ shouldBe(g.next(0).value, undefined);
+ shouldBe(g.next(1).value, 1);
+ shouldBe(g.next(2).value, 2);
+ shouldThrow(() => {
+ g.throw(42);
+ }, `TypeError: Delegated generator does not have a 'throw' method.`);
+ shouldThrow(() => {
+ g.throw(44);
+ }, `44`);
+ shouldBe(c1.count, 1);
+}());
+
+(function () {
+ // No `throw` method, `return` returns an incorrect result.
+ let c1 = new CallSite;
+ class Iterator {
+ next(value)
+ {
+ return { value, done: false };
+ }
+
+ 'return'(value)
+ {
+ shouldBe(value, undefined);
+ c1.call();
+ }
+
+ [Symbol.iterator]()
+ {
+ return this;
+ }
+ }
+
+ function *gen()
+ {
+ let iter = new Iterator();
+ yield * iter;
+ }
+
+ let g = gen();
+ shouldBe(g.next(0).value, undefined);
+ shouldBe(g.next(1).value, 1);
+ shouldBe(g.next(2).value, 2);
+ shouldThrow(() => {
+ g.throw(42);
+ }, `TypeError: Iterator result interface is not an object.`);
+ shouldThrow(() => {
+ g.throw(44);
+ }, `44`);
+ shouldBe(c1.count, 1);
+}());
+
+(function () {
+ // No `throw` method, No `return` method.
+ class Iterator {
+ next(value)
+ {
+ return { value, done: false };
+ }
+
+ [Symbol.iterator]()
+ {
+ return this;
+ }
+ }
+
+ function *gen()
+ {
+ let iter = new Iterator();
+ yield * iter;
+ }
+
+ let g = gen();
+ shouldBe(g.next(0).value, undefined);
+ shouldBe(g.next(1).value, 1);
+ shouldBe(g.next(2).value, 2);
+ shouldThrow(() => {
+ g.throw(42);
+ }, `TypeError: Delegated generator does not have a 'throw' method.`);
+ shouldThrow(() => {
+ g.throw(44);
+ }, `44`);
+}());
+
+
+(function () {
+ // `throw` does not throw. Not returns a object.
+ class Iterator {
+ next(value)
+ {
+ return { value, done: false };
+ }
+
+ 'throw'(value)
+ {
+ }
+
+ [Symbol.iterator]()
+ {
+ return this;
+ }
+ }
+
+ function *gen()
+ {
+ let iter = new Iterator();
+ yield * iter;
+ }
+
+ let g = gen();
+ shouldBe(g.next(0).value, undefined);
+ shouldBe(g.next(1).value, 1);
+ shouldBe(g.next(2).value, 2);
+ shouldThrow(() => {
+ g.throw(42);
+ }, `TypeError: Iterator result interface is not an object.`);
+ shouldThrow(() => {
+ g.throw(44);
+ }, `44`);
+}());
+
+(function () {
+ // `throw` does not throw. If returned iterator result is marked as done, it becomes `return`.
+ class Iterator {
+ next(value)
+ {
+ return { value, done: false };
+ }
+
+ 'throw'(value)
+ {
+ return { value, done: true };
+ }
+
+ [Symbol.iterator]()
+ {
+ return this;
+ }
+ }
+
+ function *gen()
+ {
+ let iter = new Iterator();
+ yield * iter;
+ }
+
+ let g = gen();
+ shouldBe(g.next(0).value, undefined);
+ shouldBe(g.next(1).value, 1);
+ shouldBe(g.next(2).value, 2);
+ shouldBe(g.throw(42).value, 42);
+ shouldThrow(() => {
+ g.throw(44);
+ }, `44`);
+}());
+
+(function () {
+ // `return` returns done: false.
+ class Iterator {
+ next(value)
+ {
+ return { value, done: false };
+ }
+
+ 'return'(value)
+ {
+ return { value, done: false };
+ }
+
+ [Symbol.iterator]()
+ {
+ return this;
+ }
+ }
+
+ function *gen()
+ {
+ let iter = new Iterator();
+ let result = yield * iter;
+ yield result;
+ yield 42;
+ }
+
+ let g = gen();
+ shouldBe(g.next(0).value, undefined);
+ shouldBe(g.next(1).value, 1);
+ shouldBe(g.next(2).value, 2);
+ shouldBe(g.return(42).value, 42);
+ shouldBe(g.return(42).done, false);
+}());
+
+(function () {
+ function *gen()
+ {
+ let result = yield * [ 0, 1, 2 ];
+ yield result;
+ }
+
+ let g = gen();
+ shouldBe(g.next().value, 0);
+ shouldBe(g.next().value, 1);
+ shouldBe(g.next().value, 2);
+ shouldBe(g.next().value, undefined);
+}());