142 lines
3.5 KiB
JavaScript
142 lines
3.5 KiB
JavaScript
/**
|
|
* Copyright 2013-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
var Syntax = require('esprima-fb').Syntax;
|
|
var utils = require('../src/utils');
|
|
|
|
/**
|
|
* Replaces `undefined` with `(void 0)` when it appears as an rvalue and is
|
|
* undeclared in the lexical scope.
|
|
*
|
|
* Example:
|
|
*
|
|
* (function() {
|
|
* foo.undefined = bar;
|
|
* ({undefined: foo});
|
|
*
|
|
* var bar = undefined;
|
|
* bar = undefined;
|
|
* foo.bar = undefined;
|
|
* undefined.foo = bar;
|
|
* foo[undefined] = bar;
|
|
* ({foo: undefined});
|
|
* })(undefined);
|
|
*
|
|
* (function(undefined) { // declared here
|
|
* return undefined;
|
|
* })(1);
|
|
*
|
|
* (function() {
|
|
* var undefined; // declared here
|
|
* undefined = 1; // assignment to declared variable
|
|
* return undefined;
|
|
* })();
|
|
*
|
|
* (function(undefined) { // declared here
|
|
* return (function() {
|
|
* return undefined; // respects lexical scope
|
|
* })();
|
|
* })();
|
|
*
|
|
* Becomes:
|
|
*
|
|
* (function() {
|
|
* foo.undefined = bar;
|
|
* ({undefined: foo});
|
|
*
|
|
* var bar = (void 0);
|
|
* bar = (void 0);
|
|
* foo.bar = (void 0);
|
|
* (void 0).foo = bar;
|
|
* foo[(void 0)] = bar;
|
|
* ({foo: (void 0)});
|
|
* })((void 0));
|
|
*
|
|
* (function(undefined) { // declared here
|
|
* return undefined;
|
|
* })(1);
|
|
*
|
|
* (function() {
|
|
* var undefined; // declared here
|
|
* undefined = 1; // assignment to declared variable
|
|
* return undefined;
|
|
* })();
|
|
*
|
|
* (function(undefined) { // declared here
|
|
* return (function() {
|
|
* return undefined; // respects lexical scope
|
|
* })();
|
|
* })();
|
|
*
|
|
*
|
|
*
|
|
* NOTE: Any assignment to `undefined` where `undefined` is not declared in the
|
|
* lexical scope will result in an exception.
|
|
*/
|
|
|
|
function visitIdentifierUndefined(traverse, node, path, state) {
|
|
utils.catchup(node.range[1], state, function(value) {
|
|
return '(void 0)';
|
|
});
|
|
}
|
|
|
|
visitIdentifierUndefined.test = function(node, path, state) {
|
|
if (
|
|
node.type === Syntax.Identifier
|
|
&& node.name === 'undefined'
|
|
&& !utils.identWithinLexicalScope('undefined', state)
|
|
) {
|
|
if (path[0]) {
|
|
switch (path[0].type) {
|
|
case Syntax.FunctionDeclaration:
|
|
case Syntax.FunctionExpression:
|
|
case Syntax.ArrowFunctionExpression:
|
|
// skips: function params
|
|
if (node !== path[0].body) {
|
|
return false;
|
|
}
|
|
break;
|
|
case Syntax.AssignmentExpression:
|
|
// throws for: `undefined = foo` (where `undefined` is not declared)
|
|
if (node === path[0].left) {
|
|
throw new Error(
|
|
'Illegal assignment to `undefined`. '
|
|
+ 'This breaks assumptions of the transform.'
|
|
);
|
|
}
|
|
break;
|
|
case Syntax.MemberExpression:
|
|
// skips: `foo.undefined` but not `foo[undefined]`
|
|
if (node === path[0].property && !path[0].computed) {
|
|
return false;
|
|
}
|
|
break;
|
|
case Syntax.VariableDeclarator:
|
|
// skips: `var undefined`
|
|
if (node !== path[0].init) {
|
|
return false;
|
|
}
|
|
break;
|
|
case Syntax.Property:
|
|
// skips: `undefined: foo`
|
|
if (node === path[0].key) {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
exports.visitorList = [
|
|
visitIdentifierUndefined
|
|
];
|