Last active
December 17, 2015 05:08
-
-
Save bjouhier/5555142 to your computer and use it in GitHub Desktop.
Demonstrates crash with V8 generators
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function isGenerator(val) { | |
return Object.prototype.toString.call(val) === "[object Generator]"; | |
} | |
var PENDING = {}; | |
function run(fn, args, idx) { | |
var cb = args[idx], | |
g; | |
function resume(err, val) { | |
while (g) { | |
try { | |
val = err ? g. | |
throw (err) : g.send(val); | |
val = val.done ? undefined : val.value; | |
err = null; | |
// if we get PENDING, the current call completed with a pending I/O | |
// resume will be called again when the I/O completes. So just save the context and return here. | |
if (val === PENDING) { | |
return; | |
} | |
// if we get [PENDING, e, r], the current call invoked its callback synchronously | |
// we just loop to send/throw what the callback gave us. | |
if (val && val[0] === PENDING) { | |
err = val[1]; | |
val = val[2]; | |
} | |
// else, if g yielded a value which is not a generator, g is done. | |
// so we unwind it we send val to the parent generator (or through cb if we are at the top) | |
else if (!isGenerator(val)) { | |
//g.close(); | |
g = g.prev; | |
} | |
// else, we got a new generator which means that g called another generator function | |
// the new generator become current and we loop with g.send(undefined) (equiv to g.next()) | |
else { | |
val.prev = g; | |
g = val; | |
val = undefined; | |
} | |
} catch (ex) { | |
// the send/throw call failed. | |
// we unwind the current generator and we rethrow into the parent generator (or through cb if at the top) | |
//g.close(); | |
g = g.prev; | |
err = makeError(ex); | |
val = undefined; | |
} | |
} | |
// we have exhausted the stack of generators. | |
// return the result or error through the callback. | |
try { | |
cb(err, val); | |
} catch (ex) { | |
trap(ex); | |
} | |
} | |
// set resume as the new callback | |
args[idx] = resume; | |
// call fn to get the initial generator | |
g = fn.apply(this, args); | |
// start the resume loop | |
resume(); | |
} | |
function create(fn, idx) { | |
function F() { | |
//if (arguments[idx] == null) return future.call(this, F, arguments, idx); | |
return run.call(this, fn, arguments, idx); | |
}; | |
// Memoize the original function for fast passing later | |
F.gstreamlineFunction = fn; | |
return F; | |
} | |
function invoke(that, fn, args, idx) { | |
// Resolve the function to be called | |
if (typeof fn !== 'function') { | |
if (typeof that === 'function' && that.gstreamlineFunction && fn === 'call') { | |
return that.gstreamlineFunction.apply(args[0], args.slice(1)); | |
} | |
fn = that[fn]; | |
} | |
// If we're waiting on a fstreamline.create function we can just call it directly instead | |
if (fn.gstreamlineFunction) return fn.gstreamlineFunction.apply(that, args); | |
// Set things up so that call returns: | |
// * PENDING if it completes with a pending I/O (and cb will be called later) | |
// * [PENDING, e, r] if the callback is called synchronously. | |
var result = PENDING, | |
sync = true; | |
var cb = args[idx]; | |
args[idx] = function(e, r) { | |
if (sync) { | |
result = [PENDING, e, r]; | |
} else { | |
cb(e, r); | |
} | |
} | |
fn.apply(that, args); | |
sync = false; | |
return result; | |
} | |
function fib(n) { | |
return n <= 1 ? 1 : fib(n - 1) + fib(n - 2); | |
} | |
var bench = create(function *bench(prefix, n, loop, modulo, asyncFn, _) { | |
var fibo_ = create(fibo, 0); | |
var count = 0; | |
function *fibo(_, nn) { | |
if (modulo && count++ % modulo === 0)(yield invoke(null, asyncFn, [_], 0)); | |
if (nn <= 1) yield(1); | |
yield((yield fibo(_, nn - 1)) + (yield fibo(_, nn - 2))); | |
} | |
var expected = fib(n); | |
var t0 = Date.now(); | |
for (var i = 0; i < loop; i++) { | |
var got = (yield fibo(_, n)); | |
if (got !== expected) throw new Error("bench failed: got " + got + ', expected ' + expected); | |
} | |
var tabs = (prefix.length < 7) ? '\t\t' : '\t'; | |
console.log(prefix + ':' + tabs + (Date.now() - t0) + "ms"); | |
yield(count); | |
}, 5); | |
bench("generators", 25, 1, 3, setImmediate, function(err) { | |
if (err) throw err; | |
console.log("BENCH DONE"); | |
}) |
I've filed down the test case: http://code.google.com/p/v8/issues/detail?id=2681
Fixed, and updated my node mirror. Thanks for the report. If you find something else, please use v8's issue tracker; thanks :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is what I get with gdb: