“use strict”; module.exports = function(Promise,

apiRejection,
INTERNAL,
tryConvertToPromise,
Proxyable,
debug) {

var errors = require(“./errors”); var TypeError = errors.TypeError; var util = require(“./util”); var errorObj = util.errorObj; var tryCatch = util.tryCatch; var yieldHandlers = [];

function promiseFromYieldHandler(value, yieldHandlers, traceParent) {

for (var i = 0; i < yieldHandlers.length; ++i) {
    traceParent._pushContext();
    var result = tryCatch(yieldHandlers[i])(value);
    traceParent._popContext();
    if (result === errorObj) {
        traceParent._pushContext();
        var ret = Promise.reject(errorObj.e);
        traceParent._popContext();
        return ret;
    }
    var maybePromise = tryConvertToPromise(result, traceParent);
    if (maybePromise instanceof Promise) return maybePromise;
}
return null;

}

function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {

if (debug.cancellation()) {
    var internal = new Promise(INTERNAL);
    var _finallyPromise = this._finallyPromise = new Promise(INTERNAL);
    this._promise = internal.lastly(function() {
        return _finallyPromise;
    });
    internal._captureStackTrace();
    internal._setOnCancel(this);
} else {
    var promise = this._promise = new Promise(INTERNAL);
    promise._captureStackTrace();
}
this._stack = stack;
this._generatorFunction = generatorFunction;
this._receiver = receiver;
this._generator = undefined;
this._yieldHandlers = typeof yieldHandler === "function"
    ? [yieldHandler].concat(yieldHandlers)
    : yieldHandlers;
this._yieldedPromise = null;
this._cancellationPhase = false;

} util.inherits(PromiseSpawn, Proxyable);

PromiseSpawn.prototype._isResolved = function() {

return this._promise === null;

};

PromiseSpawn.prototype._cleanup = function() {

this._promise = this._generator = null;
if (debug.cancellation() && this._finallyPromise !== null) {
    this._finallyPromise._fulfill();
    this._finallyPromise = null;
}

};

PromiseSpawn.prototype._promiseCancelled = function() {

if (this._isResolved()) return;
var implementsReturn = typeof this._generator["return"] !== "undefined";

var result;
if (!implementsReturn) {
    var reason = new Promise.CancellationError(
        "generator .return() sentinel");
    Promise.coroutine.returnSentinel = reason;
    this._promise._attachExtraTrace(reason);
    this._promise._pushContext();
    result = tryCatch(this._generator["throw"]).call(this._generator,
                                                     reason);
    this._promise._popContext();
} else {
    this._promise._pushContext();
    result = tryCatch(this._generator["return"]).call(this._generator,
                                                      undefined);
    this._promise._popContext();
}
this._cancellationPhase = true;
this._yieldedPromise = null;
this._continue(result);

};

PromiseSpawn.prototype._promiseFulfilled = function(value) {

this._yieldedPromise = null;
this._promise._pushContext();
var result = tryCatch(this._generator.next).call(this._generator, value);
this._promise._popContext();
this._continue(result);

};

PromiseSpawn.prototype._promiseRejected = function(reason) {

this._yieldedPromise = null;
this._promise._attachExtraTrace(reason);
this._promise._pushContext();
var result = tryCatch(this._generator["throw"])
    .call(this._generator, reason);
this._promise._popContext();
this._continue(result);

};

PromiseSpawn.prototype._resultCancelled = function() {

if (this._yieldedPromise instanceof Promise) {
    var promise = this._yieldedPromise;
    this._yieldedPromise = null;
    promise.cancel();
}

};

PromiseSpawn.prototype.promise = function () {

return this._promise;

};

PromiseSpawn.prototype._run = function () {

this._generator = this._generatorFunction.call(this._receiver);
this._receiver =
    this._generatorFunction = undefined;
this._promiseFulfilled(undefined);

};

PromiseSpawn.prototype._continue = function (result) {

var promise = this._promise;
if (result === errorObj) {
    this._cleanup();
    if (this._cancellationPhase) {
        return promise.cancel();
    } else {
        return promise._rejectCallback(result.e, false);
    }
}

var value = result.value;
if (result.done === true) {
    this._cleanup();
    if (this._cancellationPhase) {
        return promise.cancel();
    } else {
        return promise._resolveCallback(value);
    }
} else {
    var maybePromise = tryConvertToPromise(value, this._promise);
    if (!(maybePromise instanceof Promise)) {
        maybePromise =
            promiseFromYieldHandler(maybePromise,
                                    this._yieldHandlers,
                                    this._promise);
        if (maybePromise === null) {
            this._promiseRejected(
                new TypeError(
                    "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)) +
                    "From coroutine:\u000a" +
                    this._stack.split("\n").slice(1, -7).join("\n")
                )
            );
            return;
        }
    }
    maybePromise = maybePromise._target();
    var bitField = maybePromise._bitField;
    ;
    if (((bitField & 50397184) === 0)) {
        this._yieldedPromise = maybePromise;
        maybePromise._proxy(this, null);
    } else if (((bitField & 33554432) !== 0)) {
        Promise._async.invoke(
            this._promiseFulfilled, this, maybePromise._value()
        );
    } else if (((bitField & 16777216) !== 0)) {
        Promise._async.invoke(
            this._promiseRejected, this, maybePromise._reason()
        );
    } else {
        this._promiseCancelled();
    }
}

};

Promise.coroutine = function (generatorFunction, options) {

if (typeof generatorFunction !== "function") {
    throw new TypeError("generatorFunction must be a function\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
}
var yieldHandler = Object(options).yieldHandler;
var PromiseSpawn$ = PromiseSpawn;
var stack = new Error().stack;
return function () {
    var generator = generatorFunction.apply(this, arguments);
    var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
                                  stack);
    var ret = spawn.promise();
    spawn._generator = generator;
    spawn._promiseFulfilled(undefined);
    return ret;
};

};

Promise.coroutine.addYieldHandler = function(fn) {

if (typeof fn !== "function") {
    throw new TypeError("expecting a function but got " + util.classString(fn));
}
yieldHandlers.push(fn);

};

Promise.spawn = function (generatorFunction) {

debug.deprecated("Promise.spawn()", "Promise.coroutine()");
if (typeof generatorFunction !== "function") {
    return apiRejection("generatorFunction must be a function\u000a\u000a    See http://goo.gl/MqrFmX\u000a");
}
var spawn = new PromiseSpawn(generatorFunction, this);
var ret = spawn.promise();
spawn._run(Promise.spawn);
return ret;

}; };