Upgrade ES6 Iterator interfaces
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Mar 2015 14:57:17 +0000 (14:57 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Mar 2015 14:57:17 +0000 (14:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=141351

Reviewed by Filip Pizlo.

This patch upgrades the exising ES6 iterator to align the latest spec.
In the latest spec,
1. `Iterator.next` returns object that implements IteratorResult interface { value: value, done, boolean }.
2. `Iterator.return` is introduced. When the iteration is terminated by the abrupt completion,
it is called to close iterator state.
3. Iterator.next of Array is moved from an iterator object to `%ArrayIteratorPrototype%`.

To upgrade it, we changes the bytecode that represents for-of loops.
And to embody the efficient iteration with an iterator object,
we implemented %ArrayIteratorPrototype%.next in JavaScript and
it is located in builtins/ArrayIterator.prototype.js.
Implementing it in JavaScript encourages inlining and
utilizes escape analysis for an iterator result object in DFG JIT.
And we dropped the intrinsic version of %ArrayIteratorPrototype%.next.

And we introduced IteratorOperations that is defined in the spec.
It aligns the iteration in the runtime to the latest spec.
Currently, Promise.all and Promise.race uses an iterable object.
However, Promise.all and Promise.race implementation is also based on the old spec.
Subsequent patches will upgrade it.

* CMakeLists.txt:
* DerivedSources.make:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* builtins/ArrayIterator.prototype.js: Copied from Source/JavaScriptCore/runtime/ArrayIteratorPrototype.h.
(next):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitReturn):
(JSC::BytecodeGenerator::emitThrowTypeError):
(JSC::BytecodeGenerator::emitEnumeration):
(JSC::BytecodeGenerator::emitIsObject):
(JSC::BytecodeGenerator::emitIsUndefined):
* bytecompiler/BytecodeGenerator.h:
* jit/ThunkGenerators.cpp:
(JSC::arrayIteratorNextThunkGenerator): Deleted.
(JSC::arrayIteratorNextKeyThunkGenerator): Deleted.
(JSC::arrayIteratorNextValueThunkGenerator): Deleted.
* jit/ThunkGenerators.h:
* runtime/ArgumentsIteratorPrototype.cpp:
(JSC::ArgumentsIteratorPrototype::finishCreation):
(JSC::argumentsIteratorPrototypeFuncNext):
* runtime/ArrayIteratorPrototype.cpp:
(JSC::ArrayIteratorPrototype::finishCreation):
(JSC::ArrayIteratorPrototype::getOwnPropertySlot):
(JSC::arrayIteratorProtoFuncIterator):
(JSC::arrayIteratorPrototypeIterate): Deleted.
* runtime/ArrayIteratorPrototype.h:
* runtime/CommonIdentifiers.h:
* runtime/Intrinsic.h:
* runtime/IteratorOperations.cpp: Added.
(JSC::iteratorNext):
(JSC::iteratorValue):
(JSC::iteratorComplete):
(JSC::iteratorStep):
(JSC::iteratorClose):
(JSC::createIterResultObject):
* runtime/IteratorOperations.h: Copied from Source/JavaScriptCore/runtime/ArrayIteratorPrototype.cpp.
* runtime/JSArrayIterator.cpp:
(JSC::JSArrayIterator::finishCreation):
(JSC::JSArrayIterator::visitChildren): Deleted.
(JSC::createIteratorResult): Deleted.
(JSC::arrayIteratorNext): Deleted.
(JSC::arrayIteratorNextKey): Deleted.
(JSC::arrayIteratorNextValue): Deleted.
(JSC::arrayIteratorNextGeneric): Deleted.
* runtime/JSArrayIterator.h:
(JSC::JSArrayIterator::JSArrayIterator):
(JSC::JSArrayIterator::iterationKind): Deleted.
(JSC::JSArrayIterator::iteratedObject): Deleted.
(JSC::JSArrayIterator::nextIndex): Deleted.
(JSC::JSArrayIterator::setNextIndex): Deleted.
(JSC::JSArrayIterator::finish): Deleted.
(JSC::JSArrayIterator::offsetOfIterationKind): Deleted.
(JSC::JSArrayIterator::offsetOfIteratedObject): Deleted.
(JSC::JSArrayIterator::offsetOfNextIndex): Deleted.
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSPromiseConstructor.cpp:
(JSC::performPromiseRaceLoop):
(JSC::JSPromiseConstructorFuncRace):
(JSC::performPromiseAll):
(JSC::JSPromiseConstructorFuncAll):
* runtime/MapIteratorPrototype.cpp:
(JSC::MapIteratorPrototype::finishCreation):
(JSC::MapIteratorPrototypeFuncNext):
* runtime/SetIteratorPrototype.cpp:
(JSC::SetIteratorPrototype::finishCreation):
(JSC::SetIteratorPrototypeFuncNext):
* runtime/VM.cpp:
(JSC::thunkGeneratorForIntrinsic):
* tests/stress/array-iterators-next-with-call.js: Added.
(increment):
(for):
* tests/stress/array-iterators-next.js: Added.

Revive the older Array iterator tests that manually call 'next' method.

* tests/stress/custom-iterators.js: Added.
(iter.next):
(iter.Symbol.iterator):
(iter.return):
(iter.get next):
(iter.get return):
(iteratorInterfaceErrorTest.iter.next):
(iteratorInterfaceErrorTest.iter.Symbol.iterator):
(iteratorInterfaceErrorTest.iter.return):
(iteratorInterfaceErrorTest):
(iteratorInterfaceErrorTestReturn.iter.next):
(iteratorInterfaceErrorTestReturn.iter.Symbol.iterator):
(iteratorInterfaceErrorTestReturn.iter.return):
(iteratorInterfaceErrorTestReturn):
(iteratorInterfaceBreakTestReturn.iter.next):
(iteratorInterfaceBreakTestReturn.iter.Symbol.iterator):
(iteratorInterfaceBreakTestReturn.iter.return):
(iteratorInterfaceBreakTestReturn):

This tests the behavior of custom iterators.
'next' and 'return' of iterator work with for-of.

* tests/stress/iterators-shape.js: Added.
(iteratorShape):
(sameNextMethods):
(set var):

This tests the shape of iterators; iterators of Array have 'next' method in %ArrayIteratorPrototype%.

* tests/stress/map-iterators-next.js: Added.
(set var):
(.get if):
(otherKey):
* tests/stress/set-iterators-next.js: Added.
(otherKey):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@181077 268f45cc-cd09-0410-ab3c-d52691b4dbfc

31 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/DerivedSources.make
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/builtins/ArrayIterator.prototype.js [new file with mode: 0644]
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/jit/ThunkGenerators.cpp
Source/JavaScriptCore/jit/ThunkGenerators.h
Source/JavaScriptCore/runtime/ArgumentsIteratorPrototype.cpp
Source/JavaScriptCore/runtime/ArrayIteratorPrototype.cpp
Source/JavaScriptCore/runtime/ArrayIteratorPrototype.h
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/Intrinsic.h
Source/JavaScriptCore/runtime/IteratorOperations.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/IteratorOperations.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSArrayIterator.cpp
Source/JavaScriptCore/runtime/JSArrayIterator.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSPromiseConstructor.cpp
Source/JavaScriptCore/runtime/MapIteratorPrototype.cpp
Source/JavaScriptCore/runtime/SetIteratorPrototype.cpp
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/tests/stress/array-iterators-next-with-call.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/array-iterators-next.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/custom-iterators.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/iterators-shape.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/map-iterators-next.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/set-iterators-next.js [new file with mode: 0644]

index f9cbddc..e754c6e 100644 (file)
@@ -438,7 +438,7 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/ErrorInstance.cpp
     runtime/ErrorPrototype.cpp
     runtime/ExceptionFuzz.cpp
-    runtime/ExceptionHelpers.cpp 
+    runtime/ExceptionHelpers.cpp
     runtime/Executable.cpp
     runtime/FunctionConstructor.cpp
     runtime/FunctionExecutableDump.cpp
@@ -450,6 +450,7 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/InitializeThreading.cpp
     runtime/IntendedStructureChain.cpp
     runtime/InternalFunction.cpp
+    runtime/IteratorOperations.cpp
     runtime/JSAPIValueWrapper.cpp
     runtime/JSLexicalEnvironment.cpp
     runtime/JSArgumentsIterator.cpp
@@ -579,6 +580,7 @@ list(APPEND JavaScriptCore_SOURCES
 
 set(JavaScriptCore_LUT_FILES
     runtime/ArrayConstructor.cpp
+    runtime/ArrayIteratorPrototype.cpp
     runtime/ArrayPrototype.cpp
     runtime/BooleanPrototype.cpp
     runtime/DateConstructor.cpp
index dcfb8c7..0840036 100644 (file)
@@ -1,3 +1,145 @@
+2015-03-05  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        Upgrade ES6 Iterator interfaces
+        https://bugs.webkit.org/show_bug.cgi?id=141351
+
+        Reviewed by Filip Pizlo.
+
+        This patch upgrades the exising ES6 iterator to align the latest spec.
+        In the latest spec,
+        1. `Iterator.next` returns object that implements IteratorResult interface { value: value, done, boolean }.
+        2. `Iterator.return` is introduced. When the iteration is terminated by the abrupt completion,
+        it is called to close iterator state.
+        3. Iterator.next of Array is moved from an iterator object to `%ArrayIteratorPrototype%`.
+
+        To upgrade it, we changes the bytecode that represents for-of loops.
+        And to embody the efficient iteration with an iterator object,
+        we implemented %ArrayIteratorPrototype%.next in JavaScript and
+        it is located in builtins/ArrayIterator.prototype.js.
+        Implementing it in JavaScript encourages inlining and
+        utilizes escape analysis for an iterator result object in DFG JIT.
+        And we dropped the intrinsic version of %ArrayIteratorPrototype%.next.
+
+        And we introduced IteratorOperations that is defined in the spec.
+        It aligns the iteration in the runtime to the latest spec.
+        Currently, Promise.all and Promise.race uses an iterable object.
+        However, Promise.all and Promise.race implementation is also based on the old spec.
+        Subsequent patches will upgrade it.
+
+        * CMakeLists.txt:
+        * DerivedSources.make:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * builtins/ArrayIterator.prototype.js: Copied from Source/JavaScriptCore/runtime/ArrayIteratorPrototype.h.
+        (next):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitReturn):
+        (JSC::BytecodeGenerator::emitThrowTypeError):
+        (JSC::BytecodeGenerator::emitEnumeration):
+        (JSC::BytecodeGenerator::emitIsObject):
+        (JSC::BytecodeGenerator::emitIsUndefined):
+        * bytecompiler/BytecodeGenerator.h:
+        * jit/ThunkGenerators.cpp:
+        (JSC::arrayIteratorNextThunkGenerator): Deleted.
+        (JSC::arrayIteratorNextKeyThunkGenerator): Deleted.
+        (JSC::arrayIteratorNextValueThunkGenerator): Deleted.
+        * jit/ThunkGenerators.h:
+        * runtime/ArgumentsIteratorPrototype.cpp:
+        (JSC::ArgumentsIteratorPrototype::finishCreation):
+        (JSC::argumentsIteratorPrototypeFuncNext):
+        * runtime/ArrayIteratorPrototype.cpp:
+        (JSC::ArrayIteratorPrototype::finishCreation):
+        (JSC::ArrayIteratorPrototype::getOwnPropertySlot):
+        (JSC::arrayIteratorProtoFuncIterator):
+        (JSC::arrayIteratorPrototypeIterate): Deleted.
+        * runtime/ArrayIteratorPrototype.h:
+        * runtime/CommonIdentifiers.h:
+        * runtime/Intrinsic.h:
+        * runtime/IteratorOperations.cpp: Added.
+        (JSC::iteratorNext):
+        (JSC::iteratorValue):
+        (JSC::iteratorComplete):
+        (JSC::iteratorStep):
+        (JSC::iteratorClose):
+        (JSC::createIterResultObject):
+        * runtime/IteratorOperations.h: Copied from Source/JavaScriptCore/runtime/ArrayIteratorPrototype.cpp.
+        * runtime/JSArrayIterator.cpp:
+        (JSC::JSArrayIterator::finishCreation):
+        (JSC::JSArrayIterator::visitChildren): Deleted.
+        (JSC::createIteratorResult): Deleted.
+        (JSC::arrayIteratorNext): Deleted.
+        (JSC::arrayIteratorNextKey): Deleted.
+        (JSC::arrayIteratorNextValue): Deleted.
+        (JSC::arrayIteratorNextGeneric): Deleted.
+        * runtime/JSArrayIterator.h:
+        (JSC::JSArrayIterator::JSArrayIterator):
+        (JSC::JSArrayIterator::iterationKind): Deleted.
+        (JSC::JSArrayIterator::iteratedObject): Deleted.
+        (JSC::JSArrayIterator::nextIndex): Deleted.
+        (JSC::JSArrayIterator::setNextIndex): Deleted.
+        (JSC::JSArrayIterator::finish): Deleted.
+        (JSC::JSArrayIterator::offsetOfIterationKind): Deleted.
+        (JSC::JSArrayIterator::offsetOfIteratedObject): Deleted.
+        (JSC::JSArrayIterator::offsetOfNextIndex): Deleted.
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        * runtime/JSPromiseConstructor.cpp:
+        (JSC::performPromiseRaceLoop):
+        (JSC::JSPromiseConstructorFuncRace):
+        (JSC::performPromiseAll):
+        (JSC::JSPromiseConstructorFuncAll):
+        * runtime/MapIteratorPrototype.cpp:
+        (JSC::MapIteratorPrototype::finishCreation):
+        (JSC::MapIteratorPrototypeFuncNext):
+        * runtime/SetIteratorPrototype.cpp:
+        (JSC::SetIteratorPrototype::finishCreation):
+        (JSC::SetIteratorPrototypeFuncNext):
+        * runtime/VM.cpp:
+        (JSC::thunkGeneratorForIntrinsic):
+        * tests/stress/array-iterators-next-with-call.js: Added.
+        (increment):
+        (for):
+        * tests/stress/array-iterators-next.js: Added.
+
+        Revive the older Array iterator tests that manually call 'next' method.
+
+        * tests/stress/custom-iterators.js: Added.
+        (iter.next):
+        (iter.Symbol.iterator):
+        (iter.return):
+        (iter.get next):
+        (iter.get return):
+        (iteratorInterfaceErrorTest.iter.next):
+        (iteratorInterfaceErrorTest.iter.Symbol.iterator):
+        (iteratorInterfaceErrorTest.iter.return):
+        (iteratorInterfaceErrorTest):
+        (iteratorInterfaceErrorTestReturn.iter.next):
+        (iteratorInterfaceErrorTestReturn.iter.Symbol.iterator):
+        (iteratorInterfaceErrorTestReturn.iter.return):
+        (iteratorInterfaceErrorTestReturn):
+        (iteratorInterfaceBreakTestReturn.iter.next):
+        (iteratorInterfaceBreakTestReturn.iter.Symbol.iterator):
+        (iteratorInterfaceBreakTestReturn.iter.return):
+        (iteratorInterfaceBreakTestReturn):
+
+        This tests the behavior of custom iterators.
+        'next' and 'return' of iterator work with for-of.
+
+        * tests/stress/iterators-shape.js: Added.
+        (iteratorShape):
+        (sameNextMethods):
+        (set var):
+
+        This tests the shape of iterators; iterators of Array have 'next' method in %ArrayIteratorPrototype%.
+
+        * tests/stress/map-iterators-next.js: Added.
+        (set var):
+        (.get if):
+        (otherKey):
+        * tests/stress/set-iterators-next.js: Added.
+        (otherKey):
+
 2015-03-04  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Hide Promise with runtime flags under Cocoa JSContext API
index 4a2623e..4db09bd 100644 (file)
@@ -36,6 +36,7 @@ VPATH = \
 .PHONY : all
 all : \
     ArrayConstructor.lut.h \
+    ArrayIteratorPrototype.lut.h \
     ArrayPrototype.lut.h \
     BooleanPrototype.lut.h \
     DateConstructor.lut.h \
index 831070c..d27c3fd 100644 (file)
     <ClCompile Include="..\runtime\InitializeThreading.cpp" />
     <ClCompile Include="..\runtime\IntendedStructureChain.cpp" />
     <ClCompile Include="..\runtime\InternalFunction.cpp" />
+    <ClCompile Include="..\runtime\IteratorOperations.cpp" />
     <ClCompile Include="..\runtime\JSAPIValueWrapper.cpp" />
     <ClCompile Include="..\runtime\JSLexicalEnvironment.cpp" />
     <ClCompile Include="..\runtime\JSArgumentsIterator.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\ArrayConstructor.lut.h" />
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\ArrayIteratorPrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\ArrayPrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\BooleanPrototype.lut.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\DateConstructor.lut.h" />
     <ClInclude Include="..\runtime\IntendedStructureChain.h" />
     <ClInclude Include="..\runtime\InternalFunction.h" />
     <ClInclude Include="..\runtime\Intrinsic.h" />
+    <ClInclude Include="..\runtime\IteratorOperations.h" />
     <ClInclude Include="..\runtime\JSAPIValueWrapper.h" />
     <ClInclude Include="..\runtime\JSLexicalEnvironment.h" />
     <ClInclude Include="..\runtime\JSArgumentsIterator.h" />
index fff720c..ab62188 100644 (file)
     <ClCompile Include="..\runtime\InternalFunction.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
+    <ClCompile Include="..\runtime\IteratorOperations.cpp">
+      <Filter>runtime</Filter>
+    </ClCompile>
     <ClCompile Include="..\runtime\JSLexicalEnvironment.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
     <ClInclude Include="..\runtime\Intrinsic.h">
       <Filter>runtime</Filter>
     </ClInclude>
+    <ClInclude Include="..\runtime\IteratorOperations.h">
+      <Filter>runtime</Filter>
+    </ClInclude>
     <ClInclude Include="..\runtime\JSLexicalEnvironment.h">
       <Filter>runtime</Filter>
     </ClInclude>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\StringConstructor.lut.h">
       <Filter>Derived Sources</Filter>
     </ClInclude>
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\ArrayIteratorPrototype.lut.h">
+      <Filter>Derived Sources</Filter>
+    </ClInclude>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\ArrayPrototype.lut.h">
       <Filter>Derived Sources</Filter>
     </ClInclude>
index 3989c88..fb06abc 100644 (file)
                65C0285D1717966800351E35 /* ARMv7DOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 65C0285B1717966800351E35 /* ARMv7DOpcode.h */; };
                65FB5117184EEE7000C12B70 /* ProtoCallFrame.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 65FB5116184EE9BC00C12B70 /* ProtoCallFrame.cpp */; };
                6AD2CB4D19B9140100065719 /* DebuggerEvalEnabler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6AD2CB4C19B9140100065719 /* DebuggerEvalEnabler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               70113D4B1A8DB093003848C4 /* IteratorOperations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70113D491A8DB093003848C4 /* IteratorOperations.cpp */; };
