generate_hints: special case for sizeless anchor
[conkeror.git] / modules / coroutine.js
blob77b83de781bbc5b238c993b660b95e49f6a63c82
1 /**
2  * (C) Copyright 2008 Jeremy Maitin-Shepard
3  *
4  * Use, modification, and distribution are subject to the terms specified in the
5  * COPYING file.
6 **/
8 /**
9  * Coroutine (i.e. cooperative multithreading) implementation in
10  * JavaScript based on Mozilla JavaScript 1.7 generators.
11  *
12  * Before trying to understand this file, first read about generators
13  * as described here:
14  * https://developer.mozilla.org/en/New_in_JavaScript_1.7
15  *
16  * Additionally, here is a document describing another implementation
17  * of coroutines/cooperative multithreading in JavaScript based on
18  * generators that may be of interest:
19  * http://www.neilmix.com/2007/02/07/threading-in-javascript-17/
20  *
21  * === Introduction ===
22  *
23  * For the purposes of Conkeror, a coroutine is a generator function
24  * (i.e. a function that uses the yield keyword) that adheres to
25  * certain practices (described later in this file).
26  *
27  * As described in the "New in JavaScript 1.7" document, although a
28  * generator function `foo' can be invoked using the same syntax as
29  * any other function, i.e.:
30  *
31  * foo(a,b,c)
32  *
33  * this "function call" merely serves to bind the arguments (including
34  * the special `this' argument) without actually running any of the
35  * code specified in the defintion of foo, and return a special
36  * generator object. The generator object has three methods, `next',
37  * 'send', and 'close', that can be called to actually run code
38  * specified in the definition of the generator function.
39  *
40  * In Conkeror, a `coroutine' refers to a generator function that
41  * adheres to the practices described later in this file. In a
42  * JavaScript program, a coroutine (or any generator function)
43  * unfortunately cannot be distinguished from a normal function
44  * without actually invoking it. A `prepared coroutine' refers to a
45  * generator object obtained from calling a coroutine
46  * function. Generally when using this coroutine library, none of the
47  * methods of these generator objects should be called directly. The
48  * `is_coroutine' function can be used to check whether a given value
49  * is a generator object (not a generator function). This library
50  * generally assumes that any generator objects it is passed are
51  * proper prepared coroutines. If a generator function that does not
52  * adhere to the practices required of a coroutine is used with this
53  * library as a coroutine, undefined (and generally undesirable)
54  * behavior may occur.
55  *
56  * === Requirements for a coroutine ===
57  *
58  * In most ways, a coroutine function can be written like a normal
59  * function. Arbitrary computation can be done, including arbitrary
60  * calls to normal functions, exceptions can be thrown, and exceptions
61  * can be handled using try-catch-finally blocks.
62  *
63  * --- Return values ---
64  *
65  * One of the main differences from a normal function is that the
66  * `return' keyword cannot be used to return a value to the caller
67  * (which is necessarily either another coroutine function, or the
68  * coroutine was itself started in a new "thread" in which case the
69  * return value is ignored). The `return' keyword can still be used to
70  * return `undefined' to the caller. In order to return a value,
71  * though, the special syntax:
72  *
73  * yield co_return(<expr>);
74  *
75  * must be used in place of the normal syntax:
76  *
77  * return <expr>;
78  *
79  * --- Invoking another coroutine function synchronously ---
80  *
81  * Another very important operation is calling another coroutine
82  * function synchronously, meaning that control will not return to the
83  * caller (the current coroutine) until the specified coroutine has
84  * either returned or thrown an exception. Conceptually, the specified
85  * coroutine is run in the same "thread" as the current coroutine, as
86  * opposed to being invoked asynchronously, in which case it would be
87  * run in a new "thread". This is done using the syntax:
88  *
89  * yield <prepared-coroutine-expr>
90  *
91  * where <prepared-coroutine-expr> is some expression that evaluates to
92  * a generator object, most typically a direct call to a coroutine
93  * function in the form of
94  *
95  * yield foo(a,b,c)
96  *
97  * or
98  *
99  * yield obj.foo(a,b,c)
101  * in the case that "foo" is a coroutine method of some object
102  * `obj'.
104  * If the specified coroutine returns a value normally, the yield
105  * expression evaluates to that value. That is, using the the syntax
107  * var x = yield foo(a,b,c);
109  * if foo is a coroutine and returns a normal value, that value will
110  * be stored in `x'.
112  * Alternatively, if the specified coroutine throws an exception, the
113  * exception will be propagated out of the yield expression, and can
114  * optionally be handled using a try-catch-finally block. If it is not
115  * handled, it will be propagated to the caller of the current
116  * coroutine in the same way.
118  * Note that it is safe to invoke a normal function using `yield' as
119  * well as if it were a coroutine. That is, the syntax
121  * yield foo(a,b,c)
123  * can likewise be used if foo is a normal function, and the same
124  * return value and exception propagation semantics
125  * apply. (Technically, what is actually happenining is that if yield
126  * is passed a value that is not a generator object or one of the
127  * several other types of values that are handled specially like the
128  * return value of co_return, it simply returns the value back
129  * untouched to the coroutine function. Thus, if foo is a normal
130  * function and returns a value, the return value is passed to yield,
131  * which immediately passes it back. If it throws an exception, then
132  * due to the normal exception propagation, yield is never even
133  * called.)
135  * --- Current continutation/"thread" handle ---
137  * The special syntax
139  * var cc = yield CONTINUATION;
141  * can be used to obtain a special "continuation" object that serves
142  * as a sort of "handle" to the current thread. Note that while in a
143  * single "thread", even if not in the same coroutine function, the
144  * exact same "continuation" object will always be returned.
146  * The continuation object is used in conjuction with another special
147  * operation:
149  * yield SUSPEND;
151  * This operation suspends execution of the current "thread" until it
152  * is resumed using a reference to the continuation object for the
153  * "thread". There are two ways to resume executation. To resume
154  * execution normally, the syntax:
156  * cc(value)
158  * or
160  * cc() (equivalent to cc(undefined))
162  * can be used. This resumes execution and causes the yield SUSPEND
163  * expression to evaluate to the specified value. Alternatively, the
164  * syntax:
166  * cc.throw(e)
168  * can be used. This causes the specified exception `e' to be thrown
169  * from the yield SUSPEND expression.
171  * It is not valid to use either of these two operations on a
172  * continutation corresponding to a thread that is either currently
173  * running or has already terminated.
175  * Generally, any coroutine function that suspends the current
176  * "thread" should also arrange using some other asynchronous
177  * facility, such as a timer with a callback or an event handler, to
178  * resume the "thread" later. It should also arrange to resume the
179  * "thread" with an exception if an error of some sort occurs in lieu
180  * of simply not resuming the "thread" at all.
182  * It is not technically necessary to resume a "thread" after
183  * suspending it, but it generally should always be done, as otherwise
184  * important error handling code, including code in `finally' blocks,
185  * may not be run.
187  * === Invoking a coroutine asynchronously/spawning a new thread ===
189  * A coroutine function can be called asynchronously from either a
190  * normal function or a coroutine function. Conceptually, this is
191  * equivalent to spawning a new "thread" to run the specified
192  * coroutine function. This operation is done using the co_call
193  * function as follows:
195  * co_call(<prepared-coroutine-expr>)
197  * or, for example,
199  * co_call(foo(a,b,c))
201  * or
203  * co_call(function (){ yield foo(a,b,c); }())
205  * In the last example, by modifying the anonymous coroutine function,
206  * the return value of the coroutine foo, or an exception it throws,
207  * could be processed, which is not possible in the case that foo is
208  * called directly by co_call.
210  * The function co_call returns the continuation for the new "thread"
211  * created. The return value of the specified continuation is ignored,
212  * as are any exceptions it throws. The call to co_call returns as
213  * soon as the specified coroutine suspends execution for the first
214  * time, or completes.
215  **/
217 in_module(null);
219 function _return_value (x) {
220     this.value = x;
223 function co_return (x) {
224     return new _return_value(x);
227 const CONTINUATION = { toString: function () "[object CONTINUATION]" };
228 const SUSPEND = { toString: function () "[object SUSPEND]" };
231  * Returns true if the `obj' is a generator object. Returns false
232  * otherwise. It is assumed that only generator objects that are
233  * actually `prepared coroutines' (see above) will be used with this
234  * library.
235  **/
236 function is_coroutine (obj) {
237     return obj != null &&
238         typeof(obj) == "object" &&
239         typeof(obj.next) == "function" &&
240         typeof(obj.send) == "function";
243 function _do_call (f) {
245     /* Suspend immediately so that co_call can pass us the continuation object. */
246     var cc = yield;
248     /**
249      * Stack of (partially-run) prepared coroutines/generator objects
250      * that specifies the call-chain of the current coroutine function
251      * `f'.  Conceptually, `f' is at the top of the stack.
252      **/
253     var stack = [];
255     /**
256      * `y' and `throw_value' together specify how execution of `f' will be resumed.
257      * If `throw_value' is false, f.send(y) is called to resume execution normally.
258      * If `throw_value' is true, f.throw(y) is called to throw the exception `y' in `f'.
259      *
260      * Because `f' is initially a just-created prepared coroutine that has not run any
261      * code yet, we must start it by calling f.send(undefined) (which is equivalent to
262      * calling f.next()).
263      **/
264     var y = undefined;
265     var throw_value = false;
266     while (true) {
267         try { // We must capture any exception thrown by `f'
269             /**
270              * If `f' yields again after being resumed, the value
271              * passed to yield will be stored in `x'.
272              **/
273             let x;
275             if (throw_value) {
276                 throw_value = false;
277                 x = f.throw(y); // f.throw returns the next value passed to yield
278             } else
279                 x = f.send(y); // f.send also returns the next value passed to yield
281             if (x === CONTINUATION) {
282                 /**
283                  * The coroutine (`f') asked us to pass it a reference
284                  * to the current continuation.  We don't need to make
285                  * any adjustments to the call stack.
286                  **/
287                 y = cc;
288                 continue;
289             }
291             if (x === SUSPEND) {
292                 /**
293                  * The coroutine `f' asked us to suspend execution of
294                  * the current thread.  We do this by calling yield ourself.
295                  **/
296                 try {
297                     /* our execution will be suspended until send or throw is called on our generator object */
298                     x = yield;
300                     /**
301                      * Since no exception was thrown, user must have requested that we resume
302                      * normally using cc(value); we simply pass that value back to `f', which
303                      * asked us to suspend in the first place.
304                      **/
305                     y = x;
306                 } catch (e) {
307                     /**
308                      * User requested that we resume by throwing an exception, so we re-throw
309                      * the exception in `f'.
310                      **/
311                     throw_value = true;
312                     y = e;
313                 }
314                 continue;
315             }
317             if (is_coroutine(x)) {
318                 // `f' wants to synchronously call the coroutine `x'
319                 stack[stack.length] = f; // push the current coroutine `f' onto the call stack
320                 f = x; // make `x' the new top of the stack
321                 y = undefined; // `x` is a new coroutine so we must start it by passing `undefined'
322                 continue;
323             }
325             if (x instanceof _return_value) {
326                 // `f' wants to return a value
327                 f.close();
328                 if (stack.length == 0) {
329                     /**
330                      * `f' doesn't have a caller, so we simply ignore
331                      * the return value and terminate the thread.
332                      */
333                     return;
334                 }
335                 // Pop the caller of `f' off the top of the stack
336                 f = stack[stack.length - 1];
337                 stack.length--;
338                 // Pass the return value to the caller, which is now the current coroutine
339                 y = x.value;
340                 continue;
341             }
343             /**
344              * `f' yielded to us a value without any special
345              * interpretation. Most likely, this is due to `f' calling
346              * a normal function as if it were a coroutine, in which
347              * case `x' simply contains the return value of that
348              * normal function. Just return the value back to `f'.
349              **/
350             y = x;
351         } catch (e) {
352             /**
353              * `f' threw an exception. If `e' is a StopIteration
354              * exception, then `f' exited without returning a value
355              * (equivalent to returning a value of
356              * `undefined'). Otherwise, `f' threw or propagted a real
357              * exception.
358              **/
359             if (stack.length == 0) {
360                 /**
361                  * `f' doesn't have a caller, so regardless of whether
362                  * `f' exited normally or threw an exception, we
363                  * simply terminate the thread.
364                  */
365                 return;
366             }
367             // Pop the caller of `f' off the top of the stack
368             f = stack[stack.length - 1];
369             stack.length--;
370             if (e instanceof StopIteration)
371                 y = undefined; // propagate a return value of `undefined' to the caller
372             else {
373                 // propagate the exception to the caller
374                 y = e;
375                 throw_value = true;
376             }
377         }
378     }
382  * Invokes the specified coroutine. If the argument is not a (already
383  * prepared) coroutine, this function simply returns, doing
384  * nothing. Thus, the syntax:
386  * co_call(foo(a,b,c))
388  * can be used to call the function foo with the arguments [a,b,c]
389  * regardless of whether foo is a normal function or a coroutine
390  * function. Note, though, that using this syntax, if foo is a normal
391  * function and throws an exception, it will be propagated, while if
392  * foo is a coroutine, any exceptions it throws will be ignored.
394  **/
395 function co_call (f) {
396     if (!is_coroutine(f))
397         return;
399     /** The _do_call helper function is called to actually do all of
400      * the work.  `_do_call' is written as a generator function for
401      * implementation convenience.
402      **/
403     var g = _do_call(f);
404     g.next();
405     var cc = function (x) {
406         try {
407             // We resume execution of the thread by calling send on
408             // the generator object corresponding to our invocation of
409             // _do_call.
410             g.send(x);
411         } catch (e if e instanceof StopIteration) {}
412         catch (e) {
413             // Dump this error, because it indicates a programming error
414             dump_error(e);
415         }
416     };
417     cc.throw = function (x) {
418         try {
419             g.throw(x);
420         } catch (e if e instanceof StopIteration) {}
421         catch (e) {
422             // Dump this error, because it indicates a programming error
423             dump_error(e);
424         }
425     };
426     try {
427         g.send(cc);
428     } catch (e if e instanceof StopIteration) {}
429     catch (e) {
430         // Dump this error, because it indicates a programming error
431         dump_error(e);
432     }
433     return cc;
436 provide("coroutine");