transformClass.js 18 KB

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