reduce-vars.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  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_Accessor,
  35. AST_Array,
  36. AST_Assign,
  37. AST_Await,
  38. AST_Binary,
  39. AST_Block,
  40. AST_Call,
  41. AST_Case,
  42. AST_Chain,
  43. AST_Class,
  44. AST_ClassStaticBlock,
  45. AST_ClassExpression,
  46. AST_Conditional,
  47. AST_Default,
  48. AST_Defun,
  49. AST_Destructuring,
  50. AST_Do,
  51. AST_Exit,
  52. AST_Expansion,
  53. AST_For,
  54. AST_ForIn,
  55. AST_If,
  56. AST_LabeledStatement,
  57. AST_Lambda,
  58. AST_New,
  59. AST_Node,
  60. AST_Number,
  61. AST_ObjectKeyVal,
  62. AST_PropAccess,
  63. AST_Scope,
  64. AST_Sequence,
  65. AST_SimpleStatement,
  66. AST_Symbol,
  67. AST_SymbolCatch,
  68. AST_SymbolConst,
  69. AST_SymbolDeclaration,
  70. AST_SymbolDefun,
  71. AST_SymbolFunarg,
  72. AST_SymbolLambda,
  73. AST_SymbolRef,
  74. AST_This,
  75. AST_Toplevel,
  76. AST_Try,
  77. AST_Unary,
  78. AST_UnaryPrefix,
  79. AST_Undefined,
  80. AST_VarDef,
  81. AST_While,
  82. AST_Yield,
  83. walk,
  84. walk_body,
  85. TreeWalker,
  86. _INLINE,
  87. _NOINLINE,
  88. _PURE
  89. } from "../ast.js";
  90. import { HOP, make_node, noop } from "../utils/index.js";
  91. import { lazy_op, is_modified, is_lhs } from "./inference.js";
  92. import { INLINED, clear_flag } from "./compressor-flags.js";
  93. import { read_property, has_break_or_continue, is_recursive_ref } from "./common.js";
  94. /**
  95. * Define the method AST_Node#reduce_vars, which goes through the AST in
  96. * execution order to perform basic flow analysis
  97. */
  98. function def_reduce_vars(node, func) {
  99. node.DEFMETHOD("reduce_vars", func);
  100. }
  101. def_reduce_vars(AST_Node, noop);
  102. /** Clear definition properties */
  103. function reset_def(compressor, def) {
  104. def.assignments = 0;
  105. def.chained = false;
  106. def.direct_access = false;
  107. def.escaped = 0;
  108. def.recursive_refs = 0;
  109. def.references = [];
  110. def.single_use = undefined;
  111. if (
  112. def.scope.pinned()
  113. || (def.orig[0] instanceof AST_SymbolFunarg && def.scope.uses_arguments)
  114. ) {
  115. def.fixed = false;
  116. } else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) {
  117. def.fixed = def.init;
  118. } else {
  119. def.fixed = false;
  120. }
  121. }
  122. function reset_variables(tw, compressor, node) {
  123. node.variables.forEach(function(def) {
  124. reset_def(compressor, def);
  125. if (def.fixed === null) {
  126. tw.defs_to_safe_ids.set(def.id, tw.safe_ids);
  127. mark(tw, def, true);
  128. } else if (def.fixed) {
  129. tw.loop_ids.set(def.id, tw.in_loop);
  130. mark(tw, def, true);
  131. }
  132. });
  133. }
  134. function reset_block_variables(compressor, node) {
  135. if (node.block_scope) node.block_scope.variables.forEach((def) => {
  136. reset_def(compressor, def);
  137. });
  138. }
  139. function push(tw) {
  140. tw.safe_ids = Object.create(tw.safe_ids);
  141. }
  142. function pop(tw) {
  143. tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
  144. }
  145. function mark(tw, def, safe) {
  146. tw.safe_ids[def.id] = safe;
  147. }
  148. function safe_to_read(tw, def) {
  149. if (def.single_use == "m") return false;
  150. if (tw.safe_ids[def.id]) {
  151. if (def.fixed == null) {
  152. var orig = def.orig[0];
  153. if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
  154. def.fixed = make_node(AST_Undefined, orig);
  155. }
  156. return true;
  157. }
  158. return def.fixed instanceof AST_Defun;
  159. }
  160. function safe_to_assign(tw, def, scope, value) {
  161. if (def.fixed === undefined) return true;
  162. let def_safe_ids;
  163. if (def.fixed === null
  164. && (def_safe_ids = tw.defs_to_safe_ids.get(def.id))
  165. ) {
  166. def_safe_ids[def.id] = false;
  167. tw.defs_to_safe_ids.delete(def.id);
  168. return true;
  169. }
  170. if (!HOP(tw.safe_ids, def.id)) return false;
  171. if (!safe_to_read(tw, def)) return false;
  172. if (def.fixed === false) return false;
  173. if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
  174. if (def.fixed instanceof AST_Defun) {
  175. return value instanceof AST_Node && def.fixed.parent_scope === scope;
  176. }
  177. return def.orig.every((sym) => {
  178. return !(sym instanceof AST_SymbolConst
  179. || sym instanceof AST_SymbolDefun
  180. || sym instanceof AST_SymbolLambda);
  181. });
  182. }
  183. function ref_once(tw, compressor, def) {
  184. return compressor.option("unused")
  185. && !def.scope.pinned()
  186. && def.references.length - def.recursive_refs == 1
  187. && tw.loop_ids.get(def.id) === tw.in_loop;
  188. }
  189. function is_immutable(value) {
  190. if (!value) return false;
  191. return value.is_constant()
  192. || value instanceof AST_Lambda
  193. || value instanceof AST_This;
  194. }
  195. // A definition "escapes" when its value can leave the point of use.
  196. // Example: `a = b || c`
  197. // In this example, "b" and "c" are escaping, because they're going into "a"
  198. //
  199. // def.escaped is != 0 when it escapes.
  200. //
  201. // When greater than 1, it means that N chained properties will be read off
  202. // of that def before an escape occurs. This is useful for evaluating
  203. // property accesses, where you need to know when to stop.
  204. function mark_escaped(tw, d, scope, node, value, level = 0, depth = 1) {
  205. var parent = tw.parent(level);
  206. if (value) {
  207. if (value.is_constant()) return;
  208. if (value instanceof AST_ClassExpression) return;
  209. }
  210. if (
  211. parent instanceof AST_Assign && (parent.operator === "=" || parent.logical) && node === parent.right
  212. || parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New)
  213. || parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
  214. || parent instanceof AST_VarDef && node === parent.value
  215. || parent instanceof AST_Yield && node === parent.value && node.scope !== d.scope
  216. ) {
  217. if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
  218. if (!d.escaped || d.escaped > depth) d.escaped = depth;
  219. return;
  220. } else if (
  221. parent instanceof AST_Array
  222. || parent instanceof AST_Await
  223. || parent instanceof AST_Binary && lazy_op.has(parent.operator)
  224. || parent instanceof AST_Conditional && node !== parent.condition
  225. || parent instanceof AST_Expansion
  226. || parent instanceof AST_Sequence && node === parent.tail_node()
  227. ) {
  228. mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
  229. } else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
  230. var obj = tw.parent(level + 1);
  231. mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
  232. } else if (parent instanceof AST_PropAccess && node === parent.expression) {
  233. value = read_property(value, parent.property);
  234. mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
  235. if (value) return;
  236. }
  237. if (level > 0) return;
  238. if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
  239. if (parent instanceof AST_SimpleStatement) return;
  240. d.direct_access = true;
  241. }
  242. const suppress = node => walk(node, node => {
  243. if (!(node instanceof AST_Symbol)) return;
  244. var d = node.definition();
  245. if (!d) return;
  246. if (node instanceof AST_SymbolRef) d.references.push(node);
  247. d.fixed = false;
  248. });
  249. def_reduce_vars(AST_Accessor, function(tw, descend, compressor) {
  250. push(tw);
  251. reset_variables(tw, compressor, this);
  252. descend();
  253. pop(tw);
  254. return true;
  255. });
  256. def_reduce_vars(AST_Assign, function(tw, descend, compressor) {
  257. var node = this;
  258. if (node.left instanceof AST_Destructuring) {
  259. suppress(node.left);
  260. return;
  261. }
  262. const finish_walk = () => {
  263. if (node.logical) {
  264. node.left.walk(tw);
  265. push(tw);
  266. node.right.walk(tw);
  267. pop(tw);
  268. return true;
  269. }
  270. };
  271. var sym = node.left;
  272. if (!(sym instanceof AST_SymbolRef)) return finish_walk();
  273. var def = sym.definition();
  274. var safe = safe_to_assign(tw, def, sym.scope, node.right);
  275. def.assignments++;
  276. if (!safe) return finish_walk();
  277. var fixed = def.fixed;
  278. if (!fixed && node.operator != "=" && !node.logical) return finish_walk();
  279. var eq = node.operator == "=";
  280. var value = eq ? node.right : node;
  281. if (is_modified(compressor, tw, node, value, 0)) return finish_walk();
  282. def.references.push(sym);
  283. if (!node.logical) {
  284. if (!eq) def.chained = true;
  285. def.fixed = eq ? function() {
  286. return node.right;
  287. } : function() {
  288. return make_node(AST_Binary, node, {
  289. operator: node.operator.slice(0, -1),
  290. left: fixed instanceof AST_Node ? fixed : fixed(),
  291. right: node.right
  292. });
  293. };
  294. }
  295. if (node.logical) {
  296. mark(tw, def, false);
  297. push(tw);
  298. node.right.walk(tw);
  299. pop(tw);
  300. return true;
  301. }
  302. mark(tw, def, false);
  303. node.right.walk(tw);
  304. mark(tw, def, true);
  305. mark_escaped(tw, def, sym.scope, node, value, 0, 1);
  306. return true;
  307. });
  308. def_reduce_vars(AST_Binary, function(tw) {
  309. if (!lazy_op.has(this.operator)) return;
  310. this.left.walk(tw);
  311. push(tw);
  312. this.right.walk(tw);
  313. pop(tw);
  314. return true;
  315. });
  316. def_reduce_vars(AST_Block, function(tw, descend, compressor) {
  317. reset_block_variables(compressor, this);
  318. });
  319. def_reduce_vars(AST_Case, function(tw) {
  320. push(tw);
  321. this.expression.walk(tw);
  322. pop(tw);
  323. push(tw);
  324. walk_body(this, tw);
  325. pop(tw);
  326. return true;
  327. });
  328. def_reduce_vars(AST_Class, function(tw, descend) {
  329. clear_flag(this, INLINED);
  330. push(tw);
  331. descend();
  332. pop(tw);
  333. return true;
  334. });
  335. def_reduce_vars(AST_ClassStaticBlock, function(tw, descend, compressor) {
  336. reset_block_variables(compressor, this);
  337. });
  338. def_reduce_vars(AST_Conditional, function(tw) {
  339. this.condition.walk(tw);
  340. push(tw);
  341. this.consequent.walk(tw);
  342. pop(tw);
  343. push(tw);
  344. this.alternative.walk(tw);
  345. pop(tw);
  346. return true;
  347. });
  348. def_reduce_vars(AST_Chain, function(tw, descend) {
  349. // Chains' conditions apply left-to-right, cumulatively.
  350. // If we walk normally we don't go in that order because we would pop before pushing again
  351. // Solution: AST_PropAccess and AST_Call push when they are optional, and never pop.
  352. // Then we pop everything when they are done being walked.
  353. const safe_ids = tw.safe_ids;
  354. descend();
  355. // Unroll back to start
  356. tw.safe_ids = safe_ids;
  357. return true;
  358. });
  359. def_reduce_vars(AST_Call, function (tw) {
  360. this.expression.walk(tw);
  361. if (this.optional) {
  362. // Never pop -- it's popped at AST_Chain above
  363. push(tw);
  364. }
  365. for (const arg of this.args) arg.walk(tw);
  366. return true;
  367. });
  368. def_reduce_vars(AST_PropAccess, function (tw) {
  369. if (!this.optional) return;
  370. this.expression.walk(tw);
  371. // Never pop -- it's popped at AST_Chain above
  372. push(tw);
  373. if (this.property instanceof AST_Node) this.property.walk(tw);
  374. return true;
  375. });
  376. def_reduce_vars(AST_Default, function(tw, descend) {
  377. push(tw);
  378. descend();
  379. pop(tw);
  380. return true;
  381. });
  382. function mark_lambda(tw, descend, compressor) {
  383. clear_flag(this, INLINED);
  384. push(tw);
  385. reset_variables(tw, compressor, this);
  386. var iife;
  387. if (!this.name
  388. && !this.uses_arguments
  389. && !this.pinned()
  390. && (iife = tw.parent()) instanceof AST_Call
  391. && iife.expression === this
  392. && !iife.args.some(arg => arg instanceof AST_Expansion)
  393. && this.argnames.every(arg_name => arg_name instanceof AST_Symbol)
  394. ) {
  395. // Virtually turn IIFE parameters into variable definitions:
  396. // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
  397. // So existing transformation rules can work on them.
  398. this.argnames.forEach((arg, i) => {
  399. if (!arg.definition) return;
  400. var d = arg.definition();
  401. // Avoid setting fixed when there's more than one origin for a variable value
  402. if (d.orig.length > 1) return;
  403. if (d.fixed === undefined && (!this.uses_arguments || tw.has_directive("use strict"))) {
  404. d.fixed = function() {
  405. return iife.args[i] || make_node(AST_Undefined, iife);
  406. };
  407. tw.loop_ids.set(d.id, tw.in_loop);
  408. mark(tw, d, true);
  409. } else {
  410. d.fixed = false;
  411. }
  412. });
  413. }
  414. descend();
  415. pop(tw);
  416. handle_defined_after_hoist(this);
  417. return true;
  418. }
  419. /**
  420. * It's possible for a hoisted function to use something that's not defined yet. Example:
  421. *
  422. * hoisted();
  423. * var defined_after = true;
  424. * function hoisted() {
  425. * // use defined_after
  426. * }
  427. *
  428. * This function is called on the parent to handle this issue.
  429. */
  430. function handle_defined_after_hoist(parent) {
  431. const defuns = [];
  432. walk(parent, node => {
  433. if (node === parent) return;
  434. if (node instanceof AST_Defun) defuns.push(node);
  435. if (
  436. node instanceof AST_Scope
  437. || node instanceof AST_SimpleStatement
  438. ) return true;
  439. });
  440. const symbols_of_interest = new Set();
  441. const defuns_of_interest = new Set();
  442. const potential_conflicts = [];
  443. for (const defun of defuns) {
  444. const fname_def = defun.name.definition();
  445. const found_self_ref_in_other_defuns = defuns.some(
  446. d => d !== defun && d.enclosed.indexOf(fname_def) !== -1
  447. );
  448. for (const def of defun.enclosed) {
  449. if (
  450. def.fixed === false
  451. || def === fname_def
  452. || def.scope.get_defun_scope() !== parent
  453. ) {
  454. continue;
  455. }
  456. // defun is hoisted, so always safe
  457. if (
  458. def.assignments === 0
  459. && def.orig.length === 1
  460. && def.orig[0] instanceof AST_SymbolDefun
  461. ) {
  462. continue;
  463. }
  464. if (found_self_ref_in_other_defuns) {
  465. def.fixed = false;
  466. continue;
  467. }
  468. // for the slower checks below this loop
  469. potential_conflicts.push({ defun, def, fname_def });
  470. symbols_of_interest.add(def.id);
  471. symbols_of_interest.add(fname_def.id);
  472. defuns_of_interest.add(defun);
  473. }
  474. }
  475. // linearize all symbols, and locate defs that are read after the defun
  476. if (potential_conflicts.length) {
  477. // All "symbols of interest", that is, defuns or defs, that we found.
  478. // These are placed in order so we can check which is after which.
  479. const found_symbols = [];
  480. // Indices of `found_symbols` which are writes
  481. const found_symbol_writes = new Set();
  482. // Defun ranges are recorded because we don't care if a function uses the def internally
  483. const defun_ranges = new Map();
  484. let tw;
  485. parent.walk((tw = new TreeWalker((node, descend) => {
  486. if (node instanceof AST_Defun && defuns_of_interest.has(node)) {
  487. const start = found_symbols.length;
  488. descend();
  489. const end = found_symbols.length;
  490. defun_ranges.set(node, { start, end });
  491. return true;
  492. }
  493. // if we found a defun on the list, mark IN_DEFUN=id and descend
  494. if (node instanceof AST_Symbol && node.thedef) {
  495. const id = node.definition().id;
  496. if (symbols_of_interest.has(id)) {
  497. if (node instanceof AST_SymbolDeclaration || is_lhs(node, tw)) {
  498. found_symbol_writes.add(found_symbols.length);
  499. }
  500. found_symbols.push(id);
  501. }
  502. }
  503. })));
  504. for (const { def, defun, fname_def } of potential_conflicts) {
  505. const defun_range = defun_ranges.get(defun);
  506. // find the index in `found_symbols`, with some special rules:
  507. const find = (sym_id, starting_at = 0, must_be_write = false) => {
  508. let index = starting_at;
  509. for (;;) {
  510. index = found_symbols.indexOf(sym_id, index);
  511. if (index === -1) {
  512. break;
  513. } else if (index >= defun_range.start && index < defun_range.end) {
  514. index = defun_range.end;
  515. continue;
  516. } else if (must_be_write && !found_symbol_writes.has(index)) {
  517. index++;
  518. continue;
  519. } else {
  520. break;
  521. }
  522. }
  523. return index;
  524. };
  525. const read_defun_at = find(fname_def.id);
  526. const wrote_def_at = find(def.id, read_defun_at + 1, true);
  527. const wrote_def_after_reading_defun = read_defun_at != -1 && wrote_def_at != -1 && wrote_def_at > read_defun_at;
  528. if (wrote_def_after_reading_defun) {
  529. def.fixed = false;
  530. }
  531. }
  532. }
  533. }
  534. def_reduce_vars(AST_Lambda, mark_lambda);
  535. def_reduce_vars(AST_Do, function(tw, descend, compressor) {
  536. reset_block_variables(compressor, this);
  537. const saved_loop = tw.in_loop;
  538. tw.in_loop = this;
  539. push(tw);
  540. this.body.walk(tw);
  541. if (has_break_or_continue(this)) {
  542. pop(tw);
  543. push(tw);
  544. }
  545. this.condition.walk(tw);
  546. pop(tw);
  547. tw.in_loop = saved_loop;
  548. return true;
  549. });
  550. def_reduce_vars(AST_For, function(tw, descend, compressor) {
  551. reset_block_variables(compressor, this);
  552. if (this.init) this.init.walk(tw);
  553. const saved_loop = tw.in_loop;
  554. tw.in_loop = this;
  555. push(tw);
  556. if (this.condition) this.condition.walk(tw);
  557. this.body.walk(tw);
  558. if (this.step) {
  559. if (has_break_or_continue(this)) {
  560. pop(tw);
  561. push(tw);
  562. }
  563. this.step.walk(tw);
  564. }
  565. pop(tw);
  566. tw.in_loop = saved_loop;
  567. return true;
  568. });
  569. def_reduce_vars(AST_ForIn, function(tw, descend, compressor) {
  570. reset_block_variables(compressor, this);
  571. suppress(this.init);
  572. this.object.walk(tw);
  573. const saved_loop = tw.in_loop;
  574. tw.in_loop = this;
  575. push(tw);
  576. this.body.walk(tw);
  577. pop(tw);
  578. tw.in_loop = saved_loop;
  579. return true;
  580. });
  581. def_reduce_vars(AST_If, function(tw) {
  582. this.condition.walk(tw);
  583. push(tw);
  584. this.body.walk(tw);
  585. pop(tw);
  586. if (this.alternative) {
  587. push(tw);
  588. this.alternative.walk(tw);
  589. pop(tw);
  590. }
  591. return true;
  592. });
  593. def_reduce_vars(AST_LabeledStatement, function(tw) {
  594. push(tw);
  595. this.body.walk(tw);
  596. pop(tw);
  597. return true;
  598. });
  599. def_reduce_vars(AST_SymbolCatch, function() {
  600. this.definition().fixed = false;
  601. });
  602. def_reduce_vars(AST_SymbolRef, function(tw, descend, compressor) {
  603. var d = this.definition();
  604. d.references.push(this);
  605. if (d.references.length == 1
  606. && !d.fixed
  607. && d.orig[0] instanceof AST_SymbolDefun) {
  608. tw.loop_ids.set(d.id, tw.in_loop);
  609. }
  610. var fixed_value;
  611. if (d.fixed === undefined || !safe_to_read(tw, d)) {
  612. d.fixed = false;
  613. } else if (d.fixed) {
  614. fixed_value = this.fixed_value();
  615. if (
  616. fixed_value instanceof AST_Lambda
  617. && is_recursive_ref(tw, d)
  618. ) {
  619. d.recursive_refs++;
  620. } else if (fixed_value
  621. && !compressor.exposed(d)
  622. && ref_once(tw, compressor, d)
  623. ) {
  624. d.single_use =
  625. fixed_value instanceof AST_Lambda && !fixed_value.pinned()
  626. || fixed_value instanceof AST_Class
  627. || d.scope === this.scope && fixed_value.is_constant_expression();
  628. } else {
  629. d.single_use = false;
  630. }
  631. if (is_modified(compressor, tw, this, fixed_value, 0, is_immutable(fixed_value))) {
  632. if (d.single_use) {
  633. d.single_use = "m";
  634. } else {
  635. d.fixed = false;
  636. }
  637. }
  638. }
  639. mark_escaped(tw, d, this.scope, this, fixed_value, 0, 1);
  640. });
  641. def_reduce_vars(AST_Toplevel, function(tw, descend, compressor) {
  642. this.globals.forEach(function(def) {
  643. reset_def(compressor, def);
  644. });
  645. reset_variables(tw, compressor, this);
  646. descend();
  647. handle_defined_after_hoist(this);
  648. return true;
  649. });
  650. def_reduce_vars(AST_Try, function(tw, descend, compressor) {
  651. reset_block_variables(compressor, this);
  652. push(tw);
  653. this.body.walk(tw);
  654. pop(tw);
  655. if (this.bcatch) {
  656. push(tw);
  657. this.bcatch.walk(tw);
  658. pop(tw);
  659. }
  660. if (this.bfinally) this.bfinally.walk(tw);
  661. return true;
  662. });
  663. def_reduce_vars(AST_Unary, function(tw) {
  664. var node = this;
  665. if (node.operator !== "++" && node.operator !== "--") return;
  666. var exp = node.expression;
  667. if (!(exp instanceof AST_SymbolRef)) return;
  668. var def = exp.definition();
  669. var safe = safe_to_assign(tw, def, exp.scope, true);
  670. def.assignments++;
  671. if (!safe) return;
  672. var fixed = def.fixed;
  673. if (!fixed) return;
  674. def.references.push(exp);
  675. def.chained = true;
  676. def.fixed = function() {
  677. return make_node(AST_Binary, node, {
  678. operator: node.operator.slice(0, -1),
  679. left: make_node(AST_UnaryPrefix, node, {
  680. operator: "+",
  681. expression: fixed instanceof AST_Node ? fixed : fixed()
  682. }),
  683. right: make_node(AST_Number, node, {
  684. value: 1
  685. })
  686. });
  687. };
  688. mark(tw, def, true);
  689. return true;
  690. });
  691. def_reduce_vars(AST_VarDef, function(tw, descend) {
  692. var node = this;
  693. if (node.name instanceof AST_Destructuring) {
  694. suppress(node.name);
  695. return;
  696. }
  697. var d = node.name.definition();
  698. if (node.value) {
  699. if (safe_to_assign(tw, d, node.name.scope, node.value)) {
  700. d.fixed = function() {
  701. return node.value;
  702. };
  703. tw.loop_ids.set(d.id, tw.in_loop);
  704. mark(tw, d, false);
  705. descend();
  706. mark(tw, d, true);
  707. return true;
  708. } else {
  709. d.fixed = false;
  710. }
  711. }
  712. });
  713. def_reduce_vars(AST_While, function(tw, descend, compressor) {
  714. reset_block_variables(compressor, this);
  715. const saved_loop = tw.in_loop;
  716. tw.in_loop = this;
  717. push(tw);
  718. descend();
  719. pop(tw);
  720. tw.in_loop = saved_loop;
  721. return true;
  722. });