"use strict"; module.exports = function (Promise, apiRejection, tryConvertToPromise, createContext) { var TypeError = require("./errors.js").TypeError; var inherits = require("./util.js").inherits; var PromiseInspection = Promise.PromiseInspection; function inspectionMapper(inspections) { var len = inspections.length; for (var i = 0; i < len; ++i) { var inspection = inspections[i]; if (inspection.isRejected()) { return Promise.reject(inspection.error()); } inspections[i] = inspection._settledValue; } return inspections; } function thrower(e) { setTimeout(function(){throw e;}, 0); } function castPreservingDisposable(thenable) { var maybePromise = tryConvertToPromise(thenable); if (maybePromise !== thenable && typeof thenable._isDisposable === "function" && typeof thenable._getDisposer === "function" && thenable._isDisposable()) { maybePromise._setDisposable(thenable._getDisposer()); } return maybePromise; } function dispose(resources, inspection) { var i = 0; var len = resources.length; var ret = Promise.defer(); function iterator() { if (i >= len) return ret.resolve(); var maybePromise = castPreservingDisposable(resources[i++]); if (maybePromise instanceof Promise && maybePromise._isDisposable()) { try { maybePromise = tryConvertToPromise( maybePromise._getDisposer().tryDispose(inspection), resources.promise); } catch (e) { return thrower(e); } if (maybePromise instanceof Promise) { return maybePromise._then(iterator, thrower, null, null, null); } } iterator(); } iterator(); return ret.promise; } function disposerSuccess(value) { var inspection = new PromiseInspection(); inspection._settledValue = value; inspection._bitField = 268435456; return dispose(this, inspection).thenReturn(value); } function disposerFail(reason) { var inspection = new PromiseInspection(); inspection._settledValue = reason; inspection._bitField = 134217728; return dispose(this, inspection).thenThrow(reason); } function Disposer(data, promise, context) { this._data = data; this._promise = promise; this._context = context; } Disposer.prototype.data = function () { return this._data; }; Disposer.prototype.promise = function () { return this._promise; }; Disposer.prototype.resource = function () { if (this.promise().isFulfilled()) { return this.promise().value(); } return null; }; Disposer.prototype.tryDispose = function(inspection) { var resource = this.resource(); var context = this._context; if (context !== undefined) context._pushContext(); var ret = resource !== null ? this.doDispose(resource, inspection) : null; if (context !== undefined) context._popContext(); this._promise._unsetDisposable(); this._data = null; return ret; }; Disposer.isDisposer = function (d) { return (d != null && typeof d.resource === "function" && typeof d.tryDispose === "function"); }; function FunctionDisposer(fn, promise, context) { this.constructor$(fn, promise, context); } inherits(FunctionDisposer, Disposer); FunctionDisposer.prototype.doDispose = function (resource, inspection) { var fn = this.data(); return fn.call(resource, resource, inspection); }; function maybeUnwrapDisposer(value) { if (Disposer.isDisposer(value)) { this.resources[this.index]._setDisposable(value); return value.promise(); } return value; } Promise.using = function () { var len = arguments.length; if (len < 2) return apiRejection( "you must pass at least 2 arguments to Promise.using"); var fn = arguments[len - 1]; if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a"); len--; var resources = new Array(len); for (var i = 0; i < len; ++i) { var resource = arguments[i]; if (Disposer.isDisposer(resource)) { var disposer = resource; resource = resource.promise(); resource._setDisposable(disposer); } else { var maybePromise = tryConvertToPromise(resource); if (maybePromise instanceof Promise) { resource = maybePromise._then(maybeUnwrapDisposer, null, null, { resources: resources, index: i }, undefined); } } resources[i] = resource; } var promise = Promise.settle(resources) .then(inspectionMapper) .then(function(vals) { promise._pushContext(); var ret; try { ret = fn.apply(undefined, vals); } finally { promise._popContext(); } return ret; }) ._then( disposerSuccess, disposerFail, undefined, resources, undefined); resources.promise = promise; return promise; }; Promise.prototype._setDisposable = function (disposer) { this._bitField = this._bitField | 262144; this._disposer = disposer; }; Promise.prototype._isDisposable = function () { return (this._bitField & 262144) > 0; }; Promise.prototype._getDisposer = function () { return this._disposer; }; Promise.prototype._unsetDisposable = function () { this._bitField = this._bitField & (~262144); this._disposer = undefined; }; Promise.prototype.disposer = function (fn) { if (typeof fn === "function") { return new FunctionDisposer(fn, this, createContext()); } throw new TypeError(); }; };