+               70113D4C1A8DB093003848C4 /* IteratorOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 70113D4A1A8DB093003848C4 /* IteratorOperations.h */; settings = {ATTRIBUTES = (Private, ); }; };
                705B41AB1A6E501E00716757 /* Symbol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 705B41A31A6E501E00716757 /* Symbol.cpp */; };
                705B41AC1A6E501E00716757 /* Symbol.h in Headers */ = {isa = PBXBuildFile; fileRef = 705B41A41A6E501E00716757 /* Symbol.h */; settings = {ATTRIBUTES = (Private, ); }; };
                705B41AD1A6E501E00716757 /* SymbolConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 705B41A51A6E501E00716757 /* SymbolConstructor.cpp */; };
                65FB5115184EE8F800C12B70 /* ProtoCallFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProtoCallFrame.h; sourceTree = "<group>"; };
                65FB5116184EE9BC00C12B70 /* ProtoCallFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProtoCallFrame.cpp; sourceTree = "<group>"; };
                6AD2CB4C19B9140100065719 /* DebuggerEvalEnabler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebuggerEvalEnabler.h; sourceTree = "<group>"; };
+               70113D491A8DB093003848C4 /* IteratorOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IteratorOperations.cpp; sourceTree = "<group>"; };
+               70113D4A1A8DB093003848C4 /* IteratorOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IteratorOperations.h; sourceTree = "<group>"; };
                704FD35305697E6D003DBED9 /* BooleanObject.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = BooleanObject.h; sourceTree = "<group>"; tabWidth = 8; };
                705B41A31A6E501E00716757 /* Symbol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Symbol.cpp; sourceTree = "<group>"; };
                705B41A41A6E501E00716757 /* Symbol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Symbol.h; sourceTree = "<group>"; };
                                BC9BB95B0E19680600DF8855 /* InternalFunction.cpp */,
                                BC11667A0E199C05008066DD /* InternalFunction.h */,
                                86BF642A148DB2B5004DE36A /* Intrinsic.h */,
+                               70113D491A8DB093003848C4 /* IteratorOperations.cpp */,
+                               70113D4A1A8DB093003848C4 /* IteratorOperations.h */,
                                A76140CB182982CB00750624 /* JSArgumentsIterator.cpp */,
                                A76140CC182982CB00750624 /* JSArgumentsIterator.h */,
                                93ADFCE60CCBD7AC00D30B08 /* JSArray.cpp */,
                                BC11667B0E199C05008066DD /* InternalFunction.h in Headers */,
                                1429D77C0ED20D7300B89619 /* Interpreter.h in Headers */,
                                860BD801148EA6F200112B2F /* Intrinsic.h in Headers */,
+                               70113D4C1A8DB093003848C4 /* IteratorOperations.h in Headers */,
                                BC18C4130E16F5CD00B34460 /* JavaScript.h in Headers */,
                                0F2B9CF519D0BAC100B1D1B5 /* FTLExitPropertyValue.h in Headers */,
                                A503FA1A188E0FB000110F14 /* JavaScriptCallFrame.h in Headers */,
                                A78853F917972629001440E4 /* IntendedStructureChain.cpp in Sources */,
                                147F39CF107EC37600427A48 /* InternalFunction.cpp in Sources */,
                                1429D7D40ED2128200B89619 /* Interpreter.cpp in Sources */,
+                               70113D4B1A8DB093003848C4 /* IteratorOperations.cpp in Sources */,
                                A503FA19188E0FB000110F14 /* JavaScriptCallFrame.cpp in Sources */,
                                1429D92F0ED22D7000B89619 /* JIT.cpp in Sources */,
                                86A90ED00EE7D51F00AB350D /* JITArithmetic.cpp in Sources */,
