AMDDefineDependencyParserPlugin.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RuntimeGlobals = require("../RuntimeGlobals");
  7. const AMDDefineDependency = require("./AMDDefineDependency");
  8. const AMDRequireArrayDependency = require("./AMDRequireArrayDependency");
  9. const AMDRequireContextDependency = require("./AMDRequireContextDependency");
  10. const AMDRequireItemDependency = require("./AMDRequireItemDependency");
  11. const ConstDependency = require("./ConstDependency");
  12. const ContextDependencyHelpers = require("./ContextDependencyHelpers");
  13. const DynamicExports = require("./DynamicExports");
  14. const LocalModuleDependency = require("./LocalModuleDependency");
  15. const { addLocalModule, getLocalModule } = require("./LocalModulesHelpers");
  16. /** @typedef {import("estree").CallExpression} CallExpression */
  17. /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
  18. /**
  19. * @param {CallExpression} expr expression
  20. * @returns {boolean} true if it's a bound function expression
  21. */
  22. const isBoundFunctionExpression = expr => {
  23. if (expr.type !== "CallExpression") return false;
  24. if (expr.callee.type !== "MemberExpression") return false;
  25. if (expr.callee.computed) return false;
  26. if (expr.callee.object.type !== "FunctionExpression") return false;
  27. if (expr.callee.property.type !== "Identifier") return false;
  28. if (expr.callee.property.name !== "bind") return false;
  29. return true;
  30. };
  31. const isUnboundFunctionExpression = expr => {
  32. if (expr.type === "FunctionExpression") return true;
  33. if (expr.type === "ArrowFunctionExpression") return true;
  34. return false;
  35. };
  36. const isCallable = expr => {
  37. if (isUnboundFunctionExpression(expr)) return true;
  38. if (isBoundFunctionExpression(expr)) return true;
  39. return false;
  40. };
  41. class AMDDefineDependencyParserPlugin {
  42. constructor(options) {
  43. this.options = options;
  44. }
  45. /**
  46. * @param {JavascriptParser} parser the parser
  47. * @returns {void}
  48. */
  49. apply(parser) {
  50. parser.hooks.call
  51. .for("define")
  52. .tap(
  53. "AMDDefineDependencyParserPlugin",
  54. this.processCallDefine.bind(this, parser)
  55. );
  56. }
  57. processArray(parser, expr, param, identifiers, namedModule) {
  58. if (param.isArray()) {
  59. param.items.forEach((param, idx) => {
  60. if (
  61. param.isString() &&
  62. ["require", "module", "exports"].includes(param.string)
  63. )
  64. identifiers[idx] = param.string;
  65. const result = this.processItem(parser, expr, param, namedModule);
  66. if (result === undefined) {
  67. this.processContext(parser, expr, param);
  68. }
  69. });
  70. return true;
  71. } else if (param.isConstArray()) {
  72. const deps = [];
  73. param.array.forEach((request, idx) => {
  74. let dep;
  75. let localModule;
  76. if (request === "require") {
  77. identifiers[idx] = request;
  78. dep = RuntimeGlobals.require;
  79. } else if (["exports", "module"].includes(request)) {
  80. identifiers[idx] = request;
  81. dep = request;
  82. } else if ((localModule = getLocalModule(parser.state, request))) {
  83. localModule.flagUsed();
  84. dep = new LocalModuleDependency(localModule, undefined, false);
  85. dep.loc = expr.loc;
  86. parser.state.module.addPresentationalDependency(dep);
  87. } else {
  88. dep = this.newRequireItemDependency(request);
  89. dep.loc = expr.loc;
  90. dep.optional = !!parser.scope.inTry;
  91. parser.state.current.addDependency(dep);
  92. }
  93. deps.push(dep);
  94. });
  95. const dep = this.newRequireArrayDependency(deps, param.range);
  96. dep.loc = expr.loc;
  97. dep.optional = !!parser.scope.inTry;
  98. parser.state.module.addPresentationalDependency(dep);
  99. return true;
  100. }
  101. }
  102. processItem(parser, expr, param, namedModule) {
  103. if (param.isConditional()) {
  104. param.options.forEach(param => {
  105. const result = this.processItem(parser, expr, param);
  106. if (result === undefined) {
  107. this.processContext(parser, expr, param);
  108. }
  109. });
  110. return true;
  111. } else if (param.isString()) {
  112. let dep, localModule;
  113. if (param.string === "require") {
  114. dep = new ConstDependency(RuntimeGlobals.require, param.range, [
  115. RuntimeGlobals.require
  116. ]);
  117. } else if (param.string === "exports") {
  118. dep = new ConstDependency("exports", param.range, [
  119. RuntimeGlobals.exports
  120. ]);
  121. } else if (param.string === "module") {
  122. dep = new ConstDependency("module", param.range, [
  123. RuntimeGlobals.module
  124. ]);
  125. } else if (
  126. (localModule = getLocalModule(parser.state, param.string, namedModule))
  127. ) {
  128. localModule.flagUsed();
  129. dep = new LocalModuleDependency(localModule, param.range, false);
  130. } else {
  131. dep = this.newRequireItemDependency(param.string, param.range);
  132. dep.optional = !!parser.scope.inTry;
  133. parser.state.current.addDependency(dep);
  134. return true;
  135. }
  136. dep.loc = expr.loc;
  137. parser.state.module.addPresentationalDependency(dep);
  138. return true;
  139. }
  140. }
  141. processContext(parser, expr, param) {
  142. const dep = ContextDependencyHelpers.create(
  143. AMDRequireContextDependency,
  144. param.range,
  145. param,
  146. expr,
  147. this.options,
  148. {
  149. category: "amd"
  150. },
  151. parser
  152. );
  153. if (!dep) return;
  154. dep.loc = expr.loc;
  155. dep.optional = !!parser.scope.inTry;
  156. parser.state.current.addDependency(dep);
  157. return true;
  158. }
  159. processCallDefine(parser, expr) {
  160. let array, fn, obj, namedModule;
  161. switch (expr.arguments.length) {
  162. case 1:
  163. if (isCallable(expr.arguments[0])) {
  164. // define(f() {…})
  165. fn = expr.arguments[0];
  166. } else if (expr.arguments[0].type === "ObjectExpression") {
  167. // define({…})
  168. obj = expr.arguments[0];
  169. } else {
  170. // define(expr)
  171. // unclear if function or object
  172. obj = fn = expr.arguments[0];
  173. }
  174. break;
  175. case 2:
  176. if (expr.arguments[0].type === "Literal") {
  177. namedModule = expr.arguments[0].value;
  178. // define("…", …)
  179. if (isCallable(expr.arguments[1])) {
  180. // define("…", f() {…})
  181. fn = expr.arguments[1];
  182. } else if (expr.arguments[1].type === "ObjectExpression") {
  183. // define("…", {…})
  184. obj = expr.arguments[1];
  185. } else {
  186. // define("…", expr)
  187. // unclear if function or object
  188. obj = fn = expr.arguments[1];
  189. }
  190. } else {
  191. array = expr.arguments[0];
  192. if (isCallable(expr.arguments[1])) {
  193. // define([…], f() {})
  194. fn = expr.arguments[1];
  195. } else if (expr.arguments[1].type === "ObjectExpression") {
  196. // define([…], {…})
  197. obj = expr.arguments[1];
  198. } else {
  199. // define([…], expr)
  200. // unclear if function or object
  201. obj = fn = expr.arguments[1];
  202. }
  203. }
  204. break;
  205. case 3:
  206. // define("…", […], f() {…})
  207. namedModule = expr.arguments[0].value;
  208. array = expr.arguments[1];
  209. if (isCallable(expr.arguments[2])) {
  210. // define("…", […], f() {})
  211. fn = expr.arguments[2];
  212. } else if (expr.arguments[2].type === "ObjectExpression") {
  213. // define("…", […], {…})
  214. obj = expr.arguments[2];
  215. } else {
  216. // define("…", […], expr)
  217. // unclear if function or object
  218. obj = fn = expr.arguments[2];
  219. }
  220. break;
  221. default:
  222. return;
  223. }
  224. DynamicExports.bailout(parser.state);
  225. let fnParams = null;
  226. let fnParamsOffset = 0;
  227. if (fn) {
  228. if (isUnboundFunctionExpression(fn)) {
  229. fnParams = fn.params;
  230. } else if (isBoundFunctionExpression(fn)) {
  231. fnParams = fn.callee.object.params;
  232. fnParamsOffset = fn.arguments.length - 1;
  233. if (fnParamsOffset < 0) {
  234. fnParamsOffset = 0;
  235. }
  236. }
  237. }
  238. let fnRenames = new Map();
  239. if (array) {
  240. const identifiers = {};
  241. const param = parser.evaluateExpression(array);
  242. const result = this.processArray(
  243. parser,
  244. expr,
  245. param,
  246. identifiers,
  247. namedModule
  248. );
  249. if (!result) return;
  250. if (fnParams) {
  251. fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
  252. if (identifiers[idx]) {
  253. fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
  254. return false;
  255. }
  256. return true;
  257. });
  258. }
  259. } else {
  260. const identifiers = ["require", "exports", "module"];
  261. if (fnParams) {
  262. fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
  263. if (identifiers[idx]) {
  264. fnRenames.set(param.name, parser.getVariableInfo(identifiers[idx]));
  265. return false;
  266. }
  267. return true;
  268. });
  269. }
  270. }
  271. let inTry;
  272. if (fn && isUnboundFunctionExpression(fn)) {
  273. inTry = parser.scope.inTry;
  274. parser.inScope(fnParams, () => {
  275. for (const [name, varInfo] of fnRenames) {
  276. parser.setVariable(name, varInfo);
  277. }
  278. parser.scope.inTry = inTry;
  279. if (fn.body.type === "BlockStatement") {
  280. parser.detectMode(fn.body.body);
  281. const prev = parser.prevStatement;
  282. parser.preWalkStatement(fn.body);
  283. parser.prevStatement = prev;
  284. parser.walkStatement(fn.body);
  285. } else {
  286. parser.walkExpression(fn.body);
  287. }
  288. });
  289. } else if (fn && isBoundFunctionExpression(fn)) {
  290. inTry = parser.scope.inTry;
  291. parser.inScope(
  292. fn.callee.object.params.filter(
  293. i => !["require", "module", "exports"].includes(i.name)
  294. ),
  295. () => {
  296. for (const [name, varInfo] of fnRenames) {
  297. parser.setVariable(name, varInfo);
  298. }
  299. parser.scope.inTry = inTry;
  300. if (fn.callee.object.body.type === "BlockStatement") {
  301. parser.detectMode(fn.callee.object.body.body);
  302. const prev = parser.prevStatement;
  303. parser.preWalkStatement(fn.callee.object.body);
  304. parser.prevStatement = prev;
  305. parser.walkStatement(fn.callee.object.body);
  306. } else {
  307. parser.walkExpression(fn.callee.object.body);
  308. }
  309. }
  310. );
  311. if (fn.arguments) {
  312. parser.walkExpressions(fn.arguments);
  313. }
  314. } else if (fn || obj) {
  315. parser.walkExpression(fn || obj);
  316. }
  317. const dep = this.newDefineDependency(
  318. expr.range,
  319. array ? array.range : null,
  320. fn ? fn.range : null,
  321. obj ? obj.range : null,
  322. namedModule ? namedModule : null
  323. );
  324. dep.loc = expr.loc;
  325. if (namedModule) {
  326. dep.localModule = addLocalModule(parser.state, namedModule);
  327. }
  328. parser.state.module.addPresentationalDependency(dep);
  329. return true;
  330. }
  331. newDefineDependency(
  332. range,
  333. arrayRange,
  334. functionRange,
  335. objectRange,
  336. namedModule
  337. ) {
  338. return new AMDDefineDependency(
  339. range,
  340. arrayRange,
  341. functionRange,
  342. objectRange,
  343. namedModule
  344. );
  345. }
  346. newRequireArrayDependency(depsArray, range) {
  347. return new AMDRequireArrayDependency(depsArray, range);
  348. }
  349. newRequireItemDependency(request, range) {
  350. return new AMDRequireItemDependency(request, range);
  351. }
  352. }
  353. module.exports = AMDDefineDependencyParserPlugin;