Permalink
Browse files

merged upstream

  • Loading branch information...
2 parents df9f855 + ff55586 commit ff890db8cc823de26ea9e47f591cc0499d71fee1 @bjouhier committed Oct 13, 2012
Showing with 160 additions and 46 deletions.
  1. +16 −6 lib/spoon/block.js
  2. +7 −7 lib/spoon/cfg.js
  3. +12 −7 lib/spoon/renderer.js
  4. +1 −1 package.json
  5. +124 −25 test/asyncify-test.js
View
22 lib/spoon/block.js
@@ -111,20 +111,29 @@ Block.prototype.split = function split(at, root, asyncify, marker) {
this.successors = [];
// Add function declaration to the root block
- var fn = root.prepend('fn', [ next ]);
+ var fn = root.prepend('fn', [ next ]),
+ res = '__$r' + next.id;
+
+ root.prepend('var', [ res ]);
fn.name = '__$fn' + next.id;
fn.params = ['__$e', '__$r'];
fn.isExpression = false;
- next.instructions = [this.add('async-test-err')].concat(next.instructions);
+ next.prepend('async-prelude', [ res ]);
this.ended = false;
if (asyncify) {
- if (marker) {
- at.args[at.args.length - marker].args[0] = fn.name;
+ var getfn = this.add('get', [ fn.name ]);
+ if (marker >= 0) {
+ var removed = at.args.splice(at.args.length - marker, 1, getfn)[0],
+ index = removed.block.instructions.indexOf(removed);
+
+ if (index !== -1) {
+ removed.block.instructions.splice(index, 1);
+ }
} else {
- at.args.push(this.add('get', [ fn.name ]));
+ at.args.push(getfn);
}
this.instructions.push(at);
@@ -148,7 +157,8 @@ Block.prototype.split = function split(at, root, asyncify, marker) {
return {
next: next,
- fn: fn
+ fn: fn,
+ res: res
};
};
View
14 lib/spoon/cfg.js
@@ -106,7 +106,7 @@ Cfg.prototype.split = function split(at, root, asyncify, marker) {
if (this.asyncifyState[block.id]) return this.asyncifyState[block.id];
var info = block.split(at, root, asyncify, marker);
- this.asyncifyState[block.id] = info.fn;
+ this.asyncifyState[block.id] = info;
this.roots.push(info.next);
this.blocks.push(info.next);
@@ -116,21 +116,21 @@ Cfg.prototype.split = function split(at, root, asyncify, marker) {
// Traverse blocks starting from next, split on every frontier
info.next.frontier.forEach(function(block) {
- var fn = this.split(block.instructions[0], root, false);
+ var info = this.split(block.instructions[0], root, false, marker);
block.predecessors.forEach(function(pred) {
// Remove goto
var last = pred.instructions.pop();
pred.ended = false;
- pred.add('async-goto', [ pred.add('get', [ fn.name ]) ]);
+ pred.add('async-goto', [ pred.add('get', [ info.fn.name ]) ]);
pred.end();
pred.successors = [];
}, this);
block.predecessors = [];
}, this);
- return info.fn;
+ return info;
};
Cfg.prototype.asyncify = function asyncify(asts, options) {
@@ -251,15 +251,15 @@ Cfg.prototype.asyncify = function asyncify(asts, options) {
if (marker < 0 && !match(instr)) return;
// Split graph at instruction
- var fn = this.split(instr, instr.block.getRoot(), true, marker);
+ var info = this.split(instr, instr.block.getRoot(), true, marker);
- // Replace all instruction uses by __$r
+ // Replace all instruction uses by __$r[num]
instr.uses.forEach(function(use, i) {
if (i === instr.uses.length - 1) return;
use.args = use.args.map(function(arg) {
if (arg !== instr) return arg;
- var r = spoon.instruction.create(use.block, 'get', ['__$r']),
+ var r = spoon.instruction.create(use.block, 'get', [ info.res ]),
index = use.block.instructions.indexOf(use);
use.block.instructions = [].concat(
View
19 lib/spoon/renderer.js
@@ -204,8 +204,8 @@ Renderer.prototype.renderInstruction = function renderInstruction(instr) {
fn = this.renderAsyncReturn;
} else if (t === 'async-end') {
fn = this.renderAsyncEnd;
- } else if (t === 'async-test-err') {
- fn = this.renderAsyncTestErr;
+ } else if (t === 'async-prelude') {
+ fn = this.renderAsyncPrelude;
} else if (t === 'nop') {
fn = this.renderNop;
} else {
@@ -409,11 +409,16 @@ Renderer.prototype.renderAsyncGoto = function renderAsyncGoto(args) {
return ['return', ['call', ['dot', args[0], 'call'], [['name', 'this']]]];
};
-Renderer.prototype.renderAsyncTestErr = function renderAsyncTestErr(args) {
- return ['if', ['name', '__$e'], ['block',
- [['return', ['call',
- ['dot', ['name', '__$callback'], 'call'],
- [['name', 'this'],['name', '__$e']]]]]]];
+Renderer.prototype.renderAsyncPrelude = function renderAsyncPrelude(args) {
+ return ['if', ['name', '__$e'],
+ ['block', [
+ ['return', ['call', ['dot', ['name', '__$callback'], 'call'],
+ [['name', 'this'],['name', '__$e']]]]
+ ]],
+ ['block', [
+ ['assign', true, ['name', args[0]], ['name', '__$r']]
+ ]]
+ ];
};
Renderer.prototype.renderNop = function renderNop() {
View
2 package.json
@@ -1,6 +1,6 @@
{
"name": "spoon",
- "version": "0.0.6",
+ "version": "0.1.2",
"main": "lib/spoon",
"dependencies": {
"esprima": "~0.9.9",
View
149 test/asyncify-test.js
@@ -5,37 +5,50 @@ var spoon = require('..'),
uglify = require('uglify-js');
describe('Spoon', function() {
- function test(code, what) {
- var ast = esprima.parse(code.toString()),
- cfg = spoon.construct(ast);
+ describe('asyncify', function() {
+ function test(code, what) {
+ var ast = esprima.parse(code.toString()),
+ cfg = spoon.construct(ast);
- cfg.asyncify([esprima.parse(what || 'async')], {
- declaration: 'enable spoon'
- });
+ cfg.asyncify([esprima.parse(what || 'async')], {
+ declaration: 'enable spoon'
+ });
+
+ var out = spoon.render(cfg);
+ var code = uglify.uglify.gen_code(out, { beautify: true });
- var out = spoon.render(cfg);
- var code = uglify.uglify.gen_code(out, { beautify: true });
+ var res,
+ once = false;
+ vm.runInNewContext(code + ';\nfn(callback)', {
+ callback: function(err, r) {
+ assert.equal(err, null);
+ if (once) throw new Error('Called twice');
+ once = true;
- var res,
- once = false;
- vm.runInNewContext(code + ';\nfn(callback)', {
- callback: function(r) {
- if (once) throw new Error('Called twice');
- once = true;
+ res = r;
+ }
+ });
+ return res;
+ }
- res = r;
- }
+ it('should asyncify two-fold operation', function() {
+ var r = test(function fn(__$callback) {
+ "enable spoon";
+ function async(a, callback) {
+ callback(null, a * a);
+ }
+
+ return async(3) + async(4);
+ }, 'async');
+ assert.equal(r, 25);
});
- return res;
- }
- describe('asyncify', function() {
it('should asyncify method', function() {
var r = test(function fn(__$callback) {
"enable spoon";
var obj = {
async: function async(a, callback) {
- callback(a);
+ callback(null, a);
}
};
return obj.async(1);
@@ -47,7 +60,7 @@ describe('Spoon', function() {
var r = test(function fn(__$callback) {
"enable spoon";
function async(a, callback) {
- callback(1);
+ callback(null, 1);
}
return 1, async(1), 2;
});
@@ -58,7 +71,7 @@ describe('Spoon', function() {
var r = test(function fn(__$callback) {
"enable spoon";
function async(a, callback) {
- callback(a);
+ callback(null, a);
}
if (1 + 2 > 2) {
@@ -77,7 +90,7 @@ describe('Spoon', function() {
var r = test(function fn(__$callback) {
"enable spoon";
function async(a, b, callback) {
- callback(a + b);
+ callback(null, a + b);
}
for (var i = 0; i < 10; i++) {
@@ -90,11 +103,48 @@ describe('Spoon', function() {
r = assert.equal(r, 46);
});
+ it('should asyncify call in for loop #2', function() {
+ var r = test(function fn(__$callback) {
+ "enable spoon";
+ function async(a, b, callback) {
+ callback(null, a + b);
+ }
+
+ for (var i = async(0, 0); i < async(5, 5); i = async(i, 1)) {
+ var x = async(x || 0, 1);
+ }
+
+ return x + 1;
+ });
+
+ r = assert.equal(r, 11);
+ });
+
+ it('should asyncify call in property', function() {
+ var r = test(function fn(__$callback) {
+ "enable spoon";
+ function async(a, callback) {
+ callback(null, a);
+ }
+
+ var obj = { a: 123, b: 456, c: 789 },
+ x = 1;
+
+ for (var i in obj) {
+ x = async(obj[async(i)]) * async(x);
+ }
+
+ return x;
+ });
+
+ r = assert.equal(r, 44253432);
+ });
+
it('should asyncify call in do while loop', function() {
var r = test(function fn(__$callback) {
"enable spoon";
function async(a, b, callback) {
- callback(a + b);
+ callback(null, a + b);
}
var x = 0,
@@ -114,7 +164,7 @@ describe('Spoon', function() {
var r = test(function fn(__$callback) {
"enable spoon";
function async(a, b, callback) {
- callback(a + b);
+ callback(null, a + b);
}
var obj = { a : 1, b : 2 };
@@ -129,4 +179,53 @@ describe('Spoon', function() {
r = assert.equal(r, 4);
});
});
+
+ describe('marker', function() {
+ function test(code, what) {
+ var ast = esprima.parse(code.toString()),
+ cfg = spoon.construct(ast);
+
+ cfg.asyncify([esprima.parse(what || 'async')], {
+ declaration: 'enable spoon',
+ marker: '_'
+ });
+
+ var out = spoon.render(cfg);
+ var code = uglify.uglify.gen_code(out, { beautify: true });
+
+ var res,
+ once = false;
+ vm.runInNewContext(code + ';\nfn(callback)', {
+ callback: function(err, r) {
+ assert.equal(err, null);
+ if (once) throw new Error('Called twice');
+ once = true;
+
+ res = r;
+ }
+ });
+ return res;
+ }
+
+ it('should replace marker in property', function() {
+ var r = test(function fn(_) {
+ "enable spoon";
+ function async(_, a) {
+ "enable spoon";
+ return a;
+ }
+
+ var obj = { a: 123, b: 456, c: 789 },
+ x = 1;
+
+ for (var i in obj) {
+ x = async(_, obj[async(_, i)]) * async(_, x);
+ }
+
+ return x;
+ });
+
+ r = assert.equal(r, 44253432);
+ });
+ });
});

0 comments on commit ff890db

Please sign in to comment.