diff --git a/Source/JavaScriptCore/builtins/ArrayIterator.prototype.js b/Source/JavaScriptCore/builtins/ArrayIterator.prototype.js
new file mode 100644 (file)
index 0000000..976bcb2
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function next() {
+    "use strict";
+
+    if (this == null)
+        throw new @TypeError("%ArrayIteratorPrototype%.next requires that |this| not be null or undefined");
+
+    var itemKind = this.@arrayIterationKind;
+    if (itemKind === undefined)
+        throw new @TypeError("%ArrayIteratorPrototype%.next requires that |this| be Array Iterator Instance");
+
+    var done = true;
+    var value = undefined;
+
+    var array = this.@iteratedObject;
+    if (array !== undefined) {
+        var index = this.@arrayIteratorNextIndex;
+        var length = array.length >>> 0;
+        if (index >= length) {
+            this.@iteratedObject = undefined;
+        } else {
+            this.@arrayIteratorNextIndex = index + 1;
+            done = false;
+            if (itemKind === @arrayIterationKindKey) {
+                value = index;
+            } else if (itemKind === @arrayIterationKindValue) {
+                value = array[index];
+            } else {
+                value = [ index, array[index] ];
+            }
+        }
+    }
+
+    return {
+        done: done,
+        value: value
+    };
+}
index 08178e4..5b7f92a 100644 (file)
@@ -1905,16 +1905,8 @@ RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
 
     if (isConstructor() && src->index() != m_thisRegister.index()) {
         RefPtr<Label> isObjectLabel = newLabel();
-        RefPtr<RegisterID> isObjectRegister = newTemporary();
 
-        emitOpcode(op_is_object);
-        instructions().append(isObjectRegister->index());
-        instructions().append(src->index());
-
-        size_t begin = instructions().size();
-        emitOpcode(op_jtrue);
-        instructions().append(isObjectRegister->index());
-        instructions().append(isObjectLabel->bind(begin, instructions().size()));
+        emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get());
 
         emitUnaryNoDstOp(op_ret, &m_thisRegister);
 
@@ -2352,6 +2344,13 @@ void BytecodeGenerator::emitThrowReferenceError(const String& message)
     instructions().append(true);
 }
 
+void BytecodeGenerator::emitThrowTypeError(const String& message)
+{
+    emitOpcode(op_throw_static_error);
+    instructions().append(addConstantValue(addStringConstant(Identifier(m_vm, message)))->index());
+    instructions().append(false);
+}
+
 void BytecodeGenerator::emitPushFunctionNameScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes)
 {
     emitOpcode(op_push_name_scope);
@@ -2551,28 +2550,81 @@ void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, Expressio
     emitNode(subject.get(), subjectNode);
     RefPtr<RegisterID> iterator = emitGetById(newTemporary(), subject.get(), propertyNames().iteratorPrivateName);
     {
-        CallArguments args(*this, 0);
+        CallArguments args(*this, nullptr);
         emitMove(args.thisRegister(), subject.get());
         emitCall(iterator.get(), iterator.get(), NoExpectedFunction, args, node->divot(), node->divotStart(), node->divotEnd());
     }
-    RefPtr<RegisterID> iteratorNext = emitGetById(newTemporary(), iterator.get(), propertyNames().iteratorNextPrivateName);
     RefPtr<RegisterID> value = newTemporary();
     emitLoad(value.get(), jsUndefined());
     
     emitJump(scope->continueTarget());
     
     RefPtr<Label> loopStart = newLabel();
+    RefPtr<Label> iteratorDone = newLabel();
     emitLabel(loopStart.get());
     emitLoopHint();
+
+    RefPtr<Label> tryStartLabel = newLabel();
+    emitLabel(tryStartLabel.get());
+    TryData* tryData = pushTry(tryStartLabel.get());
     callBack(*this, value.get());
+    RefPtr<Label> catchHere = emitLabel(newLabel().get());
+
     emitLabel(scope->continueTarget());
-    CallArguments nextArguments(*this, 0, 1);
-    emitMove(nextArguments.thisRegister(), iterator.get());
-    emitMove(nextArguments.argumentRegister(0), value.get());
-    emitCall(value.get(), iteratorNext.get(), NoExpectedFunction, nextArguments, node->divot(), node->divotStart(), node->divotEnd());
-    RefPtr<RegisterID> result = newTemporary();
-    emitJumpIfFalse(emitEqualityOp(op_stricteq, result.get(), value.get(), emitLoad(0, JSValue(vm()->iterationTerminator.get()))), loopStart.get());
+    {
+        RefPtr<RegisterID> next = emitGetById(newTemporary(), iterator.get(), propertyNames().next);
+        CallArguments nextArguments(*this, nullptr);
+        emitMove(nextArguments.thisRegister(), iterator.get());
+        emitCall(value.get(), next.get(), NoExpectedFunction, nextArguments, node->divot(), node->divotStart(), node->divotEnd());
+    }
+    {
+        RefPtr<Label> typeIsObject = newLabel();
+        emitJumpIfTrue(emitIsObject(newTemporary(), value.get()), typeIsObject.get());
+        emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object."));
+        emitLabel(typeIsObject.get());
+    }
+    emitJumpIfTrue(emitGetById(newTemporary(), value.get(), propertyNames().done), iteratorDone.get());
+    emitGetById(value.get(), value.get(), propertyNames().value);
+    emitJump(loopStart.get());
+
+    // IteratorClose sequence for throw-ed control flow.
+    {
+        RefPtr<RegisterID> exceptionRegister = popTryAndEmitCatch(tryData, newTemporary(), catchHere.get());
+        RefPtr<Label> rethrow = newLabel();
+
+        RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword);
+        emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), rethrow.get());
+
+        RefPtr<Label> returnCallTryStart = newLabel();
+        emitLabel(returnCallTryStart.get());
+        TryData* returnCallTryData = pushTry(returnCallTryStart.get());
+
+        CallArguments returnArguments(*this, nullptr);
+        emitMove(returnArguments.thisRegister(), iterator.get());
+        emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd());
+
+        RefPtr<Label> returnCallCatchHere = emitLabel(newLabel().get());
+        emitJump(rethrow.get());
+
+        popTryAndEmitCatch(returnCallTryData, newTemporary(), returnCallCatchHere.get());
+        emitLabel(rethrow.get());
+        emitThrow(exceptionRegister.get());
+    }
+
+    // IteratorClose sequence for break-ed control flow.
     emitLabel(scope->breakTarget());
+    {
+        RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword);
+        emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), iteratorDone.get());
+
+        CallArguments returnArguments(*this, nullptr);
+        emitMove(returnArguments.thisRegister(), iterator.get());
+        emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd());
+        emitJumpIfTrue(emitIsObject(newTemporary(), value.get()), iteratorDone.get());
+        emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object."));
+    }
+
+    emitLabel(iteratorDone.get());
 }
 
 RegisterID* BytecodeGenerator::emitGetEnumerableLength(RegisterID* dst, RegisterID* base)
@@ -2649,6 +2701,23 @@ RegisterID* BytecodeGenerator::emitToIndexString(RegisterID* dst, RegisterID* in
     return dst;
 }
 
+
+RegisterID* BytecodeGenerator::emitIsObject(RegisterID* dst, RegisterID* src)
+{
+    emitOpcode(op_is_object);
+    instructions().append(dst->index());
+    instructions().append(src->index());
+    return dst;
+}
+
+RegisterID* BytecodeGenerator::emitIsUndefined(RegisterID* dst, RegisterID* src)
+{
+    emitOpcode(op_is_undefined);
+    instructions().append(dst->index());
+    instructions().append(src->index());
+    return dst;
+}
+
 void BytecodeGenerator::pushIndexedForInScope(RegisterID* localRegister, RegisterID* indexRegister)
 {
     if (!localRegister)
index 5c8323a..fb08363 100644 (file)
@@ -529,6 +529,9 @@ namespace JSC {
         RegisterID* emitNextEnumeratorPropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index);
         RegisterID* emitToIndexString(RegisterID* dst, RegisterID* index);
 
+        RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
+        RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
+
         void emitReadOnlyExceptionIfNeeded();
 
         // Start a try block. 'start' must have been emitted.
@@ -543,6 +546,7 @@ namespace JSC {
         }
 
         void emitThrowReferenceError(const String& message);
+        void emitThrowTypeError(const String& message);
 
         void emitPushFunctionNameScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes);
         void emitPushCatchScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes);
index 7b0cda6..1007d2d 100644 (file)
@@ -1044,107 +1044,6 @@ MacroAssemblerCodeRef imulThunkGenerator(VM* vm)
     return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "imul");
 }
 
-static MacroAssemblerCodeRef arrayIteratorNextThunkGenerator(VM* vm, ArrayIterationKind kind)
-{
-    typedef SpecializedThunkJIT::TrustedImm32 TrustedImm32;
-    typedef SpecializedThunkJIT::TrustedImmPtr TrustedImmPtr;
-    typedef SpecializedThunkJIT::Address Address;
-    typedef SpecializedThunkJIT::BaseIndex BaseIndex;
-    typedef SpecializedThunkJIT::Jump Jump;
-    
-    SpecializedThunkJIT jit(vm);
-    // Make sure we're being called on an array iterator, and load m_iteratedObject, and m_nextIndex into regT0 and regT1 respectively
-    jit.loadArgumentWithSpecificClass(JSArrayIterator::info(), SpecializedThunkJIT::ThisArgument, SpecializedThunkJIT::regT4, SpecializedThunkJIT::regT1);
-
-    // Early exit if we don't have a thunk for this form of iteration
-    jit.appendFailure(jit.branch32(SpecializedThunkJIT::AboveOrEqual, Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIterationKind()), TrustedImm32(ArrayIterateKeyValue)));
-    
-    jit.loadPtr(Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfIteratedObject()), SpecializedThunkJIT::regT0);
-    
-    jit.load32(Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()), SpecializedThunkJIT::regT1);
-    
-    // Pull out the butterfly from iteratedObject
-    jit.load8(Address(SpecializedThunkJIT::regT0, JSCell::indexingTypeOffset()), SpecializedThunkJIT::regT3);
-    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
-    Jump nullButterfly = jit.branchTestPtr(SpecializedThunkJIT::Zero, SpecializedThunkJIT::regT2);
-    
-    Jump notDone = jit.branch32(SpecializedThunkJIT::Below, SpecializedThunkJIT::regT1, Address(SpecializedThunkJIT::regT2, Butterfly::offsetOfPublicLength()));
-
-    nullButterfly.link(&jit);
-
-    // Return the termination signal to indicate that we've finished
-    jit.move(TrustedImmPtr(vm->iterationTerminator.get()), SpecializedThunkJIT::regT0);
-    jit.returnJSCell(SpecializedThunkJIT::regT0);
-    
-    notDone.link(&jit);
-    
-    if (kind == ArrayIterateKey) {
-        jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
-        jit.returnInt32(SpecializedThunkJIT::regT1);
-        return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "array-iterator-next-key");
-        
-    }
-    ASSERT(kind == ArrayIterateValue);
-    
-    // Okay, now we're returning a value so make sure we're inside the vector size
-    jit.appendFailure(jit.branch32(SpecializedThunkJIT::AboveOrEqual, SpecializedThunkJIT::regT1, Address(SpecializedThunkJIT::regT2, Butterfly::offsetOfVectorLength())));
-    
-    // So now we perform inline loads for int32, value/undecided, and double storage
-    Jump undecidedStorage = jit.branch32(SpecializedThunkJIT::Equal, SpecializedThunkJIT::regT3, TrustedImm32(ArrayWithUndecided));
-    Jump notContiguousStorage = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(ArrayWithContiguous));
-    
-    undecidedStorage.link(&jit);
-    
-    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
-    
-#if USE(JSVALUE64)
-    jit.load64(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight), SpecializedThunkJIT::regT0);
-    Jump notHole = jit.branchTest64(SpecializedThunkJIT::NonZero, SpecializedThunkJIT::regT0);
-    jit.move(JSInterfaceJIT::TrustedImm64(ValueUndefined), JSInterfaceJIT::regT0);
-    notHole.link(&jit);
-    jit.addPtr(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
-    jit.returnJSValue(SpecializedThunkJIT::regT0);
-#else
-    jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfTag()), SpecializedThunkJIT::regT3);
-    Jump notHole = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(JSValue::EmptyValueTag));
-    jit.move(JSInterfaceJIT::TrustedImm32(JSValue::UndefinedTag), JSInterfaceJIT::regT1);
-    jit.move(JSInterfaceJIT::TrustedImm32(0), JSInterfaceJIT::regT0);
-    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
-    jit.returnJSValue(SpecializedThunkJIT::regT0, JSInterfaceJIT::regT1);
-    notHole.link(&jit);
-    jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfPayload()), SpecializedThunkJIT::regT0);
-    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
-    jit.move(SpecializedThunkJIT::regT3, SpecializedThunkJIT::regT1);
-    jit.returnJSValue(SpecializedThunkJIT::regT0, SpecializedThunkJIT::regT1);
-#endif
-    notContiguousStorage.link(&jit);
-    
-    Jump notInt32Storage = jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(ArrayWithInt32));
-    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
-    jit.load32(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight, JSValue::offsetOfPayload()), SpecializedThunkJIT::regT0);
-    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
-    jit.returnInt32(SpecializedThunkJIT::regT0);
-    notInt32Storage.link(&jit);
-    
-    jit.appendFailure(jit.branch32(SpecializedThunkJIT::NotEqual, SpecializedThunkJIT::regT3, TrustedImm32(ArrayWithDouble)));
-    jit.loadPtr(Address(SpecializedThunkJIT::regT0, JSObject::butterflyOffset()), SpecializedThunkJIT::regT2);
-    jit.loadDouble(BaseIndex(SpecializedThunkJIT::regT2, SpecializedThunkJIT::regT1, SpecializedThunkJIT::TimesEight), SpecializedThunkJIT::fpRegT0);
-    jit.add32(TrustedImm32(1), Address(SpecializedThunkJIT::regT4, JSArrayIterator::offsetOfNextIndex()));
-    jit.returnDouble(SpecializedThunkJIT::fpRegT0);
-    
-    return jit.finalize(vm->jitStubs->ctiNativeTailCall(vm), "array-iterator-next-value");
-}
-
-MacroAssemblerCodeRef arrayIteratorNextKeyThunkGenerator(VM* vm)
-{
-    return arrayIteratorNextThunkGenerator(vm, ArrayIterateKey);
-}
-
-MacroAssemblerCodeRef arrayIteratorNextValueThunkGenerator(VM* vm)
-{
-    return arrayIteratorNextThunkGenerator(vm, ArrayIterateValue);
-}
-    
 }
 
 #endif // ENABLE(JIT)
