Index: src/js/generator.js |
diff --git a/src/js/generator.js b/src/js/generator.js |
index 7f43656ebcf5c1fb4cfb5eca311e8381082f5c33..c88b15d2b46bb8a5a9d05679638b305def175a70 100644 |
--- a/src/js/generator.js |
+++ b/src/js/generator.js |
@@ -26,6 +26,23 @@ utils.Import(function(from) { |
// Generator functions and objects are specified by ES6, sections 15.19.3 and |
// 15.19.4. |
+// Explanation of the use of %GeneratorClose: |
+// |
+// There are two situations in which resuming a generator must result in |
+// closing it (in ES6 terms: setting [[GeneratorState]] to "completed"). |
+// - When the generator throws. We close it by calling %GeneratorClose in the |
+// catch blocks below. |
+// - When the generator returns a "done" iterator result. |
+// * For GeneratorObjectNext and GeneratorObjectThrow this can only happen |
+// when the generator hits a "final" yield (as introduced by the |
+// parser). A final yield already takes care of closing the generator |
+// itself, so we don't need to do it here. |
+// * For GeneratorObjectReturn this can also happen if the triggered return is |
+// not "caught" and terminates the generator. We handle this here by just |
+// always calling %GeneratorClose when the received result is "done". This |
+// may include cases where the generator has already been closed but doing |
+// it twice is harmless. |
+ |
function GeneratorObjectNext(value) { |
if (!IS_GENERATOR(this)) { |
throw MakeTypeError(kIncompatibleMethodReceiver, |
@@ -44,7 +61,35 @@ function GeneratorObjectNext(value) { |
} |
} else if (continuation == 0) { |
// Generator is already closed. |
- return { value: void 0, done: true }; |
+ return { value: UNDEFINED, done: true }; |
Dan Ehrenberg
2016/01/20 23:48:21
Not that this should be done in this patch, but sh
Benedikt Meurer
2016/01/21 05:09:02
Nice catch! Yes that should use %_CreateIterResult
|
+ } else { |
+ // Generator is running. |
+ throw MakeTypeError(kGeneratorRunning); |
+ } |
+} |
+ |
+ |
+function GeneratorObjectReturn(value) { |
+ if (!IS_GENERATOR(this)) { |
+ throw MakeTypeError(kIncompatibleMethodReceiver, |
+ '[Generator].prototype.return', this); |
+ } |
+ |
+ var continuation = %GeneratorGetContinuation(this); |
+ if (continuation > 0) { |
+ // Generator is suspended. |
+ DEBUG_PREPARE_STEP_IN_IF_STEPPING(this); |
+ try { |
+ let result = %_GeneratorReturn(this, value); |
+ if (result.done) %GeneratorClose(this); |
+ return result; |
+ } catch (e) { |
+ %GeneratorClose(this); |
+ throw e; |
+ } |
+ } else if (continuation == 0) { |
+ // Generator is already closed. |
+ return { value: value, done: true }; |
Benedikt Meurer
2016/01/21 05:09:02
Use %_CreateIterResultObject, as explained above.
|
} else { |
// Generator is running. |
throw MakeTypeError(kGeneratorRunning); |
@@ -78,9 +123,11 @@ function GeneratorObjectThrow(exn) { |
// ---------------------------------------------------------------------------- |
-// Both Runtime_GeneratorNext and Runtime_GeneratorThrow are supported by |
-// neither Crankshaft nor TurboFan, disable optimization of wrappers here. |
+// None of the three resume operations (Runtime_GeneratorNext, |
+// Runtime_GeneratorReturn, Runtime_GeneratorThrow) is supported by |
+// Crankshaft or TurboFan. Disable optimization of wrappers here. |
%NeverOptimizeFunction(GeneratorObjectNext); |
+%NeverOptimizeFunction(GeneratorObjectReturn); |
%NeverOptimizeFunction(GeneratorObjectThrow); |
// Set up non-enumerable functions on the generator prototype object. |
@@ -88,6 +135,7 @@ var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype; |
utils.InstallFunctions(GeneratorObjectPrototype, |
DONT_ENUM, |
["next", GeneratorObjectNext, |
+ "return", GeneratorObjectReturn, |
"throw", GeneratorObjectThrow]); |
%AddNamedProperty(GeneratorObjectPrototype, "constructor", |