kripken/emscripten — pull request #1985 by coolwanglu (2014-01-07 08:49:00+00:00)
Title: Transformation async functions

New version for #1963, fixes #1881.

### Description

This patch allows the usage of async functions such as `setTimeout` and async ajax, which is _transparent to the program_. The thread will be 'suspended' upon an async call, and it will be resumed with callbacks. States are preserved using clsoures. The method is inspired by streamline.js.

A new suffix `__async` is introduced in the library, which marks the functions as async. If any async function is used, the async transformation routine will be triggered. Extra information are dumped in the `pre` phase, and all async functions are found by analyzing the call graph.

Async functions must take a callback function as its 1st argument, I chose the first instead of the last because of the possibility of optional arguments. It is possible to extend to support arbitrary index.

An example test case has been added. A typical transformed function looks like this:

```
function _some_func(async_callback, arg1, arg2) {
var $local_variables...;
var label = 0;
(function async_some_func(async_returnvalue) { // async helper closure for `some_func`
while(1)switch(label) {
case ...: break;
case 1000:
label = 1001;
// make an async call
return async_func(async_some_func, args);
case 1001:
// resume here
// async_returnvalue came from async_func
...
}
})();
}
```

It works with setjmp / longjmp

`shell.js` has been modified to take care of the async version of _main

### Side effects

Extra information are dumped in the pre phase, we are callee's of the functions, for example

```
define some function (...) {
func1
func2
}
```

Without the patch, `func*` would be dumped by `emscripten.py`. Fortunately, seems that the compiler ignore whatever printed in the middle, so my hacky method does work. But maybe we need a convention for that.

This process introduces some overhead of finding the callee's, which is a linear scan with a regexp, I guess it's fine. The call graph analysis won't be triggered if no async function is used.

### Implementation

Most changes are in `analyze.js` and `compiler.js`. If a functions is found to be async, the `async` property (and a few others) is added to all the functions structures, and all the `return`s inside the function and all the calls to that function are also marked as async, which will be recognized by `jsify()`.

The process basically involves defining the closure and creating new labels. The former is similar as in ASM_JS and the latter is similar as in setjmp. I change a few `if(ASM_JS)` to `if(ASM_JS || the_current_function_is_async)`. Apparently the framework was not designed for async functions so the modifictions might look ugly.

### Issues
- It does not work with ASM.js and relooper, maybe we will need a javascript-level transformation like streamline.js
- Function pointers are not handled, we don't know if the target function is async or not during the compilation.
- It still needs lots of optimizations. Anyway it's only a first version which is intended to be integrated with existing parts. I'll create a new issue for this one.
- Library functions may be overrided, especially in the future we may provide sync api for file/network I/O. Maybe we still need javascript-level transformations
- Not sure if dynamic linking would work.
- Exceptions are not handled, we need a guard whenever we call a _real_ async function in the JS engine to catch the exception. Currently `exitRuntime` can be called only when `exit()` is never called. Maybe we will need a new event callback in `Module`, or try to catch exceptions.