index 246da2e..fdb00cf 100644 (file)
@@ -130,8 +130,6 @@ MacroAssemblerCodeRef roundThunkGenerator(VM*);
 MacroAssemblerCodeRef sqrtThunkGenerator(VM*);
 MacroAssemblerCodeRef powThunkGenerator(VM*);
 MacroAssemblerCodeRef imulThunkGenerator(VM*);
-MacroAssemblerCodeRef arrayIteratorNextKeyThunkGenerator(VM*);
-MacroAssemblerCodeRef arrayIteratorNextValueThunkGenerator(VM*);
 
 }
 #endif // ENABLE(JIT)
index d79cc52..ad8b759 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "ArgumentsIteratorPrototype.h"
 
+#include "IteratorOperations.h"
 #include "JSArgumentsIterator.h"
 #include "JSCInlines.h"
 
@@ -43,7 +44,7 @@ void ArgumentsIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalOb
     vm.prototypeMap.addPrototype(this);
 
     JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, argumentsIteratorPrototypeFuncIterator, DontEnum, 0);
-    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorNextPrivateName, argumentsIteratorPrototypeFuncNext, DontEnum, 0);
+    JSC_NATIVE_FUNCTION(vm.propertyNames->next, argumentsIteratorPrototypeFuncNext, DontEnum, 0);
 }
 
 EncodedJSValue JSC_HOST_CALL argumentsIteratorPrototypeFuncIterator(CallFrame* callFrame)
@@ -55,8 +56,8 @@ EncodedJSValue JSC_HOST_CALL argumentsIteratorPrototypeFuncNext(CallFrame* callF
 {
     JSValue result;
     if (jsCast<JSArgumentsIterator*>(callFrame->thisValue())->next(callFrame, result))
-        return JSValue::encode(result);
-    return JSValue::encode(callFrame->vm().iterationTerminator.get());
+        return JSValue::encode(createIterResultObject(callFrame, result, false));
+    return JSValue::encode(createIterResultObject(callFrame, jsUndefined(), true));
 }
 
 }
index c8800e3..f76868c 100644 (file)
  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "config.h"
 #include "ArrayIteratorPrototype.h"
 
+namespace JSC {
+
+static EncodedJSValue JSC_HOST_CALL arrayIteratorProtoFuncIterator(ExecState*);
+
+}
+
+#include "ArrayIteratorPrototype.lut.h"
+
+#include "IteratorOperations.h"
 #include "JSArrayIterator.h"
+#include "JSCInlines.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "JSGlobalObject.h"
 
 namespace JSC {
 
-const ClassInfo ArrayIteratorPrototype::s_info = { "Array Iterator", &Base::s_info, 0, CREATE_METHOD_TABLE(ArrayIteratorPrototype) };
 
-static EncodedJSValue JSC_HOST_CALL arrayIteratorPrototypeIterate(ExecState*);
+const ClassInfo ArrayIteratorPrototype::s_info = { "Array Iterator", &Base::s_info, &arrayIteratorPrototypeTable, CREATE_METHOD_TABLE(ArrayIteratorPrototype) };
+
+/* Source for ArrayIteratorPrototype.lut.h
+@begin arrayIteratorPrototypeTable
+  next      arrayIteratorProtoFuncNext  DontEnum|Function 0
+@end
+*/
 
 void ArrayIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(info()));
     vm.prototypeMap.addPrototype(this);
-
-    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, arrayIteratorPrototypeIterate, DontEnum, 0);
+    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, arrayIteratorProtoFuncIterator, DontEnum, 0);
 }
 
-EncodedJSValue JSC_HOST_CALL arrayIteratorPrototypeIterate(CallFrame* callFrame)
+bool ArrayIteratorPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
 {
-    return JSValue::encode(callFrame->thisValue());
+    return getStaticFunctionSlot<Base>(exec, arrayIteratorPrototypeTable, jsCast<ArrayIteratorPrototype*>(object), propertyName, slot);
 }
 
+// ------------------------------ Array Functions ----------------------------
+
+EncodedJSValue JSC_HOST_CALL arrayIteratorProtoFuncIterator(ExecState* exec)
+{
+    return JSValue::encode(exec->thisValue());
 }
+
+} // namespace JSC
index 10ee9a6..547e332 100644 (file)
@@ -48,12 +48,17 @@ public:
         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
     }
 
+protected:
+    static const unsigned StructureFlags = OverridesGetOwnPropertySlot | Base::StructureFlags;
+
 private:
     ArrayIteratorPrototype(VM& vm, Structure* structure)
         : Base(vm, structure)
     {
     }
+
     void finishCreation(VM&, JSGlobalObject*);
+    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
 };
 
 }
index f111773..386f543 100644 (file)
 
 #define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \
     JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
-    macro(iteratorNext) \
+    macro(iteratedObject) \
+    macro(arrayIteratorNextIndex) \
+    macro(arrayIterationKind) \
+    macro(arrayIterationKindKey) \
+    macro(arrayIterationKindValue) \
+    macro(arrayIterationKindKeyValue) \
     macro(resolve) \
     macro(reject) \
     macro(promise) \
