undefined-to-void-0-visitors.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /**
  2. * Copyright 2013-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. var Syntax = require('esprima-fb').Syntax;
  10. var utils = require('../src/utils');
  11. /**
  12. * Replaces `undefined` with `(void 0)` when it appears as an rvalue and is
  13. * undeclared in the lexical scope.
  14. *
  15. * Example:
  16. *
  17. * (function() {
  18. * foo.undefined = bar;
  19. * ({undefined: foo});
  20. *
  21. * var bar = undefined;
  22. * bar = undefined;
  23. * foo.bar = undefined;
  24. * undefined.foo = bar;
  25. * foo[undefined] = bar;
  26. * ({foo: undefined});
  27. * })(undefined);
  28. *
  29. * (function(undefined) { // declared here
  30. * return undefined;
  31. * })(1);
  32. *
  33. * (function() {
  34. * var undefined; // declared here
  35. * undefined = 1; // assignment to declared variable
  36. * return undefined;
  37. * })();
  38. *
  39. * (function(undefined) { // declared here
  40. * return (function() {
  41. * return undefined; // respects lexical scope
  42. * })();
  43. * })();
  44. *
  45. * Becomes:
  46. *
  47. * (function() {
  48. * foo.undefined = bar;
  49. * ({undefined: foo});
  50. *
  51. * var bar = (void 0);
  52. * bar = (void 0);
  53. * foo.bar = (void 0);
  54. * (void 0).foo = bar;
  55. * foo[(void 0)] = bar;
  56. * ({foo: (void 0)});
  57. * })((void 0));
  58. *
  59. * (function(undefined) { // declared here
  60. * return undefined;
  61. * })(1);
  62. *
  63. * (function() {
  64. * var undefined; // declared here
  65. * undefined = 1; // assignment to declared variable
  66. * return undefined;
  67. * })();
  68. *
  69. * (function(undefined) { // declared here
  70. * return (function() {
  71. * return undefined; // respects lexical scope
  72. * })();
  73. * })();
  74. *
  75. *
  76. *
  77. * NOTE: Any assignment to `undefined` where `undefined` is not declared in the
  78. * lexical scope will result in an exception.
  79. */
  80. function visitIdentifierUndefined(traverse, node, path, state) {
  81. utils.catchup(node.range[1], state, function(value) {
  82. return '(void 0)';
  83. });
  84. }
  85. visitIdentifierUndefined.test = function(node, path, state) {
  86. if (
  87. node.type === Syntax.Identifier
  88. && node.name === 'undefined'
  89. && !utils.identWithinLexicalScope('undefined', state)
  90. ) {
  91. if (path[0]) {
  92. switch (path[0].type) {
  93. case Syntax.FunctionDeclaration:
  94. case Syntax.FunctionExpression:
  95. case Syntax.ArrowFunctionExpression:
  96. // skips: function params
  97. if (node !== path[0].body) {
  98. return false;
  99. }
  100. break;
  101. case Syntax.AssignmentExpression:
  102. // throws for: `undefined = foo` (where `undefined` is not declared)
  103. if (node === path[0].left) {
  104. throw new Error(
  105. 'Illegal assignment to `undefined`. '
  106. + 'This breaks assumptions of the transform.'
  107. );
  108. }
  109. break;
  110. case Syntax.MemberExpression:
  111. // skips: `foo.undefined` but not `foo[undefined]`
  112. if (node === path[0].property && !path[0].computed) {
  113. return false;
  114. }
  115. break;
  116. case Syntax.VariableDeclarator:
  117. // skips: `var undefined`
  118. if (node !== path[0].init) {
  119. return false;
  120. }
  121. break;
  122. case Syntax.Property:
  123. // skips: `undefined: foo`
  124. if (node === path[0].key) {
  125. return false;
  126. }
  127. break;
  128. }
  129. }
  130. return true;
  131. }
  132. return false;
  133. };
  134. exports.visitorList = [
  135. visitIdentifierUndefined
  136. ];