• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"use strict";
2module.exports = function(Promise,
3                          apiRejection,
4                          INTERNAL,
5                          tryConvertToPromise,
6                          Proxyable,
7                          debug) {
8var errors = require("./errors");
9var TypeError = errors.TypeError;
10var util = require("./util");
11var errorObj = util.errorObj;
12var tryCatch = util.tryCatch;
13var yieldHandlers = [];
14
15function promiseFromYieldHandler(value, yieldHandlers, traceParent) {
16    for (var i = 0; i < yieldHandlers.length; ++i) {
17        traceParent._pushContext();
18        var result = tryCatch(yieldHandlers[i])(value);
19        traceParent._popContext();
20        if (result === errorObj) {
21            traceParent._pushContext();
22            var ret = Promise.reject(errorObj.e);
23            traceParent._popContext();
24            return ret;
25        }
26        var maybePromise = tryConvertToPromise(result, traceParent);
27        if (maybePromise instanceof Promise) return maybePromise;
28    }
29    return null;
30}
31
32function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {
33    if (debug.cancellation()) {
34        var internal = new Promise(INTERNAL);
35        var _finallyPromise = this._finallyPromise = new Promise(INTERNAL);
36        this._promise = internal.lastly(function() {
37            return _finallyPromise;
38        });
39        internal._captureStackTrace();
40        internal._setOnCancel(this);
41    } else {
42        var promise = this._promise = new Promise(INTERNAL);
43        promise._captureStackTrace();
44    }
45    this._stack = stack;
46    this._generatorFunction = generatorFunction;
47    this._receiver = receiver;
48    this._generator = undefined;
49    this._yieldHandlers = typeof yieldHandler === "function"
50        ? [yieldHandler].concat(yieldHandlers)
51        : yieldHandlers;
52    this._yieldedPromise = null;
53    this._cancellationPhase = false;
54}
55util.inherits(PromiseSpawn, Proxyable);
56
57PromiseSpawn.prototype._isResolved = function() {
58    return this._promise === null;
59};
60
61PromiseSpawn.prototype._cleanup = function() {
62    this._promise = this._generator = null;
63    if (debug.cancellation() && this._finallyPromise !== null) {
64        this._finallyPromise._fulfill();
65        this._finallyPromise = null;
66    }
67};
68
69PromiseSpawn.prototype._promiseCancelled = function() {
70    if (this._isResolved()) return;
71    var implementsReturn = typeof this._generator["return"] !== "undefined";
72
73    var result;
74    if (!implementsReturn) {
75        var reason = new Promise.CancellationError(
76            "generator .return() sentinel");
77        Promise.coroutine.returnSentinel = reason;
78        this._promise._attachExtraTrace(reason);
79        this._promise._pushContext();
80        result = tryCatch(this._generator["throw"]).call(this._generator,
81                                                         reason);
82        this._promise._popContext();
83    } else {
84        this._promise._pushContext();
85        result = tryCatch(this._generator["return"]).call(this._generator,
86                                                          undefined);
87        this._promise._popContext();
88    }
89    this._cancellationPhase = true;
90    this._yieldedPromise = null;
91    this._continue(result);
92};
93
94PromiseSpawn.prototype._promiseFulfilled = function(value) {
95    this._yieldedPromise = null;
96    this._promise._pushContext();
97    var result = tryCatch(this._generator.next).call(this._generator, value);
98    this._promise._popContext();
99    this._continue(result);
100};
101
102PromiseSpawn.prototype._promiseRejected = function(reason) {
103    this._yieldedPromise = null;
104    this._promise._attachExtraTrace(reason);
105    this._promise._pushContext();
106    var result = tryCatch(this._generator["throw"])
107        .call(this._generator, reason);
108    this._promise._popContext();
109    this._continue(result);
110};
111
112PromiseSpawn.prototype._resultCancelled = function() {
113    if (this._yieldedPromise instanceof Promise) {
114        var promise = this._yieldedPromise;
115        this._yieldedPromise = null;
116        promise.cancel();
117    }
118};
119
120PromiseSpawn.prototype.promise = function () {
121    return this._promise;
122};
123
124PromiseSpawn.prototype._run = function () {
125    this._generator = this._generatorFunction.call(this._receiver);
126    this._receiver =
127        this._generatorFunction = undefined;
128    this._promiseFulfilled(undefined);
129};
130
131PromiseSpawn.prototype._continue = function (result) {
132    var promise = this._promise;
133    if (result === errorObj) {
134        this._cleanup();
135        if (this._cancellationPhase) {
136            return promise.cancel();
137        } else {
138            return promise._rejectCallback(result.e, false);
139        }
140    }
141
142    var value = result.value;
143    if (result.done === true) {
144        this._cleanup();
145        if (this._cancellationPhase) {
146            return promise.cancel();
147        } else {
148            return promise._resolveCallback(value);
149        }
150    } else {
151        var maybePromise = tryConvertToPromise(value, this._promise);
152        if (!(maybePromise instanceof Promise)) {
153            maybePromise =
154                promiseFromYieldHandler(maybePromise,
155                                        this._yieldHandlers,
156                                        this._promise);
157            if (maybePromise === null) {
158                this._promiseRejected(
159                    new TypeError(
160                        "A value %s was yielded that could not be treated as a promise\u000a\u000a    See http://goo.gl/MqrFmX\u000a\u000a".replace("%s", String(value)) +
161                        "From coroutine:\u000a" +
162                        this._stack.split("\n").slice(1, -7).join("\n")
163                    )
164                );
165                return;
166            }
167        }
168        maybePromise = maybePromise._target();
169        var bitField = maybePromise._bitField;
170        ;
171        if (((bitField & 50397184) === 0)) {
172            this._yieldedPromise = maybePromise;
173            maybePromise._proxy(this, null);
174        } else if (((bitField & 33554432) !== 0)) {
175            Promise._async.invoke(
176                this._promiseFulfilled, this, maybePromise._value()
177            );
178        } else if (((bitField & 16777216) !== 0)) {
179            Promise._async.invoke(
180                this._promiseRejected, this, maybePromise._reason()
181            );
182        } else {
183            this._promiseCancelled();
184        }
185    }
186};
187
188Promise.coroutine = function (generatorFunction, options) {
189    if (typeof generatorFunction !== "function") {
190        throw new TypeError("generatorFunction must be a function\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
191    }
192    var yieldHandler = Object(options).yieldHandler;
193    var PromiseSpawn$ = PromiseSpawn;
194    var stack = new Error().stack;
195    return function () {
196        var generator = generatorFunction.apply(this, arguments);
197        var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
198                                      stack);
199        var ret = spawn.promise();
200        spawn._generator = generator;
201        spawn._promiseFulfilled(undefined);
202        return ret;
203    };
204};
205
206Promise.coroutine.addYieldHandler = function(fn) {
207    if (typeof fn !== "function") {
208        throw new TypeError("expecting a function but got " + util.classString(fn));
209    }
210    yieldHandlers.push(fn);
211};
212
213Promise.spawn = function (generatorFunction) {
214    debug.deprecated("Promise.spawn()", "Promise.coroutine()");
215    if (typeof generatorFunction !== "function") {
216        return apiRejection("generatorFunction must be a function\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
217    }
218    var spawn = new PromiseSpawn(generatorFunction, this);
219    var ret = spawn.promise();
220    spawn._run(Promise.spawn);
221    return ret;
222};
223};
224