~jbicha/ubuntu/oneiric/gjs/1.29.18

« back to all changes in this revision

Viewing changes to modules/promise.js

  • Committer: Bazaar Package Importer
  • Author(s): Micah Gersten
  • Date: 2010-08-12 00:22:06 UTC
  • mfrom: (1.3.4 sid)
  • Revision ID: james.westby@ubuntu.com-20100812002206-zc5bfuqte3of6j2t
Tags: 0.7.1-1ubuntu1
* Merge from debian unstable. (LP: #616598)  Remaining changes:
  + debian/control: 
    - Add Ubuntu VCS info
    - Drop libmozjs Build-Depends; Ubuntu does not have this library.
    - Drop chrpath Build-Depends; we need to keep the RPATH because libmozjs
      is not a system library in Ubuntu.
    - Add xulrunner{,-dev}:Depends to binary packages
    - Bump xulrunner-dev build depends to 1.9.2
    - Build-depend on unversioned automake
  + debian/rules:
    - Generate strict dependencies on xulrunner.
    - Apply patch to fix libtool's handling of --as-needed
    - Add -Wl,--as-needed to LDFLAGS
  + debian/libgjs0a.lintian-overrides:
    - Override the entire rpath tag, not just for the specific libraries.
      The full lintian warning will change with each xulrunner version.
  + debian/ltmain-add-as-needed.patch
    - Fix libtool's ordering of --as-needed argument.
  + add debian/patches/01_disable_memcheck.patch
    - Don't make leaks fatal, see b.g.o #616193
  + add debian/patches/02_gobject_introspection.patch:
    - Use gobject-introspection 0.9
* Add debian/patches/03_libmozjs_private.patch (originally in 0.5-1ubuntu1)
  - Move @JS_PACKAGE@ libmozjs to Requires.private to prevent applications
    unnecessarily linking with libmozjs

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2009-2010 litl, LLC
 
2
 *
 
3
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 
4
 * of this software and associated documentation files (the "Software"), to deal
 
5
 * in the Software without restriction, including without limitation the rights
 
6
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
7
 * copies of the Software, and to permit persons to whom the Software is
 
8
 * furnished to do so, subject to the following conditions:
 
9
 *
 
10
 * The above copyright notice and this permission notice shall be included in
 
11
 * all copies or substantial portions of the Software.
 
12
 *
 
13
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
14
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
15
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
17
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 
18
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 
19
 * THE SOFTWARE.
 
20
 */
 
21
 
 
22
 
 
23
/* Promises represent a value which will be computed in the future,
 
24
 * although it may not be available yet.
 
25
 *
 
26
 * An "async calls" convention is then built on promises, in
 
27
 * async.js. It is generally possible to write code using only the
 
28
 * Async API without explicitly using Promise.  It's also possible
 
29
 * (but often more typing) to write code with explicit Promise
 
30
 * manipulation. One or the other may be nicer in a given instance.
 
31
 *
 
32
 * Convention: when an API returns a promise, that promise should be
 
33
 * "kicked off" already (the thread or main loop source should be
 
34
 * started). It should not be necessary to invoke get() on the promise
 
35
 * to get the computation going.
 
36
 *
 
37
 * Convention: it is OK for Promise.get() to invoke the onReturn or
 
38
 * onError synchronously (_before_ the get() returns). This can
 
39
 * occasionally be surprising, but the alternatives are not
 
40
 * good either and the synchronous result can be useful.
 
41
 */
 
42
 
 
43
/** default onError handler, makes debugging missing callbacks easier.
 
44
 * It is a bug if this handler gets called.
 
45
 */
 
46
const DEFAULT_ONERROR = function(e) {
 
47
    logError(e, "No onError handler");
 
48
};
 
49
 
 
50
/* This is publicly exported from async.js, but is defined here to
 
51
 * avoid a circular dependency.
 
52
 *
 
53
 * The issue is that we want async.js to build on Promise, but as
 
54
 * a nice tweak want Promise.get to be an async call as defined
 
55
 * in async.js
 
56
 */
 
57
let _asyncFunc = function(f) {
 
58
    /* Create a task from an Asynchronous Function, providing 'this' as
 
59
     * the first argument. */
 
60
    f.asyncCall = function() {
 
61
        let params = Array.slice(arguments);
 
62
        let me = params[0];
 
63
        params[0] = f;
 
64
        return _asyncCall.apply(me, params);
 
65
    };
 
66
    return f;
 
67
};
 
68
 
 
69
/* This is publicly exported from async.js, but is defined here to
 
70
 * avoid a circular dependency.
 
71
 *
 
72
 * _asyncCall is needed by _asyncFunc above)
 
73
 */
 
74
let _asyncCall = function(f) {
 
75
    let params = Array.slice(arguments, 1); // take off 'f'
 
76
    let promise = new Promise();
 
77
    let onReturn = function(v) { promise.putReturn(v); };
 
78
    let onError  = function(e) { promise.putError(e); };
 
79
    params.unshift(onReturn, onError);
 
80
    f.apply(this /* preserve 'this' */, params);
 
81
    return promise;
 
82
};
 
83
 
 
84
/** Prototype for a Promise.
 
85
 *
 
86
 *  A promise is a value (or error) that may not be available yet.
 
87
 *  To obtain this value, we may need to return to the main loop
 
88
 *  or run a main loop recursively.
 
89
 *
 
90
 * @constructor
 
91
 */
 
92
function Promise() { this._queue = []; }
 
93
 
 
94
Promise.prototype = {
 
95
    get waiting() {
 
96
        return this.hasOwnProperty('_queue');
 
97
    },
 
98
 
 
99
    /** Invoke 'onReturn' on the result of this promise, when
 
100
     * it completes.  Any error will invoke 'onError' with the thrown
 
101
     * exception.  (The get method of a Promise is itself
 
102
     * an Asynchronous Function, see async.js)
 
103
     */
 
104
    get : _asyncFunc(function(onReturn, onError) {
 
105
        if (!this.waiting)
 
106
            throw new Error("get after get (should be cached!)");
 
107
        onError = onError || DEFAULT_ONERROR; // catch bugs
 
108
        /* no value available yet, queue continuations. */
 
109
        this._queue.push({ onReturn: onReturn, onError: onError });
 
110
    }),
 
111
 
 
112
    /** Set a normal value of a promise (only callable once), invoking any
 
113
     * queued onReturn continuations as necessary.
 
114
     */
 
115
    putReturn : function(returnValue) {
 
116
        if (!this.waiting)
 
117
            throw new Error("putReturn after put");
 
118
        // mark this promise as no longer 'waiting'
 
119
        let queue = this._queue;
 
120
        delete this._queue;
 
121
        // prevent further queuing
 
122
        this.get = _asyncFunc(function(onReturn, onError) {
 
123
            onReturn(returnValue);
 
124
        });
 
125
        // okay, now invoke queued callbacks.
 
126
        for each (let cb in queue) {
 
127
            try {
 
128
                cb.onReturn(returnValue);
 
129
            } catch (e) {
 
130
                logError(e, "Error in onReturn callback");
 
131
                // but make sure all other callbacks are still invoked.
 
132
            }
 
133
        }
 
134
    },
 
135
    /** Set an error value of a promise (only callable once), invoking any
 
136
     * queued onError continuations as necessary.
 
137
     */
 
138
    putError : function(errorValue) {
 
139
        if (!this.waiting)
 
140
            throw new Error("putError after put");
 
141
        // mark this promise as no longer 'waiting'
 
142
        let queue = this._queue;
 
143
        delete this._queue;
 
144
        // prevent further queuing
 
145
        this.get = _asyncFunc(function(onReturn, onError) {
 
146
            onError(errorValue);
 
147
        });
 
148
        // okay, now invoke queued callbacks.
 
149
        for each (let cb in queue) {
 
150
            try {
 
151
                cb.onError(errorValue);
 
152
            } catch (e) {
 
153
                logError(e, "Error in onError callback");
 
154
                // but make sure all other callbacks are still invoked.
 
155
            }
 
156
        }
 
157
    },
 
158
 
 
159
    /** Utility method: fire off the promise, don't wait for a result (but
 
160
     * log any error which occurs).
 
161
     */
 
162
    fireAndForget: function() {
 
163
        this.get(function(){}, DEFAULT_ONERROR);
 
164
    },
 
165
 
 
166
    /** Utility method: sets our return (or error) to the result of
 
167
     * another promise. Allows easily chaining promises.  If you
 
168
     * putPromisedReturn(undefined) (or with no args) then it is
 
169
     * equivalent to putReturn(undefined), it immediately completes
 
170
     * the promise but with no result value.
 
171
     */
 
172
    putPromisedReturn : function(promiseOfReturn) {
 
173
        let promise = this;
 
174
        if (promiseOfReturn !== undefined) {
 
175
            promiseOfReturn.get(function(v) {
 
176
                                    promise.putReturn(v);
 
177
                                },
 
178
                                function(e) {
 
179
                                    promise.putError(e);
 
180
                                });
 
181
        } else {
 
182
            promise.putReturn();
 
183
        }
 
184
    },
 
185
 
 
186
    toString: function() {
 
187
        return "[Promise]";
 
188
    }
 
189
};
 
190
 
 
191
/** Create a Promise representing the future construction of an object using
 
192
 * the given constructor (first arg) and arguments array.  The value of the
 
193
 * promise is the fully-constructed object.
 
194
 *
 
195
 * The constructor must be a special "async constructor" which has
 
196
 * onComplete and onError functions as the first two args.
 
197
 *
 
198
 * @returns a new promise, with newly-constructed object as expected value
 
199
 */
 
200
let fromConstructor = function(constructor, params) {
 
201
    params = params || [];
 
202
    let newobj = {
 
203
        __proto__: constructor.prototype,
 
204
        constructor: constructor
 
205
    };
 
206
 
 
207
    let promise = new Promise();
 
208
    // return the constructed object when the constructor completes
 
209
    let onComplete = function() { promise.putReturn(newobj); };
 
210
    let onError  = function(e) { promise.putError(e); };
 
211
    params.unshift(onComplete, onError);
 
212
 
 
213
    constructor.apply(newobj, params);
 
214
    return promise;
 
215
};
 
216
 
 
217
/** Converts a synchronous function into a promise. This is mostly
 
218
 * useful for testing, since the promise is never actually deferred,
 
219
 * of course. In fact the function gets called immediately.
 
220
 * We don't do anything with threads or the main loop here.
 
221
 *
 
222
 *  @returns a new promise, with value set to result of invoking function
 
223
 */
 
224
let fromSync = function(f, params) {
 
225
    params = params || [];
 
226
    let promise = new Promise();
 
227
    try {
 
228
        let v = f.apply(this, params);
 
229
        promise.putReturn(v);
 
230
    } catch (e) {
 
231
        promise.putError(e);
 
232
    }
 
233
    return promise;
 
234
};
 
235
 
 
236
/** Converts a value to an already-completed promise.
 
237
 * This is useful when you have a synchronous result
 
238
 * already available and want to return it through an
 
239
 * abstract API that returns a Promise.
 
240
 *
 
241
 * @returns a new promise with the given value already set
 
242
 */
 
243
let fromValue = function(v) {
 
244
    let promise = new Promise();
 
245
    promise.putReturn(v);
 
246
    return promise;
 
247
};
 
248
 
 
249
let _oneGeneratorStep = function(g, retval, isException, generatorResultPromise) {
 
250
    try {
 
251
        /* get the next asynchronous task to execute from the generator */
 
252
        let promise = (isException) ? g.throw(retval) : g.send(retval);
 
253
        /* execute it, with a continuation which will send the result
 
254
         * back to the generator (whether normal or exception) and
 
255
         * loop (with a tail call). */
 
256
        promise.get(function(v) {
 
257
                        _oneGeneratorStep(g, v, false, generatorResultPromise);
 
258
                    }, function(e) {
 
259
                        _oneGeneratorStep(g, e, true, generatorResultPromise);
 
260
                    });
 
261
    } catch (e) {
 
262
        /* before handling the exception, close the generator */
 
263
        try {
 
264
            g.close();
 
265
        } catch (ee) {
 
266
            /* same semantics as javascript: exception thrown in
 
267
             * finally clause overrides any other exception or
 
268
             * return value. */
 
269
            generatorResultPromise.putError(ee);
 
270
            return;
 
271
        }
 
272
        if (e === StopIteration) {
 
273
            /* generator exited without returning a value. */
 
274
            generatorResultPromise.putReturn(); /* done */
 
275
        } else if ('_asyncRetval' in e) {
 
276
            /* generator exited returning a value. */
 
277
            generatorResultPromise.putReturn(e._asyncRetval);
 
278
        } else {
 
279
            /* generator threw an exception explicitly. */
 
280
            generatorResultPromise.putError(e);
 
281
        }
 
282
    }
 
283
};
 
284
 
 
285
/** Converts a generator function (with "this" and array of params)
 
286
 * into a promise that promises the error or return value of the
 
287
 * generator function.
 
288
 *
 
289
 * A generator function should contain statements of the form:
 
290
 *
 
291
 * try {
 
292
 *   let promiseResult = yield promise;
 
293
 * } catch (promiseError) {
 
294
 * }
 
295
 *
 
296
 * At each such yield, the fromGenerator() driver will take
 
297
 * back control, let the promise complete, then pass back
 
298
 * control giving the result of the promise (or throwing the error
 
299
 * from the promise).
 
300
 *
 
301
 * The power of this is that the generator function can perform a series
 
302
 * of async actions, blocking on each promise in turn, while still having
 
303
 * a nice apparent flow of control. In other words this is a great way
 
304
 * to implement a chain of async steps, where each depends on the previous.
 
305
 *
 
306
 * The generator function can return a value by calling
 
307
 * Promise.putReturnFromGenerator(), which is implemented somewhat hackily
 
308
 * by throwing a special kind of exception.  The result is that the
 
309
 * generator ends, and you can Promise.get() the returned value from
 
310
 * the promise that fromGenerator() gave you. If the generator just
 
311
 * falls off the end without calling Promise.putReturnFromGenerator(), the
 
312
 * promise will have an undefined value.
 
313
 *
 
314
 * @returns a new promise whose eventual value depends on the generator
 
315
 */
 
316
 
 
317
let fromGenerator = function(g, self, params) {
 
318
    params = params || [];
 
319
    let generatorResultPromise = new Promise();
 
320
 
 
321
    try {
 
322
        let generator = g.apply(self, params);
 
323
        _oneGeneratorStep(generator, undefined, false, generatorResultPromise);
 
324
    } catch (e) {
 
325
        /* catch exceptions invoking g() to create generator. */
 
326
        generatorResultPromise.putError(e);
 
327
    }
 
328
 
 
329
    return generatorResultPromise;
 
330
};
 
331
 
 
332
/** Calls putReturn() on the promise returned by fromGenerator(),
 
333
 * ending that generator and completing its promise.
 
334
 */
 
335
let putReturnFromGenerator = function(val) {
 
336
    throw { _asyncRetval: val };
 
337
};