index 538b328..f828336 100644 (file)
@@ -52,10 +52,7 @@ enum Intrinsic {
     StringPrototypeValueOfIntrinsic,
     IMulIntrinsic,
     FRoundIntrinsic,
-    ArrayIteratorNextValueIntrinsic,
-    ArrayIteratorNextKeyIntrinsic,
-    ArrayIteratorNextGenericIntrinsic,
-    
+
     // Debugging intrinsics. These are meant to be used as testing hacks within
     // jsc.cpp and should never be exposed to users.
     DFGTrueIntrinsic,
diff --git a/Source/JavaScriptCore/runtime/IteratorOperations.cpp b/Source/JavaScriptCore/runtime/IteratorOperations.cpp
new file mode 100644 (file)
index 0000000..b01d33d
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "IteratorOperations.h"
+
+#include "Error.h"
+#include "JSCInlines.h"
+#include "ObjectConstructor.h"
+
+using namespace WTF;
+
+namespace JSC {
+
+JSValue iteratorNext(ExecState* exec, JSValue iterator, JSValue value)
+{
+    JSValue nextFunction = iterator.get(exec, exec->vm().propertyNames->next);
+    if (exec->hadException())
+        return jsUndefined();
+
+    CallData nextFunctionCallData;
+    CallType nextFunctionCallType = getCallData(nextFunction, nextFunctionCallData);
+    if (nextFunctionCallType == CallTypeNone)
+        return throwTypeError(exec);
+
+    MarkedArgumentBuffer nextFunctionArguments;
+    if (!value.isEmpty())
+        nextFunctionArguments.append(value);
+    JSValue result = call(exec, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments);
+    if (exec->hadException())
+        return jsUndefined();
+
+    if (!result.isObject())
+        return throwTypeError(exec, ASCIILiteral("Iterator result interface is not an object."));
+
+    return result;
+}
+
+JSValue iteratorNext(ExecState* exec, JSValue iterator)
+{
+    return iteratorNext(exec, iterator, JSValue());
+}
+
+JSValue iteratorValue(ExecState* exec, JSValue iterResult)
+{
+    return iterResult.get(exec, exec->vm().propertyNames->value);
+}
+
+bool iteratorComplete(ExecState* exec, JSValue iterResult)
+{
+    JSValue done = iterResult.get(exec, exec->vm().propertyNames->done);
+    return done.toBoolean(exec);
+}
+
+JSValue iteratorStep(ExecState* exec, JSValue iterator)
+{
+    JSValue result = iteratorNext(exec, iterator);
+    if (exec->hadException())
+        return jsUndefined();
+    bool done = iteratorComplete(exec, result);
+    if (exec->hadException())
+        return jsUndefined();
+    if (done)
+        return jsBoolean(false);
+    return result;
+}
+
+void iteratorClose(ExecState* exec, JSValue iterator)
+{
+    JSValue exception;
+    if (exec->hadException()) {
+        exception = exec->exception();
+        exec->clearException();
+    }
+    JSValue returnFunction = iterator.get(exec, exec->vm().propertyNames->returnKeyword);
+    if (exec->hadException())
+        return;
+
+    if (returnFunction.isUndefined()) {
+        if (!exception.isEmpty())
+            exec->vm().throwException(exec, exception);
+        return;
+    }
+
+    CallData returnFunctionCallData;
+    CallType returnFunctionCallType = getCallData(returnFunction, returnFunctionCallData);
+    if (returnFunctionCallType == CallTypeNone) {
+        if (!exception.isEmpty())
+            exec->vm().throwException(exec, exception);
+        else
+            throwTypeError(exec);
+        return;
+    }
+
+    MarkedArgumentBuffer returnFunctionArguments;
+    JSValue innerResult = call(exec, returnFunction, returnFunctionCallType, returnFunctionCallData, iterator, returnFunctionArguments);
+
+    if (!exception.isEmpty()) {
+        exec->vm().throwException(exec, exception);
+        return;
+    }
+
+    if (exec->hadException())
+        return;
+
+    if (!innerResult.isObject()) {
+        throwTypeError(exec, ASCIILiteral("Iterator result interface is not an object."));
+        return;
+    }
+}
+
+JSObject* createIterResultObject(ExecState* exec, JSValue value, bool done)
+{
+    JSObject* resultObject = constructEmptyObject(exec);
+    resultObject->putDirect(exec->vm(), exec->propertyNames().done, jsBoolean(done));
+    resultObject->putDirect(exec->vm(), exec->propertyNames().value, value);
+    return resultObject;
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/IteratorOperations.h b/Source/JavaScriptCore/runtime/IteratorOperations.h
new file mode 100644 (file)
index 0000000..4bef388
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IteratorOperations_h
+#define IteratorOperations_h
+
+#include "JSCJSValue.h"
+#include "JSObject.h"
+
+namespace JSC {
+
+JSValue iteratorNext(ExecState*, JSValue iterator, JSValue);
+JSValue iteratorNext(ExecState*, JSValue iterator);
+JSValue iteratorValue(ExecState*, JSValue iterResult);
+bool iteratorComplete(ExecState*, JSValue iterResult);
+JSValue iteratorStep(ExecState*, JSValue iterator);
+void iteratorClose(ExecState*, JSValue iterator);
+JSObject* createIterResultObject(ExecState*, JSValue, bool done);
+
+}
+
+#endif // !defined(IteratorOperations_h)
index 33bab71..dac562b 100644 (file)
@@ -35,132 +35,14 @@ namespace JSC {
 
 const ClassInfo JSArrayIterator::s_info = { "ArrayIterator", &Base::s_info, 0, CREATE_METHOD_TABLE(JSArrayIterator) };
 
-static EncodedJSValue JSC_HOST_CALL arrayIteratorNextKey(ExecState*);
-static EncodedJSValue JSC_HOST_CALL arrayIteratorNextValue(ExecState*);
-static EncodedJSValue JSC_HOST_CALL arrayIteratorNextGeneric(ExecState*);
-
-void JSArrayIterator::finishCreation(VM& vm, JSGlobalObject* globalObject, ArrayIterationKind kind, JSObject* iteratedObject)
+void JSArrayIterator::finishCreation(VM& vm, JSGlobalObject*, ArrayIterationKind kind, JSObject* iteratedObject)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(info()));
-    m_iterationKind = kind;
-    m_iteratedObject.set(vm, this, iteratedObject);
-    switch (kind) {
-    case ArrayIterateKey:
-        JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->iteratorNextPrivateName, arrayIteratorNextKey, DontEnum, 0, ArrayIteratorNextKeyIntrinsic);
-        break;
-    case ArrayIterateValue:
-        JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->iteratorNextPrivateName, arrayIteratorNextValue, DontEnum, 0, ArrayIteratorNextValueIntrinsic);
-        break;
-    default:
-        JSC_NATIVE_INTRINSIC_FUNCTION(vm.propertyNames->iteratorNextPrivateName, arrayIteratorNextGeneric, DontEnum, 0, ArrayIteratorNextGenericIntrinsic);
-        break;
-    }
-
-}
-    
-    
-void JSArrayIterator::visitChildren(JSCell* cell, SlotVisitor& visitor)
-{
-    JSArrayIterator* thisObject = jsCast<JSArrayIterator*>(cell);
-    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
-    Base::visitChildren(thisObject, visitor);
-    visitor.append(&thisObject->m_iteratedObject);
-}
-
-static EncodedJSValue createIteratorResult(CallFrame* callFrame, ArrayIterationKind kind, size_t index, JSValue result, bool done)
-{
-    if (done)
-        return JSValue::encode(callFrame->vm().iterationTerminator.get());
-    
-    switch (kind & ~ArrayIterateSparseTag) {
-    case ArrayIterateKey:
-        return JSValue::encode(jsNumber(index));
-        
-    case ArrayIterateValue:
-        return JSValue::encode(result);
-        
-    case ArrayIterateKeyValue: {
-        MarkedArgumentBuffer args;
-        args.append(jsNumber(index));
-        args.append(result);
-        JSGlobalObject* globalObject = callFrame->callee()->globalObject();
-        return JSValue::encode(constructArray(callFrame, 0, globalObject, args));
-        
-    }
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
-    }
-    return JSValue::encode(JSValue());
-}
 
-static inline EncodedJSValue JSC_HOST_CALL arrayIteratorNext(CallFrame* callFrame)
-{
-    JSArrayIterator* iterator = jsDynamicCast<JSArrayIterator*>(callFrame->thisValue());
-    if (!iterator) {
-        ASSERT_NOT_REACHED();
-        return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot call ArrayIterator.next() on a non-ArrayIterator object")));
-    }
-    JSObject* iteratedObject = iterator->iteratedObject();
-    size_t index = iterator->nextIndex();
-    ArrayIterationKind kind = iterator->iterationKind();
-    JSValue jsLength = JSValue(iteratedObject).get(callFrame, callFrame->propertyNames().length);
-    if (callFrame->hadException())
-        return JSValue::encode(jsNull());
-    
-    size_t length = jsLength.toUInt32(callFrame);
-    if (callFrame->hadException())
-        return JSValue::encode(jsNull());
-    
-    if (index >= length) {
-        iterator->finish();
-        return createIteratorResult(callFrame, kind, index, jsUndefined(), true);
-    }
-    if (JSValue result = iteratedObject->tryGetIndexQuickly(index)) {
-        iterator->setNextIndex(index + 1);
-        return createIteratorResult(callFrame, kind, index, result, false);
-    }
-    
-    JSValue result = jsUndefined();
-    PropertySlot slot(iteratedObject);
-    if (kind > ArrayIterateSparseTag) {
-        // We assume that the indexed property will be an own property so cache the getOwnProperty
-        // method locally
-        auto getOwnPropertySlotByIndex = iteratedObject->methodTable()->getOwnPropertySlotByIndex;
-        while (index < length) {
-            if (getOwnPropertySlotByIndex(iteratedObject, callFrame, index, slot)) {
-                result = slot.getValue(callFrame, index);
-                break;
-            }
-            if (iteratedObject->getPropertySlot(callFrame, index, slot)) {
-                result = slot.getValue(callFrame, index);
-                break;
-            }
-            index++;
-        }
-    } else if (iteratedObject->getPropertySlot(callFrame, index, slot))
-        result = slot.getValue(callFrame, index);
-    
-    if (index == length)
-        iterator->finish();
-    else
-        iterator->setNextIndex(index + 1);
-    return createIteratorResult(callFrame, kind, index, jsUndefined(), index == length);
-}
-    
-EncodedJSValue JSC_HOST_CALL arrayIteratorNextKey(CallFrame* callFrame)
-{
-    return arrayIteratorNext(callFrame);
-}
-    
-EncodedJSValue JSC_HOST_CALL arrayIteratorNextValue(CallFrame* callFrame)
-{
-    return arrayIteratorNext(callFrame);
-}
-    
-EncodedJSValue JSC_HOST_CALL arrayIteratorNextGeneric(CallFrame* callFrame)
-{
-    return arrayIteratorNext(callFrame);
+    putDirect(vm, vm.propertyNames->iteratedObjectPrivateName, iteratedObject);
+    putDirect(vm, vm.propertyNames->arrayIteratorNextIndexPrivateName, jsNumber(0));
+    putDirect(vm, vm.propertyNames->arrayIterationKindPrivateName, jsNumber(kind));
 }
 
 }
index 7bb0fc5..2e976be 100644 (file)
@@ -33,11 +33,7 @@ namespace JSC {
 enum ArrayIterationKind : uint32_t {
     ArrayIterateKey,
     ArrayIterateValue,
-    ArrayIterateKeyValue,
-    ArrayIterateSparseTag = 4,
-    ArrayIterateSparseKey,
-    ArrayIterateSparseValue,
-    ArrayIterateSparseKeyValue
+    ArrayIterateKeyValue
 };
 
 class JSArrayIterator : public JSNonFinalObject {
@@ -59,30 +55,14 @@ public:
         return instance;
     }
 
-    ArrayIterationKind iterationKind() const { return m_iterationKind; }
-    JSObject* iteratedObject() const { return m_iteratedObject.get(); }
-    size_t nextIndex() const { return m_nextIndex; }
-    void setNextIndex(size_t nextIndex) { m_nextIndex = nextIndex; }
-    void finish() { m_nextIndex = std::numeric_limits<uint32_t>::max(); }
-    
     using JSNonFinalObject::arrayStorageOrNull;
-    static ptrdiff_t offsetOfIterationKind() { return OBJECT_OFFSETOF(JSArrayIterator, m_iterationKind); }
-    static ptrdiff_t offsetOfIteratedObject() { return OBJECT_OFFSETOF(JSArrayIterator, m_iteratedObject); }
-    static ptrdiff_t offsetOfNextIndex() { return OBJECT_OFFSETOF(JSArrayIterator, m_nextIndex); }
-
 private:
     JSArrayIterator(VM& vm, Structure* structure)
         : Base(vm, structure)
-        , m_nextIndex(0)
     {
     }
 
     void finishCreation(VM&, JSGlobalObject*, ArrayIterationKind, JSObject* iteratedObject);
-    static void visitChildren(JSCell*, SlotVisitor&);
-    
-    ArrayIterationKind m_iterationKind;
-    WriteBarrier<JSObject> m_iteratedObject;
-    uint32_t m_nextIndex;
 };
 
 }
index 8c4357e..4c8d7ab 100644 (file)
@@ -429,6 +429,9 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         GlobalPropertyInfo(vm.propertyNames->absPrivateName, privateFuncAbs, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->floorPrivateName, privateFuncFloor, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->isFinitePrivateName, privateFuncIsFinite, DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->arrayIterationKindKeyPrivateName, jsNumber(ArrayIterateKey), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->arrayIterationKindValuePrivateName, jsNumber(ArrayIterateValue), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->arrayIterationKindKeyValuePrivateName, jsNumber(ArrayIterateKeyValue), DontEnum | DontDelete | ReadOnly),
     };
     addStaticGlobals(staticGlobals, WTF_ARRAY_LENGTH(staticGlobals));
     
index f1a2e56..afdfc27 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(PROMISES)
 
 #include "Error.h"
+#include "IteratorOperations.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "JSPromise.h"
@@ -232,104 +233,57 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncReject(ExecState* exec)
     return JSValue::encode(deferred->promise());
 }
 
-EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncRace(ExecState* exec)
+static void performPromiseRaceLoop(ExecState* exec, JSValue iterator, JSPromiseDeferred* deferred, JSValue C)
 {
-    // -- Promise.race(iterable) --
-    JSValue iterable = exec->argument(0);
-    VM& vm = exec->vm();
-
-    // 1. Let 'C' be the this value.
-    JSValue C = exec->thisValue();
-
-    // 2. Let 'deferred' be the result of calling GetDeferred(C).
-    JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
-
-    // 3. ReturnIfAbrupt(deferred).
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
-
-    // 4. Let 'iterator' be the result of calling GetIterator(iterable).
-    JSValue iteratorFunction = iterable.get(exec, vm.propertyNames->iteratorPrivateName);
-    if (exec->hadException())
-        return JSValue::encode(abruptRejection(exec, deferred));
-
-    CallData iteratorFunctionCallData;
-    CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData);
-    if (iteratorFunctionCallType == CallTypeNone) {
-        throwTypeError(exec);
-        return JSValue::encode(abruptRejection(exec, deferred));
-    }
-
-    ArgList iteratorFunctionArguments;
-    JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments);
-
-    // 5. RejectIfAbrupt(iterator, deferred).
-    if (exec->hadException())
-        return JSValue::encode(abruptRejection(exec, deferred));
-
     // 6. Repeat
     do {
         // i. Let 'next' be the result of calling IteratorStep(iterator).
-        JSValue nextFunction = iterator.get(exec, exec->vm().propertyNames->iteratorNextPrivateName);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData nextFunctionCallData;
-        CallType nextFunctionCallType = getCallData(nextFunction, nextFunctionCallData);
-        if (nextFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
+        JSValue next = iteratorStep(exec, iterator);
 
-        MarkedArgumentBuffer nextFunctionArguments;
-        nextFunctionArguments.append(jsUndefined());
-        JSValue next = call(exec, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments);
-        
         // ii. RejectIfAbrupt(next, deferred).
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-    
+            return;
+
         // iii. If 'next' is false, return deferred.[[Promise]].
-        // Note: We implement this as an iterationTerminator
-        if (next == vm.iterationTerminator.get())
-            return JSValue::encode(deferred->promise());
-        
+        if (next.isFalse())
+            return;
+
         // iv. Let 'nextValue' be the result of calling IteratorValue(next).
         // v. RejectIfAbrupt(nextValue, deferred).
-        // Note: 'next' is already the value, so there is nothing to do here.
+        JSValue nextValue = iteratorValue(exec, next);
+        if (exec->hadException())
+            return;
 
         // vi. Let 'nextPromise' be the result of calling Invoke(C, "resolve", (nextValue)).
-        JSValue resolveFunction = C.get(exec, vm.propertyNames->resolve);
+        JSValue resolveFunction = C.get(exec, exec->vm().propertyNames->resolve);
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return;
 
         CallData resolveFunctionCallData;
         CallType resolveFunctionCallType = getCallData(resolveFunction, resolveFunctionCallData);
         if (resolveFunctionCallType == CallTypeNone) {
             throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return;
         }
 
         MarkedArgumentBuffer resolveFunctionArguments;
-        resolveFunctionArguments.append(next);
+        resolveFunctionArguments.append(nextValue);
         JSValue nextPromise = call(exec, resolveFunction, resolveFunctionCallType, resolveFunctionCallData, C, resolveFunctionArguments);
 
         // vii. RejectIfAbrupt(nextPromise, deferred).
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return;
 
         // viii. Let 'result' be the result of calling Invoke(nextPromise, "then", (deferred.[[Resolve]], deferred.[[Reject]])).
-        JSValue thenFunction = nextPromise.get(exec, vm.propertyNames->then);
+        JSValue thenFunction = nextPromise.get(exec, exec->vm().propertyNames->then);
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return;
 
         CallData thenFunctionCallData;
         CallType thenFunctionCallType = getCallData(thenFunction, thenFunctionCallData);
         if (thenFunctionCallType == CallTypeNone) {
             throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return;
         }
 
         MarkedArgumentBuffer thenFunctionArguments;
@@ -340,14 +294,13 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncRace(ExecState* exec)
 
         // ix. RejectIfAbrupt(result, deferred).
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return;
     } while (true);
 }
 
-EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncRace(ExecState* exec)
 {
-    // -- Promise.all(iterable) --
-
+    // -- Promise.race(iterable) --
     JSValue iterable = exec->argument(0);
     VM& vm = exec->vm();
 
@@ -361,9 +314,6 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
     if (exec->hadException())
         return JSValue::encode(jsUndefined());
 
-    // NOTE: A non-abrupt completion of createJSPromiseDeferredFromConstructor implies that
-    // C and deferredValue are objects.
-    JSObject* thisObject = asObject(C);
     JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
 
     // 4. Let 'iterator' be the result of calling GetIterator(iterable).
@@ -385,40 +335,37 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
     if (exec->hadException())
         return JSValue::encode(abruptRejection(exec, deferred));
 
+    performPromiseRaceLoop(exec, iterator, deferred, C);
+    if (exec->hadException())
+        iteratorClose(exec, iterator);
+    if (exec->hadException())
+        return JSValue::encode(abruptRejection(exec, deferred));
+    return JSValue::encode(deferred->promise());
+}
+
+static JSValue performPromiseAll(ExecState* exec, JSValue iterator, JSValue C, JSPromiseDeferred* deferred)
+{
+    JSObject* thisObject = asObject(C);
+    VM& vm = exec->vm();
+
     // 6. Let 'values' be the result of calling ArrayCreate(0).
     JSArray* values = constructEmptyArray(exec, nullptr, thisObject->globalObject());
-    
+
     // 7. Let 'countdownHolder' be Record { [[Countdown]]: 0 }.
     NumberObject* countdownHolder = constructNumber(exec, thisObject->globalObject(), JSValue(0));
-    
+
     // 8. Let 'index' be 0.
     unsigned index = 0;
-    
+
     // 9. Repeat.
     do {
         // i. Let 'next' be the result of calling IteratorStep(iterator).
-        JSValue nextFunction = iterator.get(exec, exec->vm().propertyNames->iteratorNextPrivateName);
-        if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
-
-        CallData nextFunctionCallData;
-        CallType nextFunctionCallType = getCallData(nextFunction, nextFunctionCallData);
-        if (nextFunctionCallType == CallTypeNone) {
-            throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
-        }
-
-        MarkedArgumentBuffer nextFunctionArguments;
-        nextFunctionArguments.append(jsUndefined());
-        JSValue next = call(exec, nextFunction, nextFunctionCallType, nextFunctionCallData, iterator, nextFunctionArguments);
-        
-        // ii. RejectIfAbrupt(next, deferred).
+        JSValue next = iteratorStep(exec, iterator);
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return jsUndefined();
 
         // iii. If 'next' is false,
-        // Note: We implement this as an iterationTerminator
-        if (next == vm.iterationTerminator.get()) {
+        if (next.isFalse()) {
             // a. If 'index' is 0,
             if (!index) {
                 // a. Let 'resolveResult' be the result of calling the [[Call]] internal method
@@ -428,40 +375,44 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
 
                 // b. ReturnIfAbrupt(resolveResult).
                 if (exec->hadException())
-                    return JSValue::encode(jsUndefined());
+                    return jsUndefined();
             }
-            
+
             // b. Return deferred.[[Promise]].
-            return JSValue::encode(deferred->promise());
+            return deferred->promise();
         }
-        
+
         // iv. Let 'nextValue' be the result of calling IteratorValue(next).
         // v. RejectIfAbrupt(nextValue, deferred).
-        // Note: 'next' is already the value, so there is nothing to do here.
+        JSValue nextValue = iteratorValue(exec, next);
+        if (exec->hadException())
+            return jsUndefined();
+
+        values->push(exec, jsUndefined());
 
         // vi. Let 'nextPromise' be the result of calling Invoke(C, "resolve", (nextValue)).
         JSValue resolveFunction = C.get(exec, vm.propertyNames->resolve);
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return jsUndefined();
 
         CallData resolveFunctionCallData;
         CallType resolveFunctionCallType = getCallData(resolveFunction, resolveFunctionCallData);
         if (resolveFunctionCallType == CallTypeNone) {
             throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return jsUndefined();
         }
 
         MarkedArgumentBuffer resolveFunctionArguments;
-        resolveFunctionArguments.append(next);
+        resolveFunctionArguments.append(nextValue);
         JSValue nextPromise = call(exec, resolveFunction, resolveFunctionCallType, resolveFunctionCallData, C, resolveFunctionArguments);
 
         // vii. RejectIfAbrupt(nextPromise, deferred).
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return jsUndefined();
 
         // viii. Let 'countdownFunction' be a new built-in function object as defined in Promise.all Countdown Functions.
         JSFunction* countdownFunction = createPromiseAllCountdownFunction(vm, thisObject->globalObject());
-        
+
         // ix. Set the [[Index]] internal slot of 'countdownFunction' to 'index'.
         countdownFunction->putDirect(vm, vm.propertyNames->indexPrivateName, JSValue(index));
 
@@ -477,13 +428,13 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
         // xiii. Let 'result' be the result of calling Invoke(nextPromise, "then", (countdownFunction, deferred.[[Reject]])).
         JSValue thenFunction = nextPromise.get(exec, vm.propertyNames->then);
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return jsUndefined();
 
         CallData thenFunctionCallData;
         CallType thenFunctionCallType = getCallData(thenFunction, thenFunctionCallData);
         if (thenFunctionCallType == CallTypeNone) {
             throwTypeError(exec);
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return jsUndefined();
         }
 
         MarkedArgumentBuffer thenFunctionArguments;
@@ -494,7 +445,7 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
 
         // xiv. RejectIfAbrupt(result, deferred).
         if (exec->hadException())
-            return JSValue::encode(abruptRejection(exec, deferred));
+            return jsUndefined();
 
         // xv. Set index to index + 1.
         index++;
@@ -503,6 +454,57 @@ EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
         uint32_t newCountdownValue = countdownHolder->internalValue().asUInt32() + 1;
         countdownHolder->setInternalValue(vm, JSValue(newCountdownValue));
     } while (true);
+    ASSERT_NOT_REACHED();
+    return jsUndefined();
+}
+
+EncodedJSValue JSC_HOST_CALL JSPromiseConstructorFuncAll(ExecState* exec)
+{
+    // -- Promise.all(iterable) --
+
+    JSValue iterable = exec->argument(0);
+    VM& vm = exec->vm();
+
+    // 1. Let 'C' be the this value.
+    JSValue C = exec->thisValue();
+
+    // 2. Let 'deferred' be the result of calling GetDeferred(C).
+    JSValue deferredValue = createJSPromiseDeferredFromConstructor(exec, C);
+
+    // 3. ReturnIfAbrupt(deferred).
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    // NOTE: A non-abrupt completion of createJSPromiseDeferredFromConstructor implies that
+    // C and deferredValue are objects.
+    JSPromiseDeferred* deferred = jsCast<JSPromiseDeferred*>(deferredValue);
+
+    // 4. Let 'iterator' be the result of calling GetIterator(iterable).
+    JSValue iteratorFunction = iterable.get(exec, vm.propertyNames->iteratorPrivateName);
+    if (exec->hadException())
+        return JSValue::encode(abruptRejection(exec, deferred));
+
+    CallData iteratorFunctionCallData;
+    CallType iteratorFunctionCallType = getCallData(iteratorFunction, iteratorFunctionCallData);
+    if (iteratorFunctionCallType == CallTypeNone) {
+        throwTypeError(exec);
+        return JSValue::encode(abruptRejection(exec, deferred));
+    }
+
+    ArgList iteratorFunctionArguments;
+    JSValue iterator = call(exec, iteratorFunction, iteratorFunctionCallType, iteratorFunctionCallData, iterable, iteratorFunctionArguments);
+
+    // 5. RejectIfAbrupt(iterator, deferred).
+    if (exec->hadException())
+        return JSValue::encode(abruptRejection(exec, deferred));
+
+    JSValue result = performPromiseAll(exec, iterator, C, deferred);
+    if (exec->hadException()) {
+        iteratorClose(exec, iterator);
+        if (exec->hadException())
+            return JSValue::encode(abruptRejection(exec, deferred));
+    }
+    return JSValue::encode(result);
 }
 
 JSPromise* constructPromise(ExecState* exec, JSGlobalObject* globalObject, JSFunction* resolver)
index b220b98..8067cb2 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "MapIteratorPrototype.h"
 
+#include "IteratorOperations.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "JSMapIterator.h"
@@ -46,7 +47,7 @@ void MapIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     vm.prototypeMap.addPrototype(this);
 
     JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, MapIteratorPrototypeFuncIterator, DontEnum, 0);
