terser 5.7.2 → 5.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/ast.js CHANGED
@@ -1330,7 +1330,7 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", {
1330
1330
  }
1331
1331
  }, AST_ObjectProperty);
1332
1332
 
1333
- var AST_ClassPrivateProperty = DEFNODE("ClassProperty", "", {
1333
+ var AST_ClassPrivateProperty = DEFNODE("ClassPrivateProperty", "", {
1334
1334
  $documentation: "A class property for a private property",
1335
1335
  }, AST_ClassProperty);
1336
1336
 
@@ -53,7 +53,6 @@ import {
53
53
  AST_Boolean,
54
54
  AST_Break,
55
55
  AST_Call,
56
- AST_Case,
57
56
  AST_Catch,
58
57
  AST_Chain,
59
58
  AST_Class,
@@ -109,6 +108,7 @@ import {
109
108
  AST_String,
110
109
  AST_Sub,
111
110
  AST_Switch,
111
+ AST_SwitchBranch,
112
112
  AST_Symbol,
113
113
  AST_SymbolBlockDeclaration,
114
114
  AST_SymbolCatch,
@@ -390,7 +390,8 @@ class Compressor extends TreeWalker {
390
390
  var passes = +this.options.passes || 1;
391
391
  var min_count = 1 / 0;
392
392
  var stopping = false;
393
- var mangle = { ie8: this.option("ie8") };
393
+ var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
394
+ var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
394
395
  for (var pass = 0; pass < passes; pass++) {
395
396
  this._toplevel.figure_out_scope(mangle);
396
397
  if (pass === 0 && this.option("drop_console")) {
@@ -1592,58 +1593,251 @@ def_optimize(AST_Switch, function(self, compressor) {
1592
1593
  }
1593
1594
  }
1594
1595
  }
1595
- if (aborts(branch)) {
1596
- var prev = body[body.length - 1];
1597
- if (aborts(prev) && prev.body.length == branch.body.length
1598
- && make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
1599
- prev.body = [];
1600
- }
1601
- }
1602
1596
  body.push(branch);
1603
1597
  }
1604
1598
  while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
1599
+ self.body = body;
1600
+
1601
+ for (let i = 0; i < body.length; i++) {
1602
+ let branch = body[i];
1603
+ if (branch.body.length === 0) continue;
1604
+ if (!aborts(branch)) continue;
1605
+
1606
+ for (let j = i + 1; j < body.length; i++, j++) {
1607
+ let next = body[j];
1608
+ if (next.body.length === 0) continue;
1609
+ if (
1610
+ branches_equivalent(next, branch, false)
1611
+ || (j === body.length - 1 && branches_equivalent(next, branch, true))
1612
+ ) {
1613
+ branch.body = [];
1614
+ branch = next;
1615
+ continue;
1616
+ }
1617
+ break;
1618
+ }
1619
+ }
1620
+
1621
+ let default_or_exact = default_branch || exact_match;
1622
+ default_branch = null;
1623
+ exact_match = null;
1624
+
1625
+ // Prune any empty branches at the end of the switch statement.
1626
+ {
1627
+ let i = body.length - 1;
1628
+ for (; i >= 0; i--) {
1629
+ let bbody = body[i].body;
1630
+ if (is_break(bbody[bbody.length - 1], compressor)) bbody.pop();
1631
+ if (!is_inert_body(body[i])) break;
1632
+ }
1633
+ // i now points to the index of a branch that contains a body. By incrementing, it's
1634
+ // pointing to the first branch that's empty.
1635
+ i++;
1636
+ if (!default_or_exact || body.indexOf(default_or_exact) >= i) {
1637
+ // The default behavior is to do nothing. We can take advantage of that to
1638
+ // remove all case expressions that are side-effect free that also do
1639
+ // nothing, since they'll default to doing nothing. But we can't remove any
1640
+ // case expressions before one that would side-effect, since they may cause
1641
+ // the side-effect to be skipped.
1642
+ for (let j = body.length - 1; j >= i; j--) {
1643
+ let branch = body[j];
1644
+ if (branch === default_or_exact) {
1645
+ default_or_exact = null;
1646
+ body.pop();
1647
+ } else if (!branch.expression.has_side_effects(compressor)) {
1648
+ body.pop();
1649
+ } else {
1650
+ break;
1651
+ }
1652
+ }
1653
+ }
1654
+ }
1655
+
1656
+
1657
+ // Prune side-effect free branches that fall into default.
1658
+ if (default_or_exact) {
1659
+ let default_index = body.indexOf(default_or_exact);
1660
+ let default_body_index = default_index;
1661
+ for (; default_body_index < body.length - 1; default_body_index++) {
1662
+ if (!is_inert_body(body[default_body_index])) break;
1663
+ }
1664
+ let side_effect_index = body.length - 1;
1665
+ for (; side_effect_index >= 0; side_effect_index--) {
1666
+ let branch = body[side_effect_index];
1667
+ if (branch === default_or_exact) continue;
1668
+ if (branch.expression.has_side_effects(compressor)) break;
1669
+ }
1670
+ // If the default behavior comes after any side-effect case expressions,
1671
+ // then we can fold all side-effect free cases into the default branch.
1672
+ // If the side-effect case is after the default, then any side-effect
1673
+ // free cases could prevent the side-effect from occurring.
1674
+ if (default_body_index > side_effect_index) {
1675
+ let prev_body_index = default_index - 1;
1676
+ for (; prev_body_index >= 0; prev_body_index--) {
1677
+ if (!is_inert_body(body[prev_body_index])) break;
1678
+ }
1679
+ let before = Math.max(side_effect_index, prev_body_index) + 1;
1680
+ let after = default_index;
1681
+ if (side_effect_index > default_index) {
1682
+ // If the default falls into the same body as a side-effect
1683
+ // case, then we need preserve that case and only prune the
1684
+ // cases after it.
1685
+ after = side_effect_index;
1686
+ body[side_effect_index].body = body[default_body_index].body;
1687
+ } else {
1688
+ // The default will be the last branch.
1689
+ default_or_exact.body = body[default_body_index].body;
1690
+ }
1691
+
1692
+ // Prune everything after the default (or last side-effect case)
1693
+ // until the next case with a body.
1694
+ body.splice(after + 1, default_body_index - after);
1695
+ // Prune everything before the default that falls into it.
1696
+ body.splice(before, default_index - before);
1697
+ }
1698
+ }
1699
+
1700
+ // See if we can remove the switch entirely if all cases (the default) fall into the same case body.
1701
+ DEFAULT: if (default_or_exact) {
1702
+ let i = body.findIndex(branch => !is_inert_body(branch));
1703
+ let caseBody;
1704
+ // `i` is equal to one of the following:
1705
+ // - `-1`, there is no body in the switch statement.
1706
+ // - `body.length - 1`, all cases fall into the same body.
1707
+ // - anything else, there are multiple bodies in the switch.
1708
+ if (i === body.length - 1) {
1709
+ // All cases fall into the case body.
1710
+ let branch = body[i];
1711
+ if (has_nested_break(self)) break DEFAULT;
1712
+
1713
+ // This is the last case body, and we've already pruned any breaks, so it's
1714
+ // safe to hoist.
1715
+ caseBody = make_node(AST_BlockStatement, branch, {
1716
+ body: branch.body
1717
+ });
1718
+ branch.body = [];
1719
+ } else if (i !== -1) {
1720
+ // If there are multiple bodies, then we cannot optimize anything.
1721
+ break DEFAULT;
1722
+ }
1723
+
1724
+ let sideEffect = body.find(branch => {
1725
+ return (
1726
+ branch !== default_or_exact
1727
+ && branch.expression.has_side_effects(compressor)
1728
+ );
1729
+ });
1730
+ // If no cases cause a side-effect, we can eliminate the switch entirely.
1731
+ if (!sideEffect) {
1732
+ return make_node(AST_BlockStatement, self, {
1733
+ body: decl.concat(
1734
+ statement(self.expression),
1735
+ default_or_exact.expression ? statement(default_or_exact.expression) : [],
1736
+ caseBody || []
1737
+ )
1738
+ }).optimize(compressor);
1739
+ }
1740
+
1741
+ // If we're this far, either there was no body or all cases fell into the same body.
1742
+ // If there was no body, then we don't need a default branch (because the default is
1743
+ // do nothing). If there was a body, we'll extract it to after the switch, so the
1744
+ // switch's new default is to do nothing and we can still prune it.
1745
+ const default_index = body.indexOf(default_or_exact);
1746
+ body.splice(default_index, 1);
1747
+ default_or_exact = null;
1748
+
1749
+ if (caseBody) {
1750
+ // Recurse into switch statement one more time so that we can append the case body
1751
+ // outside of the switch. This recursion will only happen once since we've pruned
1752
+ // the default case.
1753
+ return make_node(AST_BlockStatement, self, {
1754
+ body: decl.concat(self, caseBody)
1755
+ }).optimize(compressor);
1756
+ }
1757
+ // If we fall here, there is a default branch somewhere, there are no case bodies,
1758
+ // and there's a side-effect somewhere. Just let the below paths take care of it.
1759
+ }
1760
+
1605
1761
  if (body.length > 0) {
1606
1762
  body[0].body = decl.concat(body[0].body);
1607
1763
  }
1608
- self.body = body;
1609
- while (branch = body[body.length - 1]) {
1610
- var stat = branch.body[branch.body.length - 1];
1611
- if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
1612
- branch.body.pop();
1613
- if (branch.body.length || branch instanceof AST_Case
1614
- && (default_branch || branch.expression.has_side_effects(compressor))) break;
1615
- if (body.pop() === default_branch) default_branch = null;
1616
- }
1764
+
1617
1765
  if (body.length == 0) {
1618
1766
  return make_node(AST_BlockStatement, self, {
1619
- body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
1620
- body: self.expression
1621
- }))
1767
+ body: decl.concat(statement(self.expression))
1622
1768
  }).optimize(compressor);
1623
1769
  }
1624
- if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
1625
- var has_break = false;
1626
- var tw = new TreeWalker(function(node) {
1627
- if (has_break
1628
- || node instanceof AST_Lambda
1629
- || node instanceof AST_SimpleStatement) return true;
1630
- if (node instanceof AST_Break && tw.loopcontrol_target(node) === self)
1631
- has_break = true;
1632
- });
1633
- self.walk(tw);
1634
- if (!has_break) {
1635
- var statements = body[0].body.slice();
1636
- var exp = body[0].expression;
1637
- if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
1638
- body: exp
1639
- }));
1640
- statements.unshift(make_node(AST_SimpleStatement, self.expression, {
1641
- body:self.expression
1642
- }));
1643
- return make_node(AST_BlockStatement, self, {
1644
- body: statements
1770
+ if (body.length == 1 && !has_nested_break(self)) {
1771
+ // This is the last case body, and we've already pruned any breaks, so it's
1772
+ // safe to hoist.
1773
+ let branch = body[0];
1774
+ return make_node(AST_If, self, {
1775
+ condition: make_node(AST_Binary, self, {
1776
+ operator: "===",
1777
+ left: self.expression,
1778
+ right: branch.expression,
1779
+ }),
1780
+ body: make_node(AST_BlockStatement, branch, {
1781
+ body: branch.body
1782
+ }),
1783
+ alternative: null
1784
+ }).optimize(compressor);
1785
+ }
1786
+ if (body.length === 2 && default_or_exact && !has_nested_break(self)) {
1787
+ let branch = body[0] === default_or_exact ? body[1] : body[0];
1788
+ let exact_exp = default_or_exact.expression && statement(default_or_exact.expression);
1789
+ if (aborts(body[0])) {
1790
+ // Only the first branch body could have a break (at the last statement)
1791
+ let first = body[0];
1792
+ if (is_break(first.body[first.body.length - 1], compressor)) {
1793
+ first.body.pop();
1794
+ }
1795
+ return make_node(AST_If, self, {
1796
+ condition: make_node(AST_Binary, self, {
1797
+ operator: "===",
1798
+ left: self.expression,
1799
+ right: branch.expression,
1800
+ }),
1801
+ body: make_node(AST_BlockStatement, branch, {
1802
+ body: branch.body
1803
+ }),
1804
+ alternative: make_node(AST_BlockStatement, default_or_exact, {
1805
+ body: [].concat(
1806
+ exact_exp || [],
1807
+ default_or_exact.body
1808
+ )
1809
+ })
1645
1810
  }).optimize(compressor);
1646
1811
  }
1812
+ let operator = "===";
1813
+ let consequent = make_node(AST_BlockStatement, branch, {
1814
+ body: branch.body,
1815
+ });
1816
+ let always = make_node(AST_BlockStatement, default_or_exact, {
1817
+ body: [].concat(
1818
+ exact_exp || [],
1819
+ default_or_exact.body
1820
+ )
1821
+ });
1822
+ if (body[0] === default_or_exact) {
1823
+ operator = "!==";
1824
+ let tmp = always;
1825
+ always = consequent;
1826
+ consequent = tmp;
1827
+ }
1828
+ return make_node(AST_BlockStatement, self, {
1829
+ body: [
1830
+ make_node(AST_If, self, {
1831
+ condition: make_node(AST_Binary, self, {
1832
+ operator: operator,
1833
+ left: self.expression,
1834
+ right: branch.expression,
1835
+ }),
1836
+ body: consequent,
1837
+ alternative: null
1838
+ })
1839
+ ].concat(always)
1840
+ }).optimize(compressor);
1647
1841
  }
1648
1842
  return self;
1649
1843
 
@@ -1654,6 +1848,50 @@ def_optimize(AST_Switch, function(self, compressor) {
1654
1848
  trim_unreachable_code(compressor, branch, decl);
1655
1849
  }
1656
1850
  }
1851
+ function branches_equivalent(branch, prev, insertBreak) {
1852
+ let bbody = branch.body;
1853
+ let pbody = prev.body;
1854
+ if (insertBreak) {
1855
+ bbody = bbody.concat(make_node(AST_Break));
1856
+ }
1857
+ if (bbody.length !== pbody.length) return false;
1858
+ let bblock = make_node(AST_BlockStatement, branch, { body: bbody });
1859
+ let pblock = make_node(AST_BlockStatement, prev, { body: pbody });
1860
+ return bblock.equivalent_to(pblock);
1861
+ }
1862
+ function statement(expression) {
1863
+ return make_node(AST_SimpleStatement, expression, {
1864
+ body: expression
1865
+ });
1866
+ }
1867
+ function has_nested_break(root) {
1868
+ let has_break = false;
1869
+ let tw = new TreeWalker(node => {
1870
+ if (has_break) return true;
1871
+ if (node instanceof AST_Lambda) return true;
1872
+ if (node instanceof AST_SimpleStatement) return true;
1873
+ if (!is_break(node, tw)) return;
1874
+ let parent = tw.parent();
1875
+ if (
1876
+ parent instanceof AST_SwitchBranch
1877
+ && parent.body[parent.body.length - 1] === node
1878
+ ) {
1879
+ return;
1880
+ }
1881
+ has_break = true;
1882
+ });
1883
+ root.walk(tw);
1884
+ return has_break;
1885
+ }
1886
+ function is_break(node, stack) {
1887
+ return node instanceof AST_Break
1888
+ && stack.loopcontrol_target(node) === self;
1889
+ }
1890
+ function is_inert_body(branch) {
1891
+ return !aborts(branch) && !make_node(AST_BlockStatement, branch, {
1892
+ body: branch.body
1893
+ }).has_side_effects(compressor);
1894
+ }
1657
1895
  });
