index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = memberExpressionToFunctions;
  6. var t = _interopRequireWildcard(require("@babel/types"));
  7. function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
  8. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  9. class AssignmentMemoiser {
  10. constructor() {
  11. this._map = new WeakMap();
  12. }
  13. has(key) {
  14. return this._map.has(key);
  15. }
  16. get(key) {
  17. if (!this.has(key)) return;
  18. const record = this._map.get(key);
  19. const {
  20. value
  21. } = record;
  22. record.count--;
  23. if (record.count === 0) {
  24. return t.assignmentExpression("=", value, key);
  25. }
  26. return value;
  27. }
  28. set(key, value, count) {
  29. return this._map.set(key, {
  30. count,
  31. value
  32. });
  33. }
  34. }
  35. function toNonOptional(path, base) {
  36. const {
  37. node
  38. } = path;
  39. if (path.isOptionalMemberExpression()) {
  40. return t.memberExpression(base, node.property, node.computed);
  41. }
  42. if (path.isOptionalCallExpression()) {
  43. const callee = path.get("callee");
  44. if (path.node.optional && callee.isOptionalMemberExpression()) {
  45. const {
  46. object
  47. } = callee.node;
  48. const context = path.scope.maybeGenerateMemoised(object) || object;
  49. callee.get("object").replaceWith(t.assignmentExpression("=", context, object));
  50. return t.callExpression(t.memberExpression(base, t.identifier("call")), [context, ...node.arguments]);
  51. }
  52. return t.callExpression(base, node.arguments);
  53. }
  54. return path.node;
  55. }
  56. function isInDetachedTree(path) {
  57. while (path) {
  58. if (path.isProgram()) break;
  59. const {
  60. parentPath,
  61. container,
  62. listKey
  63. } = path;
  64. const parentNode = parentPath.node;
  65. if (listKey) {
  66. if (container !== parentNode[listKey]) return true;
  67. } else {
  68. if (container !== parentNode) return true;
  69. }
  70. path = parentPath;
  71. }
  72. return false;
  73. }
  74. const handle = {
  75. memoise() {},
  76. handle(member) {
  77. const {
  78. node,
  79. parent,
  80. parentPath,
  81. scope
  82. } = member;
  83. if (member.isOptionalMemberExpression()) {
  84. if (isInDetachedTree(member)) return;
  85. const endPath = member.find(({
  86. node,
  87. parent,
  88. parentPath
  89. }) => {
  90. if (parentPath.isOptionalMemberExpression()) {
  91. return parent.optional || parent.object !== node;
  92. }
  93. if (parentPath.isOptionalCallExpression()) {
  94. return node !== member.node && parent.optional || parent.callee !== node;
  95. }
  96. return true;
  97. });
  98. if (scope.path.isPattern()) {
  99. endPath.replaceWith(t.callExpression(t.arrowFunctionExpression([], endPath.node), []));
  100. return;
  101. }
  102. const rootParentPath = endPath.parentPath;
  103. if (rootParentPath.isUpdateExpression({
  104. argument: node
  105. }) || rootParentPath.isAssignmentExpression({
  106. left: node
  107. })) {
  108. throw member.buildCodeFrameError(`can't handle assignment`);
  109. }
  110. const isDeleteOperation = rootParentPath.isUnaryExpression({
  111. operator: "delete"
  112. });
  113. if (isDeleteOperation && endPath.isOptionalMemberExpression() && endPath.get("property").isPrivateName()) {
  114. throw member.buildCodeFrameError(`can't delete a private class element`);
  115. }
  116. let startingOptional = member;
  117. for (;;) {
  118. if (startingOptional.isOptionalMemberExpression()) {
  119. if (startingOptional.node.optional) break;
  120. startingOptional = startingOptional.get("object");
  121. continue;
  122. } else if (startingOptional.isOptionalCallExpression()) {
  123. if (startingOptional.node.optional) break;
  124. startingOptional = startingOptional.get("callee");
  125. continue;
  126. }
  127. throw new Error(`Internal error: unexpected ${startingOptional.node.type}`);
  128. }
  129. const startingProp = startingOptional.isOptionalMemberExpression() ? "object" : "callee";
  130. const startingNode = startingOptional.node[startingProp];
  131. const baseNeedsMemoised = scope.maybeGenerateMemoised(startingNode);
  132. const baseRef = baseNeedsMemoised != null ? baseNeedsMemoised : startingNode;
  133. const parentIsOptionalCall = parentPath.isOptionalCallExpression({
  134. callee: node
  135. });
  136. const parentIsCall = parentPath.isCallExpression({
  137. callee: node
  138. });
  139. startingOptional.replaceWith(toNonOptional(startingOptional, baseRef));
  140. if (parentIsOptionalCall) {
  141. if (parent.optional) {
  142. parentPath.replaceWith(this.optionalCall(member, parent.arguments));
  143. } else {
  144. parentPath.replaceWith(this.call(member, parent.arguments));
  145. }
  146. } else if (parentIsCall) {
  147. member.replaceWith(this.boundGet(member));
  148. } else {
  149. member.replaceWith(this.get(member));
  150. }
  151. let regular = member.node;
  152. for (let current = member; current !== endPath;) {
  153. const {
  154. parentPath
  155. } = current;
  156. if (parentPath === endPath && parentIsOptionalCall && parent.optional) {
  157. regular = parentPath.node;
  158. break;
  159. }
  160. regular = toNonOptional(parentPath, regular);
  161. current = parentPath;
  162. }
  163. let context;
  164. const endParentPath = endPath.parentPath;
  165. if (t.isMemberExpression(regular) && endParentPath.isOptionalCallExpression({
  166. callee: endPath.node,
  167. optional: true
  168. })) {
  169. const {
  170. object
  171. } = regular;
  172. context = member.scope.maybeGenerateMemoised(object);
  173. if (context) {
  174. regular.object = t.assignmentExpression("=", context, object);
  175. }
  176. }
  177. let replacementPath = endPath;
  178. if (isDeleteOperation) {
  179. replacementPath = endParentPath;
  180. regular = endParentPath.node;
  181. }
  182. replacementPath.replaceWith(t.conditionalExpression(t.logicalExpression("||", t.binaryExpression("===", baseNeedsMemoised ? t.assignmentExpression("=", t.cloneNode(baseRef), t.cloneNode(startingNode)) : t.cloneNode(baseRef), t.nullLiteral()), t.binaryExpression("===", t.cloneNode(baseRef), scope.buildUndefinedNode())), isDeleteOperation ? t.booleanLiteral(true) : scope.buildUndefinedNode(), regular));
  183. if (context) {
  184. const endParent = endParentPath.node;
  185. endParentPath.replaceWith(t.optionalCallExpression(t.optionalMemberExpression(endParent.callee, t.identifier("call"), false, true), [t.cloneNode(context), ...endParent.arguments], false));
  186. }
  187. return;
  188. }
  189. if (parentPath.isUpdateExpression({
  190. argument: node
  191. })) {
  192. if (this.simpleSet) {
  193. member.replaceWith(this.simpleSet(member));
  194. return;
  195. }
  196. const {
  197. operator,
  198. prefix
  199. } = parent;
  200. this.memoise(member, 2);
  201. const value = t.binaryExpression(operator[0], t.unaryExpression("+", this.get(member)), t.numericLiteral(1));
  202. if (prefix) {
  203. parentPath.replaceWith(this.set(member, value));
  204. } else {
  205. const {
  206. scope
  207. } = member;
  208. const ref = scope.generateUidIdentifierBasedOnNode(node);
  209. scope.push({
  210. id: ref
  211. });
  212. value.left = t.assignmentExpression("=", t.cloneNode(ref), value.left);
  213. parentPath.replaceWith(t.sequenceExpression([this.set(member, value), t.cloneNode(ref)]));
  214. }
  215. return;
  216. }
  217. if (parentPath.isAssignmentExpression({
  218. left: node
  219. })) {
  220. if (this.simpleSet) {
  221. member.replaceWith(this.simpleSet(member));
  222. return;
  223. }
  224. const {
  225. operator,
  226. right: value
  227. } = parent;
  228. if (operator === "=") {
  229. parentPath.replaceWith(this.set(member, value));
  230. } else {
  231. const operatorTrunc = operator.slice(0, -1);
  232. if (t.LOGICAL_OPERATORS.includes(operatorTrunc)) {
  233. this.memoise(member, 1);
  234. parentPath.replaceWith(t.logicalExpression(operatorTrunc, this.get(member), this.set(member, value)));
  235. } else {
  236. this.memoise(member, 2);
  237. parentPath.replaceWith(this.set(member, t.binaryExpression(operatorTrunc, this.get(member), value)));
  238. }
  239. }
  240. return;
  241. }
  242. if (parentPath.isCallExpression({
  243. callee: node
  244. })) {
  245. parentPath.replaceWith(this.call(member, parent.arguments));
  246. return;
  247. }
  248. if (parentPath.isOptionalCallExpression({
  249. callee: node
  250. })) {
  251. if (scope.path.isPattern()) {
  252. parentPath.replaceWith(t.callExpression(t.arrowFunctionExpression([], parentPath.node), []));
  253. return;
  254. }
  255. parentPath.replaceWith(this.optionalCall(member, parent.arguments));
  256. return;
  257. }
  258. if (parentPath.isForXStatement({
  259. left: node
  260. }) || parentPath.isObjectProperty({
  261. value: node
  262. }) && parentPath.parentPath.isObjectPattern() || parentPath.isAssignmentPattern({
  263. left: node
  264. }) && parentPath.parentPath.isObjectProperty({
  265. value: parent
  266. }) && parentPath.parentPath.parentPath.isObjectPattern() || parentPath.isArrayPattern() || parentPath.isAssignmentPattern({
  267. left: node
  268. }) && parentPath.parentPath.isArrayPattern() || parentPath.isRestElement()) {
  269. member.replaceWith(this.destructureSet(member));
  270. return;
  271. }
  272. member.replaceWith(this.get(member));
  273. }
  274. };
  275. function memberExpressionToFunctions(path, visitor, state) {
  276. path.traverse(visitor, Object.assign({}, handle, state, {
  277. memoiser: new AssignmentMemoiser()
  278. }));
  279. }