-    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorNextPrivateName, MapIteratorPrototypeFuncNext, DontEnum, 0);
+    JSC_NATIVE_FUNCTION(vm.propertyNames->next, MapIteratorPrototypeFuncNext, DontEnum, 0);
 }
 
 EncodedJSValue JSC_HOST_CALL MapIteratorPrototypeFuncIterator(CallFrame* callFrame)
@@ -59,12 +60,12 @@ EncodedJSValue JSC_HOST_CALL MapIteratorPrototypeFuncNext(CallFrame* callFrame)
     JSMapIterator* iterator = jsDynamicCast<JSMapIterator*>(callFrame->thisValue());
     if (!iterator)
         return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot call MapIterator.next() on a non-MapIterator object")));
-    
+
     JSValue result;
     if (iterator->next(callFrame, result))
-        return JSValue::encode(result);
+        return JSValue::encode(createIterResultObject(callFrame, result, false));
     iterator->finish();
-    return JSValue::encode(callFrame->vm().iterationTerminator.get());
+    return JSValue::encode(createIterResultObject(callFrame, jsUndefined(), true));
 }
 
 
index 573acfa..5261bf3 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "SetIteratorPrototype.h"
 
+#include "IteratorOperations.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "JSSetIterator.h"
@@ -46,7 +47,7 @@ void SetIteratorPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
     vm.prototypeMap.addPrototype(this);
 
     JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorPrivateName, SetIteratorPrototypeFuncIterator, DontEnum, 0);
-    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorNextPrivateName, SetIteratorPrototypeFuncNext, DontEnum, 0);
+    JSC_NATIVE_FUNCTION(vm.propertyNames->next, SetIteratorPrototypeFuncNext, DontEnum, 0);
 }
 
 EncodedJSValue JSC_HOST_CALL SetIteratorPrototypeFuncIterator(CallFrame* callFrame)
@@ -62,10 +63,9 @@ EncodedJSValue JSC_HOST_CALL SetIteratorPrototypeFuncNext(CallFrame* callFrame)
         return JSValue::encode(throwTypeError(callFrame, ASCIILiteral("Cannot call SetIterator.next() on a non-SetIterator object")));
 
     if (iterator->next(callFrame, result))
-        return JSValue::encode(result);
+        return JSValue::encode(createIterResultObject(callFrame, result, false));
     iterator->finish();
-    return JSValue::encode(callFrame->vm().iterationTerminator.get());
+    return JSValue::encode(createIterResultObject(callFrame, jsUndefined(), true));
 }
 
-
 }
index ceb029f..a7324b4 100644 (file)
@@ -399,10 +399,6 @@ static ThunkGenerator thunkGeneratorForIntrinsic(Intrinsic intrinsic)
         return logThunkGenerator;
     case IMulIntrinsic:
         return imulThunkGenerator;
-    case ArrayIteratorNextKeyIntrinsic:
-        return arrayIteratorNextKeyThunkGenerator;
-    case ArrayIteratorNextValueIntrinsic:
-        return arrayIteratorNextValueThunkGenerator;
     default:
         return 0;
     }
