inference.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. /***********************************************************************
  2. A JavaScript tokenizer / parser / beautifier / compressor.
  3. https://github.com/mishoo/UglifyJS2
  4. -------------------------------- (C) ---------------------------------
  5. Author: Mihai Bazon
  6. <mihai.bazon@gmail.com>
  7. http://mihai.bazon.net/blog
  8. Distributed under the BSD license:
  9. Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
  10. Redistribution and use in source and binary forms, with or without
  11. modification, are permitted provided that the following conditions
  12. are met:
  13. * Redistributions of source code must retain the above
  14. copyright notice, this list of conditions and the following
  15. disclaimer.
  16. * Redistributions in binary form must reproduce the above
  17. copyright notice, this list of conditions and the following
  18. disclaimer in the documentation and/or other materials
  19. provided with the distribution.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
  21. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
  24. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  25. OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
  29. TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. SUCH DAMAGE.
  32. ***********************************************************************/
  33. import {
  34. AST_Array,
  35. AST_Arrow,
  36. AST_Assign,
  37. AST_BigInt,
  38. AST_Binary,
  39. AST_Block,
  40. AST_BlockStatement,
  41. AST_Call,
  42. AST_Case,
  43. AST_Chain,
  44. AST_Class,
  45. AST_DefClass,
  46. AST_ClassStaticBlock,
  47. AST_ClassProperty,
  48. AST_ConciseMethod,
  49. AST_Conditional,
  50. AST_Constant,
  51. AST_Definitions,
  52. AST_Dot,
  53. AST_EmptyStatement,
  54. AST_Expansion,
  55. AST_False,
  56. AST_ForIn,
  57. AST_Function,
  58. AST_If,
  59. AST_Import,
  60. AST_ImportMeta,
  61. AST_Jump,
  62. AST_LabeledStatement,
  63. AST_Lambda,
  64. AST_New,
  65. AST_Node,
  66. AST_Null,
  67. AST_Number,
  68. AST_Object,
  69. AST_ObjectGetter,
  70. AST_ObjectKeyVal,
  71. AST_ObjectProperty,
  72. AST_ObjectSetter,
  73. AST_PropAccess,
  74. AST_RegExp,
  75. AST_Return,
  76. AST_Scope,
  77. AST_Sequence,
  78. AST_SimpleStatement,
  79. AST_Statement,
  80. AST_String,
  81. AST_Sub,
  82. AST_Switch,
  83. AST_SwitchBranch,
  84. AST_SymbolClassProperty,
  85. AST_SymbolDeclaration,
  86. AST_SymbolRef,
  87. AST_TemplateSegment,
  88. AST_TemplateString,
  89. AST_This,
  90. AST_True,
  91. AST_Try,
  92. AST_Unary,
  93. AST_UnaryPostfix,
  94. AST_UnaryPrefix,
  95. AST_Undefined,
  96. AST_VarDef,
  97. walk,
  98. walk_abort,
  99. _PURE
  100. } from "../ast.js";
  101. import {
  102. makePredicate,
  103. return_true,
  104. return_false,
  105. return_null,
  106. return_this,
  107. make_node,
  108. member,
  109. has_annotation,
  110. } from "../utils/index.js";
  111. import { make_sequence, best_of_expression, read_property } from "./common.js";
  112. import { INLINED, UNDEFINED, has_flag } from "./compressor-flags.js";
  113. import { pure_prop_access_globals, is_pure_native_fn, is_pure_native_method } from "./native-objects.js";
  114. // Functions and methods to infer certain facts about expressions
  115. // It's not always possible to be 100% sure about something just by static analysis,
  116. // so `true` means yes, and `false` means maybe
  117. export const is_undeclared_ref = (node) =>
  118. node instanceof AST_SymbolRef && node.definition().undeclared;
  119. export const bitwise_binop = makePredicate("<<< >> << & | ^ ~");
  120. export const lazy_op = makePredicate("&& || ??");
  121. export const unary_side_effects = makePredicate("delete ++ --");
  122. // methods to determine whether an expression has a boolean result type
  123. (function(def_is_boolean) {
  124. const unary_bool = makePredicate("! delete");
  125. const binary_bool = makePredicate("in instanceof == != === !== < <= >= >");
  126. def_is_boolean(AST_Node, return_false);
  127. def_is_boolean(AST_UnaryPrefix, function() {
  128. return unary_bool.has(this.operator);
  129. });
  130. def_is_boolean(AST_Binary, function() {
  131. return binary_bool.has(this.operator)
  132. || lazy_op.has(this.operator)
  133. && this.left.is_boolean()
  134. && this.right.is_boolean();
  135. });
  136. def_is_boolean(AST_Conditional, function() {
  137. return this.consequent.is_boolean() && this.alternative.is_boolean();
  138. });
  139. def_is_boolean(AST_Assign, function() {
  140. return this.operator == "=" && this.right.is_boolean();
  141. });
  142. def_is_boolean(AST_Sequence, function() {
  143. return this.tail_node().is_boolean();
  144. });
  145. def_is_boolean(AST_True, return_true);
  146. def_is_boolean(AST_False, return_true);
  147. })(function(node, func) {
  148. node.DEFMETHOD("is_boolean", func);
  149. });
  150. // methods to determine if an expression has a numeric result type
  151. (function(def_is_number) {
  152. def_is_number(AST_Node, return_false);
  153. def_is_number(AST_Number, return_true);
  154. const unary = makePredicate("+ - ~ ++ --");
  155. def_is_number(AST_Unary, function() {
  156. return unary.has(this.operator) && !(this.expression instanceof AST_BigInt);
  157. });
  158. const numeric_ops = makePredicate("- * / % & | ^ << >> >>>");
  159. def_is_number(AST_Binary, function(compressor) {
  160. return numeric_ops.has(this.operator) || this.operator == "+"
  161. && this.left.is_number(compressor)
  162. && this.right.is_number(compressor);
  163. });
  164. def_is_number(AST_Assign, function(compressor) {
  165. return numeric_ops.has(this.operator.slice(0, -1))
  166. || this.operator == "=" && this.right.is_number(compressor);
  167. });
  168. def_is_number(AST_Sequence, function(compressor) {
  169. return this.tail_node().is_number(compressor);
  170. });
  171. def_is_number(AST_Conditional, function(compressor) {
  172. return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
  173. });
  174. })(function(node, func) {
  175. node.DEFMETHOD("is_number", func);
  176. });
  177. // methods to determine if an expression is a 32 bit integer (IE results from bitwise ops, or is an integer constant fitting in that size
  178. (function(def_is_32_bit_integer) {
  179. def_is_32_bit_integer(AST_Node, return_false);
  180. def_is_32_bit_integer(AST_Number, function() {
  181. return this.value === (this.value | 0);
  182. });
  183. def_is_32_bit_integer(AST_UnaryPrefix, function() {
  184. return this.operator == "~" ? this.expression.is_number()
  185. : this.operator === "+" ? this.expression.is_32_bit_integer()
  186. : false;
  187. });
  188. def_is_32_bit_integer(AST_Binary, function() {
  189. return bitwise_binop.has(this.operator);
  190. });
  191. }(function (node, func) {
  192. node.DEFMETHOD("is_32_bit_integer", func);
  193. }));
  194. // methods to determine if an expression has a string result type
  195. (function(def_is_string) {
  196. def_is_string(AST_Node, return_false);
  197. def_is_string(AST_String, return_true);
  198. def_is_string(AST_TemplateString, return_true);
  199. def_is_string(AST_UnaryPrefix, function() {
  200. return this.operator == "typeof";
  201. });
  202. def_is_string(AST_Binary, function(compressor) {
  203. return this.operator == "+" &&
  204. (this.left.is_string(compressor) || this.right.is_string(compressor));
  205. });
  206. def_is_string(AST_Assign, function(compressor) {
  207. return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
  208. });
  209. def_is_string(AST_Sequence, function(compressor) {
  210. return this.tail_node().is_string(compressor);
  211. });
  212. def_is_string(AST_Conditional, function(compressor) {
  213. return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
  214. });
  215. })(function(node, func) {
  216. node.DEFMETHOD("is_string", func);
  217. });
  218. export function is_undefined(node, compressor) {
  219. return (
  220. has_flag(node, UNDEFINED)
  221. || node instanceof AST_Undefined
  222. || node instanceof AST_UnaryPrefix
  223. && node.operator == "void"
  224. && !node.expression.has_side_effects(compressor)
  225. );
  226. }
  227. // Is the node explicitly null or undefined.
  228. function is_null_or_undefined(node, compressor) {
  229. let fixed;
  230. return (
  231. node instanceof AST_Null
  232. || is_undefined(node, compressor)
  233. || (
  234. node instanceof AST_SymbolRef
  235. && (fixed = node.definition().fixed) instanceof AST_Node
  236. && is_nullish(fixed, compressor)
  237. )
  238. );
  239. }
  240. // Find out if this expression is optionally chained from a base-point that we
  241. // can statically analyze as null or undefined.
  242. export function is_nullish_shortcircuited(node, compressor) {
  243. if (node instanceof AST_PropAccess || node instanceof AST_Call) {
  244. return (
  245. (node.optional && is_null_or_undefined(node.expression, compressor))
  246. || is_nullish_shortcircuited(node.expression, compressor)
  247. );
  248. }
  249. if (node instanceof AST_Chain) return is_nullish_shortcircuited(node.expression, compressor);
  250. return false;
  251. }
  252. // Find out if something is == null, or can short circuit into nullish.
  253. // Used to optimize ?. and ??
  254. export function is_nullish(node, compressor) {
  255. if (is_null_or_undefined(node, compressor)) return true;
  256. return is_nullish_shortcircuited(node, compressor);
  257. }
  258. // Determine if expression might cause side effects
  259. // If there's a possibility that a node may change something when it's executed, this returns true
  260. (function(def_has_side_effects) {
  261. def_has_side_effects(AST_Node, return_true);
  262. def_has_side_effects(AST_EmptyStatement, return_false);
  263. def_has_side_effects(AST_Constant, return_false);
  264. def_has_side_effects(AST_This, return_false);
  265. function any(list, compressor) {
  266. for (var i = list.length; --i >= 0;)
  267. if (list[i].has_side_effects(compressor))
  268. return true;
  269. return false;
  270. }
  271. def_has_side_effects(AST_Block, function(compressor) {
  272. return any(this.body, compressor);
  273. });
  274. def_has_side_effects(AST_Call, function(compressor) {
  275. if (
  276. !this.is_callee_pure(compressor)
  277. && (!this.expression.is_call_pure(compressor)
  278. || this.expression.has_side_effects(compressor))
  279. ) {
  280. return true;
  281. }
  282. return any(this.args, compressor);
  283. });
  284. def_has_side_effects(AST_Switch, function(compressor) {
  285. return this.expression.has_side_effects(compressor)
  286. || any(this.body, compressor);
  287. });
  288. def_has_side_effects(AST_Case, function(compressor) {
  289. return this.expression.has_side_effects(compressor)
  290. || any(this.body, compressor);
  291. });
  292. def_has_side_effects(AST_Try, function(compressor) {
  293. return this.body.has_side_effects(compressor)
  294. || this.bcatch && this.bcatch.has_side_effects(compressor)
  295. || this.bfinally && this.bfinally.has_side_effects(compressor);
  296. });
  297. def_has_side_effects(AST_If, function(compressor) {
  298. return this.condition.has_side_effects(compressor)
  299. || this.body && this.body.has_side_effects(compressor)
  300. || this.alternative && this.alternative.has_side_effects(compressor);
  301. });
  302. def_has_side_effects(AST_ImportMeta, return_false);
  303. def_has_side_effects(AST_LabeledStatement, function(compressor) {
  304. return this.body.has_side_effects(compressor);
  305. });
  306. def_has_side_effects(AST_SimpleStatement, function(compressor) {
  307. return this.body.has_side_effects(compressor);
  308. });
  309. def_has_side_effects(AST_Lambda, return_false);
  310. def_has_side_effects(AST_Class, function (compressor) {
  311. if (this.extends && this.extends.has_side_effects(compressor)) {
  312. return true;
  313. }
  314. return any(this.properties, compressor);
  315. });
  316. def_has_side_effects(AST_ClassStaticBlock, function(compressor) {
  317. return any(this.body, compressor);
  318. });
  319. def_has_side_effects(AST_Binary, function(compressor) {
  320. return this.left.has_side_effects(compressor)
  321. || this.right.has_side_effects(compressor);
  322. });
  323. def_has_side_effects(AST_Assign, return_true);
  324. def_has_side_effects(AST_Conditional, function(compressor) {
  325. return this.condition.has_side_effects(compressor)
  326. || this.consequent.has_side_effects(compressor)
  327. || this.alternative.has_side_effects(compressor);
  328. });
  329. def_has_side_effects(AST_Unary, function(compressor) {
  330. return unary_side_effects.has(this.operator)
  331. || this.expression.has_side_effects(compressor);
  332. });
  333. def_has_side_effects(AST_SymbolRef, function(compressor) {
  334. return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
  335. });
  336. def_has_side_effects(AST_SymbolClassProperty, return_false);
  337. def_has_side_effects(AST_SymbolDeclaration, return_false);
  338. def_has_side_effects(AST_Object, function(compressor) {
  339. return any(this.properties, compressor);
  340. });
  341. def_has_side_effects(AST_ObjectProperty, function(compressor) {
  342. return (
  343. this.computed_key() && this.key.has_side_effects(compressor)
  344. || this.value && this.value.has_side_effects(compressor)
  345. );
  346. });
  347. def_has_side_effects(AST_ClassProperty, function(compressor) {
  348. return (
  349. this.computed_key() && this.key.has_side_effects(compressor)
  350. || this.static && this.value && this.value.has_side_effects(compressor)
  351. );
  352. });
  353. def_has_side_effects(AST_ConciseMethod, function(compressor) {
  354. return this.computed_key() && this.key.has_side_effects(compressor);
  355. });
  356. def_has_side_effects(AST_ObjectGetter, function(compressor) {
  357. return this.computed_key() && this.key.has_side_effects(compressor);
  358. });
  359. def_has_side_effects(AST_ObjectSetter, function(compressor) {
  360. return this.computed_key() && this.key.has_side_effects(compressor);
  361. });
  362. def_has_side_effects(AST_Array, function(compressor) {
  363. return any(this.elements, compressor);
  364. });
  365. def_has_side_effects(AST_Dot, function(compressor) {
  366. if (is_nullish(this, compressor)) {
  367. return this.expression.has_side_effects(compressor);
  368. }
  369. if (!this.optional && this.expression.may_throw_on_access(compressor)) {
  370. return true;
  371. }
  372. return this.expression.has_side_effects(compressor);
  373. });
  374. def_has_side_effects(AST_Sub, function(compressor) {
  375. if (is_nullish(this, compressor)) {
  376. return this.expression.has_side_effects(compressor);
  377. }
  378. if (!this.optional && this.expression.may_throw_on_access(compressor)) {
  379. return true;
  380. }
  381. var property = this.property.has_side_effects(compressor);
  382. if (property && this.optional) return true; // "?." is a condition
  383. return property || this.expression.has_side_effects(compressor);
  384. });
  385. def_has_side_effects(AST_Chain, function (compressor) {
  386. return this.expression.has_side_effects(compressor);
  387. });
  388. def_has_side_effects(AST_Sequence, function(compressor) {
  389. return any(this.expressions, compressor);
  390. });
  391. def_has_side_effects(AST_Definitions, function(compressor) {
  392. return any(this.definitions, compressor);
  393. });
  394. def_has_side_effects(AST_VarDef, function() {
  395. return this.value != null;
  396. });
  397. def_has_side_effects(AST_TemplateSegment, return_false);
  398. def_has_side_effects(AST_TemplateString, function(compressor) {
  399. return any(this.segments, compressor);
  400. });
  401. })(function(node, func) {
  402. node.DEFMETHOD("has_side_effects", func);
  403. });
  404. // determine if expression may throw
  405. (function(def_may_throw) {
  406. def_may_throw(AST_Node, return_true);
  407. def_may_throw(AST_Constant, return_false);
  408. def_may_throw(AST_EmptyStatement, return_false);
  409. def_may_throw(AST_Lambda, return_false);
  410. def_may_throw(AST_SymbolDeclaration, return_false);
  411. def_may_throw(AST_This, return_false);
  412. def_may_throw(AST_ImportMeta, return_false);
  413. function any(list, compressor) {
  414. for (var i = list.length; --i >= 0;)
  415. if (list[i].may_throw(compressor))
  416. return true;
  417. return false;
  418. }
  419. def_may_throw(AST_Class, function(compressor) {
  420. if (this.extends && this.extends.may_throw(compressor)) return true;
  421. return any(this.properties, compressor);
  422. });
  423. def_may_throw(AST_ClassStaticBlock, function (compressor) {
  424. return any(this.body, compressor);
  425. });
  426. def_may_throw(AST_Array, function(compressor) {
  427. return any(this.elements, compressor);
  428. });
  429. def_may_throw(AST_Assign, function(compressor) {
  430. if (this.right.may_throw(compressor)) return true;
  431. if (!compressor.has_directive("use strict")
  432. && this.operator == "="
  433. && this.left instanceof AST_SymbolRef) {
  434. return false;
  435. }
  436. return this.left.may_throw(compressor);
  437. });
  438. def_may_throw(AST_Binary, function(compressor) {
  439. return this.left.may_throw(compressor)
  440. || this.right.may_throw(compressor);
  441. });
  442. def_may_throw(AST_Block, function(compressor) {
  443. return any(this.body, compressor);
  444. });
  445. def_may_throw(AST_Call, function(compressor) {
  446. if (is_nullish(this, compressor)) return false;
  447. if (any(this.args, compressor)) return true;
  448. if (this.is_callee_pure(compressor)) return false;
  449. if (this.expression.may_throw(compressor)) return true;
  450. return !(this.expression instanceof AST_Lambda)
  451. || any(this.expression.body, compressor);
  452. });
  453. def_may_throw(AST_Case, function(compressor) {
  454. return this.expression.may_throw(compressor)
  455. || any(this.body, compressor);
  456. });
  457. def_may_throw(AST_Conditional, function(compressor) {
  458. return this.condition.may_throw(compressor)
  459. || this.consequent.may_throw(compressor)
  460. || this.alternative.may_throw(compressor);
  461. });
  462. def_may_throw(AST_Definitions, function(compressor) {
  463. return any(this.definitions, compressor);
  464. });
  465. def_may_throw(AST_If, function(compressor) {
  466. return this.condition.may_throw(compressor)
  467. || this.body && this.body.may_throw(compressor)
  468. || this.alternative && this.alternative.may_throw(compressor);
  469. });
  470. def_may_throw(AST_LabeledStatement, function(compressor) {
  471. return this.body.may_throw(compressor);
  472. });
  473. def_may_throw(AST_Object, function(compressor) {
  474. return any(this.properties, compressor);
  475. });
  476. def_may_throw(AST_ObjectProperty, function(compressor) {
  477. // TODO key may throw too
  478. return this.value ? this.value.may_throw(compressor) : false;
  479. });
  480. def_may_throw(AST_ClassProperty, function(compressor) {
  481. return (
  482. this.computed_key() && this.key.may_throw(compressor)
  483. || this.static && this.value && this.value.may_throw(compressor)
  484. );
  485. });
  486. def_may_throw(AST_ConciseMethod, function(compressor) {
  487. return this.computed_key() && this.key.may_throw(compressor);
  488. });
  489. def_may_throw(AST_ObjectGetter, function(compressor) {
  490. return this.computed_key() && this.key.may_throw(compressor);
  491. });
  492. def_may_throw(AST_ObjectSetter, function(compressor) {
  493. return this.computed_key() && this.key.may_throw(compressor);
  494. });
  495. def_may_throw(AST_Return, function(compressor) {
  496. return this.value && this.value.may_throw(compressor);
  497. });
  498. def_may_throw(AST_Sequence, function(compressor) {
  499. return any(this.expressions, compressor);
  500. });
  501. def_may_throw(AST_SimpleStatement, function(compressor) {
  502. return this.body.may_throw(compressor);
  503. });
  504. def_may_throw(AST_Dot, function(compressor) {
  505. if (is_nullish(this, compressor)) return false;
  506. return !this.optional && this.expression.may_throw_on_access(compressor)
  507. || this.expression.may_throw(compressor);
  508. });
  509. def_may_throw(AST_Sub, function(compressor) {
  510. if (is_nullish(this, compressor)) return false;
  511. return !this.optional && this.expression.may_throw_on_access(compressor)
  512. || this.expression.may_throw(compressor)
  513. || this.property.may_throw(compressor);
  514. });
  515. def_may_throw(AST_Chain, function(compressor) {
  516. return this.expression.may_throw(compressor);
  517. });
  518. def_may_throw(AST_Switch, function(compressor) {
  519. return this.expression.may_throw(compressor)
  520. || any(this.body, compressor);
  521. });
  522. def_may_throw(AST_SymbolRef, function(compressor) {
  523. return !this.is_declared(compressor) && !pure_prop_access_globals.has(this.name);
  524. });
  525. def_may_throw(AST_SymbolClassProperty, return_false);
  526. def_may_throw(AST_Try, function(compressor) {
  527. return this.bcatch ? this.bcatch.may_throw(compressor) : this.body.may_throw(compressor)
  528. || this.bfinally && this.bfinally.may_throw(compressor);
  529. });
  530. def_may_throw(AST_Unary, function(compressor) {
  531. if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef)
  532. return false;
  533. return this.expression.may_throw(compressor);
  534. });
  535. def_may_throw(AST_VarDef, function(compressor) {
  536. if (!this.value) return false;
  537. return this.value.may_throw(compressor);
  538. });
  539. })(function(node, func) {
  540. node.DEFMETHOD("may_throw", func);
  541. });
  542. // determine if expression is constant
  543. (function(def_is_constant_expression) {
  544. function all_refs_local(scope) {
  545. let result = true;
  546. walk(this, node => {
  547. if (node instanceof AST_SymbolRef) {
  548. if (has_flag(this, INLINED)) {
  549. result = false;
  550. return walk_abort;
  551. }
  552. var def = node.definition();
  553. if (
  554. member(def, this.enclosed)
  555. && !this.variables.has(def.name)
  556. ) {
  557. if (scope) {
  558. var scope_def = scope.find_variable(node);
  559. if (def.undeclared ? !scope_def : scope_def === def) {
  560. result = "f";
  561. return true;
  562. }
  563. }
  564. result = false;
  565. return walk_abort;
  566. }
  567. return true;
  568. }
  569. if (node instanceof AST_This && this instanceof AST_Arrow) {
  570. result = false;
  571. return walk_abort;
  572. }
  573. });
  574. return result;
  575. }
  576. def_is_constant_expression(AST_Node, return_false);
  577. def_is_constant_expression(AST_Constant, return_true);
  578. def_is_constant_expression(AST_Class, function(scope) {
  579. if (this.extends && !this.extends.is_constant_expression(scope)) {
  580. return false;
  581. }
  582. for (const prop of this.properties) {
  583. if (prop.computed_key() && !prop.key.is_constant_expression(scope)) {
  584. return false;
  585. }
  586. if (prop.static && prop.value && !prop.value.is_constant_expression(scope)) {
  587. return false;
  588. }
  589. if (prop instanceof AST_ClassStaticBlock) {
  590. return false;
  591. }
  592. }
  593. return all_refs_local.call(this, scope);
  594. });
  595. def_is_constant_expression(AST_Lambda, all_refs_local);
  596. def_is_constant_expression(AST_Unary, function() {
  597. return this.expression.is_constant_expression();
  598. });
  599. def_is_constant_expression(AST_Binary, function() {
  600. return this.left.is_constant_expression()
  601. && this.right.is_constant_expression();
  602. });
  603. def_is_constant_expression(AST_Array, function() {
  604. return this.elements.every((l) => l.is_constant_expression());
  605. });
  606. def_is_constant_expression(AST_Object, function() {
  607. return this.properties.every((l) => l.is_constant_expression());
  608. });
  609. def_is_constant_expression(AST_ObjectProperty, function() {
  610. return !!(!(this.key instanceof AST_Node) && this.value && this.value.is_constant_expression());
  611. });
  612. })(function(node, func) {
  613. node.DEFMETHOD("is_constant_expression", func);
  614. });
  615. // may_throw_on_access()
  616. // returns true if this node may be null, undefined or contain `AST_Accessor`
  617. (function(def_may_throw_on_access) {
  618. AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
  619. return !compressor.option("pure_getters")
  620. || this._dot_throw(compressor);
  621. });
  622. function is_strict(compressor) {
  623. return /strict/.test(compressor.option("pure_getters"));
  624. }
  625. def_may_throw_on_access(AST_Node, is_strict);
  626. def_may_throw_on_access(AST_Null, return_true);
  627. def_may_throw_on_access(AST_Undefined, return_true);
  628. def_may_throw_on_access(AST_Constant, return_false);
  629. def_may_throw_on_access(AST_Array, return_false);
  630. def_may_throw_on_access(AST_Object, function(compressor) {
  631. if (!is_strict(compressor)) return false;
  632. for (var i = this.properties.length; --i >=0;)
  633. if (this.properties[i]._dot_throw(compressor)) return true;
  634. return false;
  635. });
  636. // Do not be as strict with classes as we are with objects.
  637. // Hopefully the community is not going to abuse static getters and setters.
  638. // https://github.com/terser/terser/issues/724#issuecomment-643655656
  639. def_may_throw_on_access(AST_Class, return_false);
  640. def_may_throw_on_access(AST_ObjectProperty, return_false);
  641. def_may_throw_on_access(AST_ObjectGetter, return_true);
  642. def_may_throw_on_access(AST_Expansion, function(compressor) {
  643. return this.expression._dot_throw(compressor);
  644. });
  645. def_may_throw_on_access(AST_Function, return_false);
  646. def_may_throw_on_access(AST_Arrow, return_false);
  647. def_may_throw_on_access(AST_UnaryPostfix, return_false);
  648. def_may_throw_on_access(AST_UnaryPrefix, function() {
  649. return this.operator == "void";
  650. });
  651. def_may_throw_on_access(AST_Binary, function(compressor) {
  652. return (this.operator == "&&" || this.operator == "||" || this.operator == "??")
  653. && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
  654. });
  655. def_may_throw_on_access(AST_Assign, function(compressor) {
  656. if (this.logical) return true;
  657. return this.operator == "="
  658. && this.right._dot_throw(compressor);
  659. });
  660. def_may_throw_on_access(AST_Conditional, function(compressor) {
  661. return this.consequent._dot_throw(compressor)
  662. || this.alternative._dot_throw(compressor);
  663. });
  664. def_may_throw_on_access(AST_Dot, function(compressor) {
  665. if (!is_strict(compressor)) return false;
  666. if (this.property == "prototype") {
  667. return !(
  668. this.expression instanceof AST_Function
  669. || this.expression instanceof AST_Class
  670. );
  671. }
  672. return true;
  673. });
  674. def_may_throw_on_access(AST_Chain, function(compressor) {
  675. return this.expression._dot_throw(compressor);
  676. });
  677. def_may_throw_on_access(AST_Sequence, function(compressor) {
  678. return this.tail_node()._dot_throw(compressor);
  679. });
  680. def_may_throw_on_access(AST_SymbolRef, function(compressor) {
  681. if (this.name === "arguments" && this.scope instanceof AST_Lambda) return false;
  682. if (has_flag(this, UNDEFINED)) return true;
  683. if (!is_strict(compressor)) return false;
  684. if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
  685. if (this.is_immutable()) return false;
  686. var fixed = this.fixed_value();
  687. return !fixed || fixed._dot_throw(compressor);
  688. });
  689. })(function(node, func) {
  690. node.DEFMETHOD("_dot_throw", func);
  691. });
  692. export function is_lhs(node, parent) {
  693. if (parent instanceof AST_Unary && unary_side_effects.has(parent.operator)) return parent.expression;
  694. if (parent instanceof AST_Assign && parent.left === node) return node;
  695. if (parent instanceof AST_ForIn && parent.init === node) return node;
  696. }
  697. // method to negate an expression
  698. (function(def_negate) {
  699. function basic_negation(exp) {
  700. return make_node(AST_UnaryPrefix, exp, {
  701. operator: "!",
  702. expression: exp
  703. });
  704. }
  705. function best(orig, alt, first_in_statement) {
  706. var negated = basic_negation(orig);
  707. if (first_in_statement) {
  708. var stat = make_node(AST_SimpleStatement, alt, {
  709. body: alt
  710. });
  711. return best_of_expression(negated, stat) === stat ? alt : negated;
  712. }
  713. return best_of_expression(negated, alt);
  714. }
  715. def_negate(AST_Node, function() {
  716. return basic_negation(this);
  717. });
  718. def_negate(AST_Statement, function() {
  719. throw new Error("Cannot negate a statement");
  720. });
  721. def_negate(AST_Function, function() {
  722. return basic_negation(this);
  723. });
  724. def_negate(AST_Class, function() {
  725. return basic_negation(this);
  726. });
  727. def_negate(AST_Arrow, function() {
  728. return basic_negation(this);
  729. });
  730. def_negate(AST_UnaryPrefix, function() {
  731. if (this.operator == "!")
  732. return this.expression;
  733. return basic_negation(this);
  734. });
  735. def_negate(AST_Sequence, function(compressor) {
  736. var expressions = this.expressions.slice();
  737. expressions.push(expressions.pop().negate(compressor));
  738. return make_sequence(this, expressions);
  739. });
  740. def_negate(AST_Conditional, function(compressor, first_in_statement) {
  741. var self = this.clone();
  742. self.consequent = self.consequent.negate(compressor);
  743. self.alternative = self.alternative.negate(compressor);
  744. return best(this, self, first_in_statement);
  745. });
  746. def_negate(AST_Binary, function(compressor, first_in_statement) {
  747. var self = this.clone(), op = this.operator;
  748. if (compressor.option("unsafe_comps")) {
  749. switch (op) {
  750. case "<=" : self.operator = ">" ; return self;
  751. case "<" : self.operator = ">=" ; return self;
  752. case ">=" : self.operator = "<" ; return self;
  753. case ">" : self.operator = "<=" ; return self;
  754. }
  755. }
  756. switch (op) {
  757. case "==" : self.operator = "!="; return self;
  758. case "!=" : self.operator = "=="; return self;
  759. case "===": self.operator = "!=="; return self;
  760. case "!==": self.operator = "==="; return self;
  761. case "&&":
  762. self.operator = "||";
  763. self.left = self.left.negate(compressor, first_in_statement);
  764. self.right = self.right.negate(compressor);
  765. return best(this, self, first_in_statement);
  766. case "||":
  767. self.operator = "&&";
  768. self.left = self.left.negate(compressor, first_in_statement);
  769. self.right = self.right.negate(compressor);
  770. return best(this, self, first_in_statement);
  771. }
  772. return basic_negation(this);
  773. });
  774. })(function(node, func) {
  775. node.DEFMETHOD("negate", function(compressor, first_in_statement) {
  776. return func.call(this, compressor, first_in_statement);
  777. });
  778. });
  779. (function (def_bitwise_negate) {
  780. function basic_negation(exp) {
  781. return make_node(AST_UnaryPrefix, exp, {
  782. operator: "~",
  783. expression: exp
  784. });
  785. }
  786. def_bitwise_negate(AST_Node, function() {
  787. return basic_negation(this);
  788. });
  789. def_bitwise_negate(AST_Number, function() {
  790. const neg = ~this.value;
  791. if (neg.toString().length > this.value.toString().length) {
  792. return basic_negation(this);
  793. }
  794. return make_node(AST_Number, this, { value: neg });
  795. });
  796. def_bitwise_negate(AST_UnaryPrefix, function(in_32_bit_context) {
  797. if (this.operator == "~" && (in_32_bit_context || this.expression.is_32_bit_integer())) {
  798. return this.expression;
  799. } else {
  800. return basic_negation(this);
  801. }
  802. });
  803. })(function (node, func) {
  804. node.DEFMETHOD("bitwise_negate", func);
  805. });
  806. // Is the callee of this function pure?
  807. var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
  808. AST_Call.DEFMETHOD("is_callee_pure", function(compressor) {
  809. if (compressor.option("unsafe")) {
  810. var expr = this.expression;
  811. var first_arg = (this.args && this.args[0] && this.args[0].evaluate(compressor));
  812. if (
  813. expr.expression && expr.expression.name === "hasOwnProperty" &&
  814. (first_arg == null || first_arg.thedef && first_arg.thedef.undeclared)
  815. ) {
  816. return false;
  817. }
  818. if (is_undeclared_ref(expr) && global_pure_fns.has(expr.name)) return true;
  819. if (
  820. expr instanceof AST_Dot
  821. && is_undeclared_ref(expr.expression)
  822. && is_pure_native_fn(expr.expression.name, expr.property)
  823. ) {
  824. return true;
  825. }
  826. }
  827. if ((this instanceof AST_New) && compressor.option("pure_new")) {
  828. return true;
  829. }
  830. if (compressor.option("side_effects") && has_annotation(this, _PURE)) {
  831. return true;
  832. }
  833. return !compressor.pure_funcs(this);
  834. });
  835. // If I call this, is it a pure function?
  836. AST_Node.DEFMETHOD("is_call_pure", return_false);
  837. AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
  838. if (!compressor.option("unsafe")) return;
  839. const expr = this.expression;
  840. let native_obj;
  841. if (expr instanceof AST_Array) {
  842. native_obj = "Array";
  843. } else if (expr.is_boolean()) {
  844. native_obj = "Boolean";
  845. } else if (expr.is_number(compressor)) {
  846. native_obj = "Number";
  847. } else if (expr instanceof AST_RegExp) {
  848. native_obj = "RegExp";
  849. } else if (expr.is_string(compressor)) {
  850. native_obj = "String";
  851. } else if (!this.may_throw_on_access(compressor)) {
  852. native_obj = "Object";
  853. }
  854. return native_obj != null && is_pure_native_method(native_obj, this.property);
  855. });
  856. // tell me if a statement aborts
  857. export const aborts = (thing) => thing && thing.aborts();
  858. (function(def_aborts) {
  859. def_aborts(AST_Statement, return_null);
  860. def_aborts(AST_Jump, return_this);
  861. function block_aborts() {
  862. for (var i = 0; i < this.body.length; i++) {
  863. if (aborts(this.body[i])) {
  864. return this.body[i];
  865. }
  866. }
  867. return null;
  868. }
  869. def_aborts(AST_Import, return_null);
  870. def_aborts(AST_BlockStatement, block_aborts);
  871. def_aborts(AST_SwitchBranch, block_aborts);
  872. def_aborts(AST_DefClass, function () {
  873. for (const prop of this.properties) {
  874. if (prop instanceof AST_ClassStaticBlock) {
  875. if (prop.aborts()) return prop;
  876. }
  877. }
  878. return null;
  879. });
  880. def_aborts(AST_ClassStaticBlock, block_aborts);
  881. def_aborts(AST_If, function() {
  882. return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
  883. });
  884. })(function(node, func) {
  885. node.DEFMETHOD("aborts", func);
  886. });
  887. AST_Node.DEFMETHOD("contains_this", function() {
  888. return walk(this, node => {
  889. if (node instanceof AST_This) return walk_abort;
  890. if (
  891. node !== this
  892. && node instanceof AST_Scope
  893. && !(node instanceof AST_Arrow)
  894. ) {
  895. return true;
  896. }
  897. });
  898. });
  899. export function is_modified(compressor, tw, node, value, level, immutable) {
  900. var parent = tw.parent(level);
  901. var lhs = is_lhs(node, parent);
  902. if (lhs) return lhs;
  903. if (!immutable
  904. && parent instanceof AST_Call
  905. && parent.expression === node
  906. && !(value instanceof AST_Arrow)
  907. && !(value instanceof AST_Class)
  908. && !parent.is_callee_pure(compressor)
  909. && (!(value instanceof AST_Function)
  910. || !(parent instanceof AST_New) && value.contains_this())) {
  911. return true;
  912. }
  913. if (parent instanceof AST_Array) {
  914. return is_modified(compressor, tw, parent, parent, level + 1);
  915. }
  916. if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
  917. var obj = tw.parent(level + 1);
  918. return is_modified(compressor, tw, obj, obj, level + 2);
  919. }
  920. if (parent instanceof AST_PropAccess && parent.expression === node) {
  921. var prop = read_property(value, parent.property);
  922. return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
  923. }
  924. }