Skip to content

Commit

Permalink
Support the arguments object.
Browse files Browse the repository at this point in the history
Closes #4.
  • Loading branch information
benjamn committed Oct 10, 2013
1 parent e79518b commit 95067d9
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 4 deletions.
45 changes: 42 additions & 3 deletions lib/visit.js
Expand Up @@ -27,9 +27,6 @@ function visitNode(node) {

node.generator = false;

// TODO Ensure that the context is named uniquely.
var contextId = b.identifier("$ctx");

if (node.expression) {
// Transform expression lambdas into normal functions.
node.expression = false;
Expand All @@ -38,8 +35,19 @@ function visitNode(node) {
]);
}

// TODO Ensure these identifiers are named uniquely.
var contextId = b.identifier("$ctx");
var argsId = b.identifier("$args");
var shouldAliasArguments = renameArguments(node, argsId);
var vars = hoist(node);

if (shouldAliasArguments) {
vars = vars || b.variableDeclaration("var", []);
vars.declarations.push(b.variableDeclarator(
argsId, b.identifier("arguments")
));
}

var emitter = new Emitter(contextId);
emitter.explode(node.body);

Expand All @@ -58,3 +66,34 @@ function visitNode(node) {

node.body = b.blockStatement(outerBody);
}

function renameArguments(func, argsId) {
var didReplaceArguments = false;
var hasImplicitArguments = false;

types.traverse(func, function(node) {
if (node === func) {
hasImplicitArguments = !this.scope.lookup("arguments");
} else if (n.Function.check(node)) {
return false;
}

if (n.Identifier.check(node) && node.name === "arguments") {
var isMemberProperty =
n.MemberExpression.check(this.parent.node) &&
this.name === "property" &&
!this.parent.node.computed;

if (!isMemberProperty) {
this.replace(argsId);
didReplaceArguments = true;
return false;
}
}
});

// If the traversal replaced any arguments identifiers, and those
// identifiers were free variables, then we need to alias the outer
// function's arguments object to the variable named by argsId.
return didReplaceArguments && hasImplicitArguments;
}
85 changes: 84 additions & 1 deletion test/tests.es6.js
Expand Up @@ -588,4 +588,87 @@ describe("function declaration hoisting", function() {
check(gen(3), [4, 1, "function", 5]);
check(gen(4), [5, "undefined", 8]);
});
});
});

describe("the arguments object", function() {
it("should work in simple variadic functions", function() {
function *sum() {
var result = 0;

for (var i = 0; i < arguments.length; ++i) {
yield result += arguments[i];
}

return result;
}

check(sum(1, 2, 3), [1, 3, 6], 6);
check(sum(9, -5, 3, 0, 2), [9, 4, 7, 7, 9], 9);
});

it("should alias function parameters", function() {
function *gen(x, y) {
yield x;
++arguments[0];
yield x;

yield y;
--arguments[1];
yield y;

var temp = y;
y = x;
x = temp;

yield x;
yield y;
}

check(gen(3, 7), [3, 4, 7, 6, 6, 4]);
check(gen(10, -5), [10, 11, -5, -6, -6, 11]);
});

it("should be shadowable by explicit declarations", function() {
function *asParameter(x, arguments) {
yield x + arguments;
}

check(asParameter(4, 5), [9]);
check(asParameter("asdf", "zxcv"), ["asdfzxcv"]);

function *asVariable(x) {
yield arguments;
var arguments = x + 1;
yield arguments;
}

check(asVariable(4), [void 0, 5]);
check(asVariable("asdf"), [void 0, "asdf1"]);
});

it("should not get confused by properties", function() {
function *gen(obj) {
yield obj.arguments;
obj.arguments = "oyez";
yield obj;
}

check(gen({ arguments: 42 }), [42, { arguments: "oyez" }]);
});

it("supports .callee", function() {
function *gen(doYield) {
yield 1;
if (doYield) {
yield 2;
} else {
yield 3
yield* arguments.callee(true);
yield 4
}
yield 5;
}

check(gen(false), [1, 3, 1, 2, 5, 4, 5]);
});
});

0 comments on commit 95067d9

Please sign in to comment.