You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

563 lines
17 KiB

4 years ago
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = transformClass;
  6. var _helperFunctionName = _interopRequireDefault(require("@babel/helper-function-name"));
  7. var _helperReplaceSupers = _interopRequireWildcard(require("@babel/helper-replace-supers"));
  8. var _helperOptimiseCallExpression = _interopRequireDefault(require("@babel/helper-optimise-call-expression"));
  9. var defineMap = _interopRequireWildcard(require("@babel/helper-define-map"));
  10. var _core = require("@babel/core");
  11. function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
  12. 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; }
  13. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  14. function buildConstructor(classRef, constructorBody, node) {
  15. const func = _core.types.functionDeclaration(_core.types.cloneNode(classRef), [], constructorBody);
  16. _core.types.inherits(func, node);
  17. return func;
  18. }
  19. function transformClass(path, file, builtinClasses, isLoose) {
  20. const classState = {
  21. parent: undefined,
  22. scope: undefined,
  23. node: undefined,
  24. path: undefined,
  25. file: undefined,
  26. classId: undefined,
  27. classRef: undefined,
  28. superName: undefined,
  29. superReturns: [],
  30. isDerived: false,
  31. extendsNative: false,
  32. construct: undefined,
  33. constructorBody: undefined,
  34. userConstructor: undefined,
  35. userConstructorPath: undefined,
  36. hasConstructor: false,
  37. instancePropBody: [],
  38. instancePropRefs: {},
  39. staticPropBody: [],
  40. body: [],
  41. superThises: [],
  42. pushedConstructor: false,
  43. pushedInherits: false,
  44. protoAlias: null,
  45. isLoose: false,
  46. hasInstanceDescriptors: false,
  47. hasStaticDescriptors: false,
  48. instanceMutatorMap: {},
  49. staticMutatorMap: {}
  50. };
  51. const setState = newState => {
  52. Object.assign(classState, newState);
  53. };
  54. const findThisesVisitor = _core.traverse.visitors.merge([_helperReplaceSupers.environmentVisitor, {
  55. ThisExpression(path) {
  56. classState.superThises.push(path);
  57. }
  58. }]);
  59. function pushToMap(node, enumerable, kind = "value", scope) {
  60. let mutatorMap;
  61. if (node.static) {
  62. setState({
  63. hasStaticDescriptors: true
  64. });
  65. mutatorMap = classState.staticMutatorMap;
  66. } else {
  67. setState({
  68. hasInstanceDescriptors: true
  69. });
  70. mutatorMap = classState.instanceMutatorMap;
  71. }
  72. const map = defineMap.push(mutatorMap, node, kind, classState.file, scope);
  73. if (enumerable) {
  74. map.enumerable = _core.types.booleanLiteral(true);
  75. }
  76. return map;
  77. }
  78. function maybeCreateConstructor() {
  79. let hasConstructor = false;
  80. const paths = classState.path.get("body.body");
  81. for (const path of paths) {
  82. hasConstructor = path.equals("kind", "constructor");
  83. if (hasConstructor) break;
  84. }
  85. if (hasConstructor) return;
  86. let params, body;
  87. if (classState.isDerived) {
  88. const constructor = _core.template.expression.ast`
  89. (function () {
  90. super(...arguments);
  91. })
  92. `;
  93. params = constructor.params;
  94. body = constructor.body;
  95. } else {
  96. params = [];
  97. body = _core.types.blockStatement([]);
  98. }
  99. classState.path.get("body").unshiftContainer("body", _core.types.classMethod("constructor", _core.types.identifier("constructor"), params, body));
  100. }
  101. function buildBody() {
  102. maybeCreateConstructor();
  103. pushBody();
  104. verifyConstructor();
  105. if (classState.userConstructor) {
  106. const {
  107. constructorBody,
  108. userConstructor,
  109. construct
  110. } = classState;
  111. constructorBody.body = constructorBody.body.concat(userConstructor.body.body);
  112. _core.types.inherits(construct, userConstructor);
  113. _core.types.inherits(constructorBody, userConstructor.body);
  114. }
  115. pushDescriptors();
  116. }
  117. function pushBody() {
  118. const classBodyPaths = classState.path.get("body.body");
  119. for (const path of classBodyPaths) {
  120. const node = path.node;
  121. if (path.isClassProperty()) {
  122. throw path.buildCodeFrameError("Missing class properties transform.");
  123. }
  124. if (node.decorators) {
  125. throw path.buildCodeFrameError("Method has decorators, put the decorator plugin before the classes one.");
  126. }
  127. if (_core.types.isClassMethod(node)) {
  128. const isConstructor = node.kind === "constructor";
  129. const replaceSupers = new _helperReplaceSupers.default({
  130. methodPath: path,
  131. objectRef: classState.classRef,
  132. superRef: classState.superName,
  133. isLoose: classState.isLoose,
  134. file: classState.file
  135. });
  136. replaceSupers.replace();
  137. const superReturns = [];
  138. path.traverse(_core.traverse.visitors.merge([_helperReplaceSupers.environmentVisitor, {
  139. ReturnStatement(path) {
  140. if (!path.getFunctionParent().isArrowFunctionExpression()) {
  141. superReturns.push(path);
  142. }
  143. }
  144. }]));
  145. if (isConstructor) {
  146. pushConstructor(superReturns, node, path);
  147. } else {
  148. pushMethod(node, path);
  149. }
  150. }
  151. }
  152. }
  153. function clearDescriptors() {
  154. setState({
  155. hasInstanceDescriptors: false,
  156. hasStaticDescriptors: false,
  157. instanceMutatorMap: {},
  158. staticMutatorMap: {}
  159. });
  160. }
  161. function pushDescriptors() {
  162. pushInheritsToBody();
  163. const {
  164. body
  165. } = classState;
  166. let instanceProps;
  167. let staticProps;
  168. if (classState.hasInstanceDescriptors) {
  169. instanceProps = defineMap.toClassObject(classState.instanceMutatorMap);
  170. }
  171. if (classState.hasStaticDescriptors) {
  172. staticProps = defineMap.toClassObject(classState.staticMutatorMap);
  173. }
  174. if (instanceProps || staticProps) {
  175. if (instanceProps) {
  176. instanceProps = defineMap.toComputedObjectFromClass(instanceProps);
  177. }
  178. if (staticProps) {
  179. staticProps = defineMap.toComputedObjectFromClass(staticProps);
  180. }
  181. let args = [_core.types.cloneNode(classState.classRef), _core.types.nullLiteral(), _core.types.nullLiteral()];
  182. if (instanceProps) args[1] = instanceProps;
  183. if (staticProps) args[2] = staticProps;
  184. let lastNonNullIndex = 0;
  185. for (let i = 0; i < args.length; i++) {
  186. if (!_core.types.isNullLiteral(args[i])) lastNonNullIndex = i;
  187. }
  188. args = args.slice(0, lastNonNullIndex + 1);
  189. body.push(_core.types.expressionStatement(_core.types.callExpression(classState.file.addHelper("createClass"), args)));
  190. }
  191. clearDescriptors();
  192. }
  193. function wrapSuperCall(bareSuper, superRef, thisRef, body) {
  194. let bareSuperNode = bareSuper.node;
  195. let call;
  196. if (classState.isLoose) {
  197. bareSuperNode.arguments.unshift(_core.types.thisExpression());
  198. if (bareSuperNode.arguments.length === 2 && _core.types.isSpreadElement(bareSuperNode.arguments[1]) && _core.types.isIdentifier(bareSuperNode.arguments[1].argument, {
  199. name: "arguments"
  200. })) {
  201. bareSuperNode.arguments[1] = bareSuperNode.arguments[1].argument;
  202. bareSuperNode.callee = _core.types.memberExpression(_core.types.cloneNode(superRef), _core.types.identifier("apply"));
  203. } else {
  204. bareSuperNode.callee = _core.types.memberExpression(_core.types.cloneNode(superRef), _core.types.identifier("call"));
  205. }
  206. call = _core.types.logicalExpression("||", bareSuperNode, _core.types.thisExpression());
  207. } else {
  208. bareSuperNode = (0, _helperOptimiseCallExpression.default)(_core.types.callExpression(classState.file.addHelper("getPrototypeOf"), [_core.types.cloneNode(classState.classRef)]), _core.types.thisExpression(), bareSuperNode.arguments);
  209. call = _core.types.callExpression(classState.file.addHelper("possibleConstructorReturn"), [_core.types.thisExpression(), bareSuperNode]);
  210. }
  211. if (bareSuper.parentPath.isExpressionStatement() && bareSuper.parentPath.container === body.node.body && body.node.body.length - 1 === bareSuper.parentPath.key) {
  212. if (classState.superThises.length) {
  213. call = _core.types.assignmentExpression("=", thisRef(), call);
  214. }
  215. bareSuper.parentPath.replaceWith(_core.types.returnStatement(call));
  216. } else {
  217. bareSuper.replaceWith(_core.types.assignmentExpression("=", thisRef(), call));
  218. }
  219. }
  220. function verifyConstructor() {
  221. if (!classState.isDerived) return;
  222. const path = classState.userConstructorPath;
  223. const body = path.get("body");
  224. path.traverse(findThisesVisitor);
  225. let thisRef = function () {
  226. const ref = path.scope.generateDeclaredUidIdentifier("this");
  227. thisRef = () => _core.types.cloneNode(ref);
  228. return ref;
  229. };
  230. for (const thisPath of classState.superThises) {
  231. const {
  232. node,
  233. parentPath
  234. } = thisPath;
  235. if (parentPath.isMemberExpression({
  236. object: node
  237. })) {
  238. thisPath.replaceWith(thisRef());
  239. continue;
  240. }
  241. thisPath.replaceWith(_core.types.callExpression(classState.file.addHelper("assertThisInitialized"), [thisRef()]));
  242. }
  243. const bareSupers = new Set();
  244. path.traverse(_core.traverse.visitors.merge([_helperReplaceSupers.environmentVisitor, {
  245. Super(path) {
  246. const {
  247. node,
  248. parentPath
  249. } = path;
  250. if (parentPath.isCallExpression({
  251. callee: node
  252. })) {
  253. bareSupers.add(parentPath);
  254. }
  255. }
  256. }]));
  257. let guaranteedSuperBeforeFinish = !!bareSupers.size;
  258. for (const bareSuper of bareSupers) {
  259. wrapSuperCall(bareSuper, classState.superName, thisRef, body);
  260. if (guaranteedSuperBeforeFinish) {
  261. bareSuper.find(function (parentPath) {
  262. if (parentPath === path) {
  263. return true;
  264. }
  265. if (parentPath.isLoop() || parentPath.isConditional() || parentPath.isArrowFunctionExpression()) {
  266. guaranteedSuperBeforeFinish = false;
  267. return true;
  268. }
  269. });
  270. }
  271. }
  272. let wrapReturn;
  273. if (classState.isLoose) {
  274. wrapReturn = returnArg => {
  275. const thisExpr = _core.types.callExpression(classState.file.addHelper("assertThisInitialized"), [thisRef()]);
  276. return returnArg ? _core.types.logicalExpression("||", returnArg, thisExpr) : thisExpr;
  277. };
  278. } else {
  279. wrapReturn = returnArg => _core.types.callExpression(classState.file.addHelper("possibleConstructorReturn"), [thisRef()].concat(returnArg || []));
  280. }
  281. const bodyPaths = body.get("body");
  282. if (!bodyPaths.length || !bodyPaths.pop().isReturnStatement()) {
  283. body.pushContainer("body", _core.types.returnStatement(guaranteedSuperBeforeFinish ? thisRef() : wrapReturn()));
  284. }
  285. for (const returnPath of classState.superReturns) {
  286. returnPath.get("argument").replaceWith(wrapReturn(returnPath.node.argument));
  287. }
  288. }
  289. function pushMethod(node, path) {
  290. const scope = path ? path.scope : classState.scope;
  291. if (node.kind === "method") {
  292. if (processMethod(node, scope)) return;
  293. }
  294. pushToMap(node, false, null, scope);
  295. }
  296. function processMethod(node, scope) {
  297. if (classState.isLoose && !node.decorators) {
  298. let {
  299. classRef
  300. } = classState;
  301. if (!node.static) {
  302. insertProtoAliasOnce();
  303. classRef = classState.protoAlias;
  304. }
  305. const methodName = _core.types.memberExpression(_core.types.cloneNode(classRef), node.key, node.computed || _core.types.isLiteral(node.key));
  306. let func = _core.types.functionExpression(null, node.params, node.body, node.generator, node.async);
  307. _core.types.inherits(func, node);
  308. const key = _core.types.toComputedKey(node, node.key);
  309. if (_core.types.isStringLiteral(key)) {
  310. func = (0, _helperFunctionName.default)({
  311. node: func,
  312. id: key,
  313. scope
  314. });
  315. }
  316. const expr = _core.types.expressionStatement(_core.types.assignmentExpression("=", methodName, func));
  317. _core.types.inheritsComments(expr, node);
  318. classState.body.push(expr);
  319. return true;
  320. }
  321. return false;
  322. }
  323. function insertProtoAliasOnce() {
  324. if (classState.protoAlias === null) {
  325. setState({
  326. protoAlias: classState.scope.generateUidIdentifier("proto")
  327. });
  328. const classProto = _core.types.memberExpression(classState.classRef, _core.types.identifier("prototype"));
  329. const protoDeclaration = _core.types.variableDeclaration("var", [_core.types.variableDeclarator(classState.protoAlias, classProto)]);
  330. classState.body.push(protoDeclaration);
  331. }
  332. }
  333. function pushConstructor(superReturns, method, path) {
  334. if (path.scope.hasOwnBinding(classState.classRef.name)) {
  335. path.scope.rename(classState.classRef.name);
  336. }
  337. setState({
  338. userConstructorPath: path,
  339. userConstructor: method,
  340. hasConstructor: true,
  341. superReturns
  342. });
  343. const {
  344. construct
  345. } = classState;
  346. _core.types.inheritsComments(construct, method);
  347. construct.params = method.params;
  348. _core.types.inherits(construct.body, method.body);
  349. construct.body.directives = method.body.directives;
  350. pushConstructorToBody();
  351. }
  352. function pushConstructorToBody() {
  353. if (classState.pushedConstructor) return;
  354. classState.pushedConstructor = true;
  355. if (classState.hasInstanceDescriptors || classState.hasStaticDescriptors) {
  356. pushDescriptors();
  357. }
  358. classState.body.push(classState.construct);
  359. pushInheritsToBody();
  360. }
  361. function pushInheritsToBody() {
  362. if (!classState.isDerived || classState.pushedInherits) return;
  363. setState({
  364. pushedInherits: true
  365. });
  366. classState.body.unshift(_core.types.expressionStatement(_core.types.callExpression(classState.file.addHelper(classState.isLoose ? "inheritsLoose" : "inherits"), [_core.types.cloneNode(classState.classRef), _core.types.cloneNode(classState.superName)])));
  367. }
  368. function setupClosureParamsArgs() {
  369. const {
  370. superName
  371. } = classState;
  372. const closureParams = [];
  373. const closureArgs = [];
  374. if (classState.isDerived) {
  375. const arg = classState.extendsNative ? _core.types.callExpression(classState.file.addHelper("wrapNativeSuper"), [_core.types.cloneNode(superName)]) : _core.types.cloneNode(superName);
  376. const param = classState.scope.generateUidIdentifierBasedOnNode(superName);
  377. closureParams.push(param);
  378. closureArgs.push(arg);
  379. setState({
  380. superName: _core.types.cloneNode(param)
  381. });
  382. }
  383. return {
  384. closureParams,
  385. closureArgs
  386. };
  387. }
  388. function classTransformer(path, file, builtinClasses, isLoose) {
  389. setState({
  390. parent: path.parent,
  391. scope: path.scope,
  392. node: path.node,
  393. path,
  394. file,
  395. isLoose
  396. });
  397. setState({
  398. classId: classState.node.id,
  399. classRef: classState.node.id ? _core.types.identifier(classState.node.id.name) : classState.scope.generateUidIdentifier("class"),
  400. superName: classState.node.superClass,
  401. isDerived: !!classState.node.superClass,
  402. constructorBody: _core.types.blockStatement([])
  403. });
  404. setState({
  405. extendsNative: classState.isDerived && builtinClasses.has(classState.superName.name) && !classState.scope.hasBinding(classState.superName.name, true)
  406. });
  407. const {
  408. classRef,
  409. node,
  410. constructorBody
  411. } = classState;
  412. setState({
  413. construct: buildConstructor(classRef, constructorBody, node)
  414. });
  415. let {
  416. body
  417. } = classState;
  418. const {
  419. closureParams,
  420. closureArgs
  421. } = setupClosureParamsArgs();
  422. buildBody();
  423. if (!classState.isLoose) {
  424. constructorBody.body.unshift(_core.types.expressionStatement(_core.types.callExpression(classState.file.addHelper("classCallCheck"), [_core.types.thisExpression(), _core.types.cloneNode(classState.classRef)])));
  425. }
  426. body = body.concat(classState.staticPropBody.map(fn => fn(_core.types.cloneNode(classState.classRef))));
  427. const isStrict = path.isInStrictMode();
  428. let constructorOnly = classState.classId && body.length === 1;
  429. if (constructorOnly && !isStrict) {
  430. for (const param of classState.construct.params) {
  431. if (!_core.types.isIdentifier(param)) {
  432. constructorOnly = false;
  433. break;
  434. }
  435. }
  436. }
  437. const directives = constructorOnly ? body[0].body.directives : [];
  438. if (!isStrict) {
  439. directives.push(_core.types.directive(_core.types.directiveLiteral("use strict")));
  440. }
  441. if (constructorOnly) {
  442. return _core.types.toExpression(body[0]);
  443. }
  444. body.push(_core.types.returnStatement(_core.types.cloneNode(classState.classRef)));
  445. const container = _core.types.arrowFunctionExpression(closureParams, _core.types.blockStatement(body, directives));
  446. return _core.types.callExpression(container, closureArgs);
  447. }
  448. return classTransformer(path, file, builtinClasses, isLoose);
  449. }