diff --git a/Source/JavaScriptCore/tests/stress/array-iterators-next-with-call.js b/Source/JavaScriptCore/tests/stress/array-iterators-next-with-call.js
new file mode 100644 (file)
index 0000000..e575dde
--- /dev/null
@@ -0,0 +1,115 @@
+// This test checks the behavior of the %ArrayIteratorPrototype%.next methods with call.
+
+var array = [0, 1, 2, 3, 4];
+var arrayIterator = array[Symbol.iterator]();
+var arrayIteratorPrototype = arrayIterator.__proto__;
+var arrayIteratorPrototypeNext = arrayIteratorPrototype.next;
+
+if (arrayIterator.hasOwnProperty('next'))
+    throw "next method should exists on %ArrayIteratorPrototype%";
+if (!arrayIteratorPrototype.hasOwnProperty('next'))
+    throw "next method should exists on %ArrayIteratorPrototype%";
+
+var array1 = [42, 43, 41];
+var array1Iterator = array1[Symbol.iterator]();
+var index = 0;
+while (true) {
+    var result = arrayIteratorPrototypeNext.call(array1Iterator);
+    var value = result.value;
+    if (result.done) {
+        break;
+    }
+    if (value !== array1[index++])
+        throw "Error: bad value: " + value;
+}
+if (index !== 3)
+    throw "Error: bad index: " + index;
+
+function increment(iter) {
+    return arrayIteratorPrototypeNext.call(iter);
+}
+var array1 = [42, 43, -20];
+var array2 = [42, 43, -20];
+var array1Iterator = array1[Symbol.iterator]();
+var array2Iterator = array2[Symbol.iterator]();
+for (var i = 0; i < 3; ++i) {
+    var value1 = increment(array1Iterator).value;
+    var value2 = increment(array2Iterator).value;
+    if (value1 !== value2)
+        throw "Error: bad value: " + value1 + " " + value2;
+}
+
+var array1 = [ 0, 1, 2, 4, 5, 6 ];
+var array1Iterator = array1[Symbol.iterator]();
+
+var value = array1Iterator.next().value;
+if (value !== 0)
+    throw "Error: bad value: " + value;
+var value = array1Iterator.next().value;
+if (value !== 1)
+    throw "Error: bad value: " + value;
+var value = array1Iterator.next().value;
+if (value !== 2)
+    throw "Error: bad value: " + value;
+var value = arrayIteratorPrototypeNext.call(array1Iterator).value;
+if (value !== 4)
+    throw "Error: bad value: " + value;
+var value = arrayIteratorPrototypeNext.call(array1Iterator).value;
+if (value !== 5)
+    throw "Error: bad value: " + value;
+var value = arrayIteratorPrototypeNext.call(array1Iterator).value;
+if (value !== 6)
+    throw "Error: bad value: " + value;
+var value = arrayIteratorPrototypeNext.call(array1Iterator).value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+var primitives = [
+    "string",
+    42,
+    0.03,
+    false,
+    true,
+    Symbol("Cocoa"),
+    null,
+    undefined
+];
+for (var primitive of primitives) {
+    var didThrow = null;
+    try {
+        arrayIteratorPrototypeNext.call(primitive);
+    } catch (e) {
+        didThrow = e;
+    }
+    if (!didThrow)
+        throw "Error: no error thrown";
+    var expectedMessage = 'TypeError: %ArrayIteratorPrototype%.next requires that |this| be Array Iterator Instance';
+    if (primitive == null)
+        expectedMessage = 'TypeError: %ArrayIteratorPrototype%.next requires that |this| not be null or undefined';
+    if (String(didThrow) !== expectedMessage)
+        throw "Error: bad error thrown: " + didThrow;
+}
+
+var nonRelatedObjects = [
+    {},
+    [],
+    new Date(),
+    new Error(),
+    Object(Symbol()),
+    new String("Cappuccino"),
+    new Number(42),
+    new Boolean(false),
+    function () { },
+];
+for (var object of nonRelatedObjects) {
+    var didThrow = null;
+    try {
+        arrayIteratorPrototypeNext.call(object);
+    } catch (e) {
+        didThrow = e;
+    }
+    if (!didThrow)
+        throw "Error: no error thrown";
+    if (String(didThrow) !== 'TypeError: %ArrayIteratorPrototype%.next requires that |this| be Array Iterator Instance')
+        throw "Error: bad error thrown: " + didThrow;
+}
diff --git a/Source/JavaScriptCore/tests/stress/array-iterators-next.js b/Source/JavaScriptCore/tests/stress/array-iterators-next.js
new file mode 100644 (file)
index 0000000..334c0e4
--- /dev/null
@@ -0,0 +1,90 @@
+// This test checks the behavior of the iterator.next methods on Array objects
+
+var testArray = [1,2,3,4,5,6]
+var keys = testArray.keys();
+var i = 0;
+while (true) {
+    var {done, value: key} = keys.next();
+    if (done)
+        break;
+    if (key !== i)
+        throw "Error: bad value: " + key;
+    i++;
+}
+
+if (testArray.length !== i)
+    throw "Error: bad value: " + i;
+
+var value = keys.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+var values = testArray[Symbol.iterator]();
+var i = 0;
+while (true) {
+    var {done, value} = values.next();
+    if (done)
+        break;
+    i++;
+    if (value !== i)
+        throw "Error: bad value: " + value;
+}
+
+if (testArray.length !== i)
+    throw "Error: bad value: " + i;
+
+var value = values.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+var entries = testArray.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (value !== testArray[key])
+        throw "Error: bad value: " + value + " " + testArray[key];
+    if (key !== i)
+        throw "Error: bad value: " + key;
+    i++
+    if (value !== i)
+        throw "Error: bad value: " + value + " " + i;
+} while (!done);
+
+if (testArray.length !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+var entries = testArray.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (value !== testArray[key])
+        throw "Error: bad value: " + value + " " + testArray[key];
+    if (key !== i)
+        throw "Error: bad value: " + key;
+    i++
+    if (i % 2 == 0)
+        testArray[i] *= 2;
+    if (i < 4)
+        testArray.push(testArray.length)
+    if (i == 4)
+        delete testArray[4]
+    if (i == 5)
+        testArray[4] = 5
+} while (!done);
+
+if (testArray.length !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
diff --git a/Source/JavaScriptCore/tests/stress/custom-iterators.js b/Source/JavaScriptCore/tests/stress/custom-iterators.js
new file mode 100644 (file)
index 0000000..fd6413f
--- /dev/null
@@ -0,0 +1,322 @@
+// This test checks the behavior of custom iterable objects.
+
+var returnCalled = false;
+var iter = {
+    __key: 0,
+    next: function () {
+        return {
+            done: this.__key === 42,
+            value: this.__key++
+        };
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    return: function () {
+        returnCalled = true;
+    }
+};
+var expected = 0;
+for (var value of iter) {
+    if (value !== expected++)
+        throw "Error: bad value: " + value;
+}
+if (returnCalled)
+    throw "Error: return is called.";
+
+
+
+var returnCalled = false;
+var iter = {
+    __key: 0,
+    next: function () {
+        return {
+            done: this.__key === 42,
+            value: this.__key++
+        };
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    return: function () {
+        returnCalled = true;
+        return {
+            done: true,
+            value: undefined
+        };
+    }
+};
+
+try {
+    for (var value of iter) {
+        throw "Error: Terminate iteration.";
+    }
+} catch (e) {
+    if (String(e) !== "Error: Terminate iteration.")
+        throw "Error: bad error thrown: " + e;
+}
+if (!returnCalled)
+    throw "Error: return is not called.";
+
+
+
+var returnCalled = false;
+var iter = {
+    __key: 0,
+    next: function () {
+        return {
+            done: this.__key === 42,
+            value: this.__key++
+        };
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    return: function () {
+        returnCalled = true;
+        return {
+            done: true,
+            value: undefined
+        };
+    }
+};
+
+for (var value of iter) {
+    break;
+}
+if (!returnCalled)
+    throw "Error: return is not called.";
+
+
+
+var returnCalled = false;
+var iter = {
+    __key: 0,
+    get next() {
+        throw "Error: looking up next.";
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    return: function () {
+        returnCalled = true;
+    }
+};
+try {
+    for (var value of iter) {
+        throw "Error: Iteration should not occur.";
+    }
+} catch (e) {
+    if (String(e) !== "Error: looking up next.")
+        throw "Error: bad error thrown: " + e;
+}
+if (returnCalled)
+    throw "Error: return is called.";
+
+
+
+var iter = {
+    __key: 0,
+    next: function () {
+        return {
+            done: this.__key === 42,
+            value: this.__key++
+        };
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    get return() {
+        throw "Error: looking up return."
+    }
+};
+try {
+    for (var value of iter) {
+        throw "Error: Terminate iteration.";
+    }
+} catch (e) {
+    if (String(e) !== "Error: looking up return.")
+        throw "Error: bad error thrown: " + e;
+}
+
+
+
+var returnCalled = false;
+var iter = {
+    __key: 0,
+    next: function () {
+        throw "Error: next is called."
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    return: function () {
+        returnCalled = true;
+        return {
+            done: true,
+            value: undefined
+        };
+    }
+};
+
+try {
+    for (var value of iter) {
+        throw "Error: Terminate iteration.";
+    }
+} catch (e) {
+    if (String(e) !== "Error: next is called.")
+        throw "Error: bad error thrown: " + e;
+}
+if (returnCalled)
+    throw "Error: return is called.";
+
+
+
+var returnCalled = false;
+var iter = {
+    __key: 0,
+    next: function () {
+        return { done: false, value: 42 };
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    return: function () {
+        returnCalled = true;
+        throw "Error: return is called.";
+    }
+};
+
+try {
+    for (var value of iter) {
+        throw "Error: Terminate iteration.";
+    }
+} catch (e) {
+    if (String(e) !== "Error: Terminate iteration.")
+        throw "Error: bad error thrown: " + e;
+}
+if (!returnCalled)
+    throw "Error: return is not called.";
+
+
+var returnCalled = false;
+var iter = {
+    __key: 0,
+    next: function () {
+        return { done: false, value: 42 };
+    },
+    [Symbol.iterator]: function () {
+        return this;
+    },
+    return: function () {
+        returnCalled = true;
+        throw "Error: return is called.";
+    }
+};
+try {
+    for (var value of iter) {
+        break;
+    }
+} catch (e) {
+    if (String(e) !== "Error: return is called.")
+        throw "Error: bad error thrown: " + e;
+}
+if (!returnCalled)
+    throw "Error: return is not called.";
+
+
+var primitives = [
+    undefined,
+    null,
+    42,
+    "string",
+    true,
+    Symbol("Cocoa")
+];
+
+function iteratorInterfaceErrorTest(notIteratorResult) {
+    var returnCalled = false;
+    var iter = {
+        __key: 0,
+        next: function () {
+            return notIteratorResult;
+        },
+        [Symbol.iterator]: function () {
+            return this;
+        },
+        return: function () {
+            returnCalled = true;
+            return undefined;
+        }
+    };
+    try {
+        for (var value of iter) {
+            throw "Error: Iteration should not occur.";
+        }
+    } catch (e) {
+        if (String(e) !== "TypeError: Iterator result interface is not an object.")
+            throw "Error: bad error thrown: " + e;
+    }
+    if (returnCalled)
+        throw "Error: return is called.";
+}
+
+function iteratorInterfaceErrorTestReturn(notIteratorResult) {
+    var returnCalled = false;
+    var iter = {
+        __key: 0,
+        next: function () {
+            return { done: false, value: 42 };
+        },
+        [Symbol.iterator]: function () {
+            return this;
+        },
+        return: function () {
+            returnCalled = true;
+            return notIteratorResult;
+        }
+    };
+    try {
+        for (var value of iter) {
+            throw "Error: Terminate iteration.";
+        }
+    } catch (e) {
+        if (String(e) !== "Error: Terminate iteration.")
+            throw "Error: bad error thrown: " + e;
+    }
+    if (!returnCalled)
+        throw "Error: return is not called.";
+}
+
+primitives.forEach(iteratorInterfaceErrorTest);
+primitives.forEach(iteratorInterfaceErrorTestReturn);
+
+
+function iteratorInterfaceBreakTestReturn(notIteratorResult) {
+    var returnCalled = false;
+    var iter = {
+        __key: 0,
+        next: function () {
+            return { done: false, value: 42 };
+        },
+        [Symbol.iterator]: function () {
+            return this;
+        },
+        return: function () {
+            returnCalled = true;
+            return notIteratorResult;
+        }
+    };
+    try {
+        for (var value of iter) {
+            break;
+        }
+    } catch (e) {
+        if (String(e) !== "TypeError: Iterator result interface is not an object.")
+            throw "Error: bad error thrown: " + e;
+    }
+    if (!returnCalled)
+        throw "Error: return is not called.";
+}
+
+primitives.forEach(iteratorInterfaceBreakTestReturn);
diff --git a/Source/JavaScriptCore/tests/stress/iterators-shape.js b/Source/JavaScriptCore/tests/stress/iterators-shape.js
new file mode 100644 (file)
index 0000000..f2718b2
--- /dev/null
@@ -0,0 +1,63 @@
+// This test checks the shape of builtin iterators.
+
+function iteratorShape(iter) {
+    if (iter.hasOwnProperty('next'))
+        throw "Error: iterator should not have next method.";
+    if (!iter.__proto__.hasOwnProperty('next'))
+        throw "Error: iterator prototype should have next method.";
+    if (typeof iter.__proto__.next !== "function")
+        throw "Error: iterator prototype should have next method.";
+}
+
+function sameNextMethods(iterators) {
+    var iterator = iterators[0];
+    for (var i = 1; i < iterators.length; ++i) {
+        if (iterator.next !== iterators[i].next)
+            throw "Error: next method is not the same.";
+    }
+}
+
+var array = ['Cocoa', 'Cappuccino', 'The des Alizes', 'Matcha', 'Kilimanjaro'];
+var iterator = array[Symbol.iterator]();
+iteratorShape(iterator);
+
+var keyIterator = array.keys();
+iteratorShape(keyIterator);
+
+var keyValueIterator = array.entries();
+iteratorShape(keyValueIterator);
+
+sameNextMethods([array[Symbol.iterator](), array.keys(), array.entries()]);
+
+var set = new Set(['Cocoa', 'Cappuccino', 'The des Alizes', 'Matcha', 'Kilimanjaro']);
+var iterator = set[Symbol.iterator]();
+iteratorShape(iterator);
+
+var keyIterator = set.keys();
+iteratorShape(keyIterator);
+
+var keyValueIterator = set.entries();
+iteratorShape(keyValueIterator);
+
+sameNextMethods([set[Symbol.iterator](), set.keys(), set.entries()]);
+
+var map = new Map();
+[
+    [ 'Cocoa', 2, ],
+    [ 'Cappuccino', 0 ],
+    [ 'The des Alizes', 3 ],
+    [ 'Matcha', 2 ],
+    [ 'Kilimanjaro', 1]
+].forEach(function ([ key, value ]) {
+    map.set(key, value);
+});
+var iterator = map[Symbol.iterator]();
+iteratorShape(iterator);
+
+var keyIterator = map.keys();
+iteratorShape(keyIterator);
+
+var keyValueIterator = map.entries();
+iteratorShape(keyValueIterator);
+
+sameNextMethods([map[Symbol.iterator](), map.keys(), map.entries()]);
diff --git a/Source/JavaScriptCore/tests/stress/map-iterators-next.js b/Source/JavaScriptCore/tests/stress/map-iterators-next.js
new file mode 100644 (file)
index 0000000..5329eae
--- /dev/null
@@ -0,0 +1,113 @@
+// This test checks the behavior of the iterator.next methods on Map objects
+
+var testArray = [1,2,3,4,5,6]
+var testMap = new Map();
+for (var [key, value] of testArray.entries()) {
+    testMap.set(key, value);
+}
+var keys = testMap.keys();
+var i = 0;
+while (true) {
+    var {done, value: key} = keys.next();
+    if (done)
+        break;
+    if (key >= testArray.length)
+        throw "Error: bad value: " + key;
+    i++;
+}
+
+if (testMap.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = keys.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+// TODO: Map.prototype.values() is not exposed.
+//
+// var values = testMap.values();
+// var i = 0;
+// while (true) {
+//     var {done, value} = values.next();
+//     if (done)
+//         break;
+//     i++;
+//     if (testArray.indexOf(value) === -1)
+//         throw "Error: bad value: " + value;
+// }
+//
+// if (testMap.size !== i)
+//     throw "Error: bad value: " + i;
+//
+// var value = values.next().value;
+// if (value !== undefined)
+//     throw "Error: bad value: " + value;
+
+var entries = testMap.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (value !== testMap.get(key))
+        throw "Error: bad value: " + value + " " + testMap.get(key);
+    if (key >= testArray.length)
+        throw "Error: bad value: " + key;
+    i++;
+    if (testArray.indexOf(value) === -1)
+        throw "Error: bad value: " + value + " " + i;
+} while (!done);
+
+if (testMap.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+var entries = testMap.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (value !== testMap.get(key))
+        throw "Error: bad value: " + value + " " + testMap.get(key);
+    i++;
+    if (i % 4 === 0)
+        testMap.set(100000 + i, i);
+} while (!done);
+
+if (testMap.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+function otherKey(key) {
+    return (key + 1) % testArray.length;
+}
+
+var entries = testMap.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (value !== testMap.get(key))
+        throw "Error: bad value: " + value + " " + testMap.get(key);
+    i++;
+    if (i % 4 === 0)
+        testMap.delete(otherKey(key));
+} while (!done);
+
+if (testMap.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
diff --git a/Source/JavaScriptCore/tests/stress/set-iterators-next.js b/Source/JavaScriptCore/tests/stress/set-iterators-next.js
new file mode 100644 (file)
index 0000000..871dc7e
--- /dev/null
@@ -0,0 +1,117 @@
+// This test checks the behavior of the iterator.next methods on Set objects
+
+var testArray = [1,2,3,4,5,6]
+var testSet = new Set();
+for (var [key, value] of testArray.entries()) {
+    testSet.add(value);
+}
+var keys = testSet.keys();
+var i = 0;
+while (true) {
+    var {done, value: key} = keys.next();
+    if (done)
+        break;
+    if (testArray.indexOf(key) === -1)
+        throw "Error: bad value: " + key;
+    i++;
+}
+
+if (testSet.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = keys.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+// TODO: Set.prototype.values() is not exposed.
+//
+// var values = testSet.values();
+// var i = 0;
+// while (true) {
+//     var {done, value} = values.next();
+//     if (done)
+//         break;
+//     i++;
+//     if (testArray.indexOf(value) === -1)
+//         throw "Error: bad value: " + value;
+// }
+//
+// if (testSet.size !== i)
+//     throw "Error: bad value: " + i;
+//
+// var value = values.next().value;
+// if (value !== undefined)
+//     throw "Error: bad value: " + value;
+
+var entries = testSet.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (key !== value)
+        throw "Error: bad value: " + key + " " + value;
+    if (!testSet.has(value))
+        throw "Error: bad value: " + value;
+    if (!testSet.has(key))
+        throw "Error: bad value: " + key;
+    i++;
+    if (testArray.indexOf(value) === -1)
+        throw "Error: bad value: " + value + " " + i;
+} while (!done);
+
+if (testSet.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+var entries = testSet.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (key !== value)
+        throw "Error: bad value: " + key + " " + value;
+    if (!testSet.has(key))
+        throw "Error: bad value: " + value;
+    i++;
+    if (i % 4 === 0)
+        testSet.add(100000 + i);
+} while (!done);
+
+if (testSet.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;
+
+function otherKey(key) {
+    return (key + 1) % testArray.length;
+}
+
+var entries = testSet.entries();
+var i = 0;
+do {
+    var {done, value: entry} = entries.next();
+    if (done)
+        break;
+    var [key, value] = entry;
+    if (!testSet.has(key))
+        throw "Error: bad value: " + value + " " + testSet.get(key);
+    i++;
+    if (i % 4 === 0)
+        testSet.delete(otherKey(key));
+} while (!done);
+
+if (testSet.size !== i)
+    throw "Error: bad value: " + i;
+
+var value = entries.next().value;
+if (value !== undefined)
+    throw "Error: bad value: " + value;