1658
1896
 
1659
1897
  def_optimize(AST_Try, function(self, compressor) {
@@ -1820,6 +2058,14 @@ def_optimize(AST_Call, function(self, compressor) {
1820
2058
  }
1821
2059
 
1822
2060
  if (compressor.option("unsafe")) {
2061
+ if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
2062
+ const [argument] = self.args;
2063
+ if (argument instanceof AST_Array) {
2064
+ return make_node(AST_Array, argument, {
2065
+ elements: argument.elements
2066
+ }).optimize(compressor);
2067
+ }
2068
+ }
1823
2069
  if (is_undeclared_ref(exp)) switch (exp.name) {
1824
2070
  case "Array":
1825
2071
  if (self.args.length != 1) {
@@ -2025,6 +2271,7 @@ def_optimize(AST_Call, function(self, compressor) {
2025
2271
  argnames: [],
2026
2272
  body: []
2027
2273
  }).optimize(compressor);
2274
+ var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
2028
2275
  if (self.args.every((x) => x instanceof AST_String)) {
2029
2276
  // quite a corner-case, but we can handle it:
2030
2277
  // https://github.com/mishoo/UglifyJS2/issues/203
@@ -2034,14 +2281,13 @@ def_optimize(AST_Call, function(self, compressor) {
2034
2281
  return arg.value;
2035
2282
  }).join(",") + "){" + self.args[self.args.length - 1].value + "})";
2036
2283
  var ast = parse(code);
2037
- var mangle = { ie8: compressor.option("ie8") };
2284
+ var mangle = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
2038
2285
  ast.figure_out_scope(mangle);
2039
2286
  var comp = new Compressor(compressor.options, {
2040
2287
  mangle_options: compressor.mangle_options
2041
2288
  });
2042
2289
  ast = ast.transform(comp);
2043
2290
  ast.figure_out_scope(mangle);
2044
- base54.reset();
2045
2291
  ast.compute_char_frequency(mangle);
2046
2292
  ast.mangle_names(mangle);
2047
2293
  var fun;
@@ -4239,12 +4485,15 @@ function lift_key(self, compressor) {
4239
4485
  if (self.key.value == "constructor"
4240
4486
  && compressor.parent() instanceof AST_Class) return self;
4241
4487
  if (self instanceof AST_ObjectKeyVal) {
4488
+ self.quote = self.key.quote;
4242
4489
  self.key = self.key.value;
4243
4490
  } else if (self instanceof AST_ClassProperty) {
4491
+ self.quote = self.key.quote;
4244
4492
  self.key = make_node(AST_SymbolClassProperty, self.key, {
4245
4493
  name: self.key.value
4246
4494
  });
4247
4495
  } else {
4496
+ self.quote = self.key.quote;
4248
4497
  self.key = make_node(AST_SymbolMethod, self.key, {
4249
4498
  name: self.key.value
4250
4499
  });
@@ -321,7 +321,9 @@ export function tighten_body(statements, compressor) {
321
321
  // Replace variable with assignment when found
322
322
  if (can_replace
323
323
  && !(node instanceof AST_SymbolDeclaration)
324
- && lhs.equivalent_to(node)) {
324
+ && lhs.equivalent_to(node)
325
+ && !shadows(node.scope, lvalues)
326
+ ) {
325
327
  if (stop_if_hit) {
326
328
  abort = true;
327
329
  return node;
@@ -369,7 +371,7 @@ export function tighten_body(statements, compressor) {
369
371
  || node instanceof AST_PropAccess
370
372
  && (side_effects || node.expression.may_throw_on_access(compressor))
371
373
  || node instanceof AST_SymbolRef
372
- && (lvalues.get(node.name) || side_effects && may_modify(node))
374
+ && ((lvalues.has(node.name) && lvalues.get(node.name).modified) || side_effects && may_modify(node))
373
375
  || node instanceof AST_VarDef && node.value
374
376
  && (lvalues.has(node.name.name) || side_effects && may_modify(node.name))
375
377
  || (sym = is_lhs(node.left, node))
@@ -442,8 +444,9 @@ export function tighten_body(statements, compressor) {
442
444
  // Locate symbols which may execute code outside of scanning range
443
445
  var lvalues = get_lvalues(candidate);
444
446
  var lhs_local = is_lhs_local(lhs);
445
- if (lhs instanceof AST_SymbolRef)
446
- lvalues.set(lhs.name, false);
447
+ if (lhs instanceof AST_SymbolRef) {
448
+ lvalues.set(lhs.name, { def: lhs.definition(), modified: false });
449
+ }
447
450
  var side_effects = value_has_side_effects(candidate);
448
451
  var replace_all = replace_all_symbols();
449
452
  var may_throw = candidate.may_throw(compressor);
@@ -509,8 +512,9 @@ export function tighten_body(statements, compressor) {
509
512
  return false;
510
513
  let cur_scope = def.scope;
511
514
  while (cur_scope && cur_scope !== scope) {
512
- if (cur_scope.variables.has(def.name))
515
+ if (cur_scope.variables.has(def.name)) {
513
516
  return true;
517
+ }
514
518
  cur_scope = cur_scope.parent_scope;
515
519
  }
516
520
  return false;
@@ -791,8 +795,14 @@ export function tighten_body(statements, compressor) {
791
795
  var sym = node;
792
796
  while (sym instanceof AST_PropAccess)
793
797
  sym = sym.expression;
794
- if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
795
- lvalues.set(sym.name, lvalues.get(sym.name) || is_modified(compressor, tw, node, node, 0));
798
+ if (sym instanceof AST_SymbolRef) {
799
+ const prev = lvalues.get(sym.name);
800
+ if (!prev || !prev.modified) {
801
+ lvalues.set(sym.name, {
802
+ def: sym.definition(),
803
+ modified: is_modified(compressor, tw, node, node, 0)
804
+ });
805
+ }
796
806
  }
797
807
  });
798
808
  get_rvalue(expr).walk(tw);
@@ -904,6 +914,18 @@ export function tighten_body(statements, compressor) {
904
914
  }
905
915
  return false;
906
916
  }
917
+
918
+ function shadows(newScope, lvalues) {
919
+ for (const {def} of lvalues.values()) {
920
+ let current = newScope;
921
+ while (current && current !== def.scope) {
922
+ let nested_def = current.variables.get(def.name);
923
+ if (nested_def && nested_def !== def) return true;
924
+ current = current.parent_scope;
925
+ }
926
+ }
927
+ return false;
928
+ }
907
929
  }
908
930
 
909
931
  function eliminate_spurious_blocks(statements) {
package/lib/minify.js CHANGED
@@ -11,10 +11,10 @@ import { AST_Toplevel, AST_Node } from "./ast.js";
11
11
  import { parse } from "./parse.js";
12
12
  import { OutputStream } from "./output.js";
13
13
  import { Compressor } from "./compress/index.js";
14
- import { base54 } from "./scope.js";
15
14
  import { SourceMap } from "./sourcemap.js";
16
15
  import {
17
16
  mangle_properties,
17
+ mangle_private_properties,
18
18
  reserve_quoted_keys,
19
19
  } from "./propmangle.js";
20
20
 
@@ -203,9 +203,9 @@ async function minify(files, options) {
203
203
  if (options.mangle) toplevel.figure_out_scope(options.mangle);
204
204
  if (timings) timings.mangle = Date.now();
205
205
  if (options.mangle) {
206
- base54.reset();
207
206
  toplevel.compute_char_frequency(options.mangle);
208
207
  toplevel.mangle_names(options.mangle);
208
+ toplevel = mangle_private_properties(toplevel, options.mangle);
209
209
  }
210
210
  if (timings) timings.properties = Date.now();
211
211
  if (options.mangle && options.mangle.properties) {
package/lib/output.js CHANGED
@@ -349,9 +349,11 @@ function OutputStream(options) {
349
349
  var do_add_mapping = mappings ? function() {
350
350
  mappings.forEach(function(mapping) {
351
351
  try {
352
- let name = !mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name;
353
- if (name instanceof AST_Symbol) {
354
- name = name.name;
352
+ let { name, token } = mapping;
353
+ if (token.type == "name" || token.type === "privatename") {
354
+ name = token.value;
355
+ } else if (name instanceof AST_Symbol) {
356
+ name = token.type === "string" ? token.value : name.name;
355
357
  }
356
358
  options.source_map.add(
357
359
  mapping.token.file,
@@ -1824,6 +1826,7 @@ function OutputStream(options) {
1824
1826
 
1825
1827
  if (self.optional) output.print("?");
1826
1828
  output.print(".#");
1829
+ output.add_mapping(self.end);
1827
1830
  output.print_name(prop);
1828
1831
  });
1829
1832
  DEFPRINT(AST_Sub, function(self, output) {
@@ -2277,8 +2280,10 @@ function OutputStream(options) {
2277
2280
  DEFMAP([
2278
2281
  AST_ObjectGetter,
2279
2282
  AST_ObjectSetter,
2283
+ AST_PrivateGetter,
2284
+ AST_PrivateSetter,
2280
2285
  ], function(output) {
2281
- output.add_mapping(this.start, this.key.name);
2286
+ output.add_mapping(this.key.end, this.key.name);
2282
2287
  });
2283
2288
 
2284
2289
  DEFMAP([ AST_ObjectProperty ], function(output) {