From 8ecbe14207156c2fc34b29c63bb52f8ba303afce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Wed, 22 Jan 2020 10:43:59 +0900 Subject: [PATCH] hygiene: handle use-use conflict (#599) Note that as we don't care about emitting a clean code, the renamed output may not match it of bind-bind conflict or bind-use conflict. --- ecmascript/preset_env/package.json | 4 +- ecmascript/preset_env/src/query.js | 23 +++-- ecmascript/preset_env/src/query.src.js | 14 +++ ecmascript/transforms/src/hygiene.rs | 56 +++++++---- ecmascript/transforms/src/hygiene/ops.rs | 2 + ecmascript/transforms/src/hygiene/tests.rs | 99 +++++++++++++++++++ ecmascript/transforms/src/resolver.rs | 19 ++++ ecmascript/transforms/src/tests.rs | 2 +- .../tests/proposal_class_properties.rs | 16 +-- 9 files changed, 197 insertions(+), 38 deletions(-) create mode 100644 ecmascript/preset_env/src/query.src.js diff --git a/ecmascript/preset_env/package.json b/ecmascript/preset_env/package.json index ea107de5c9d..e24af112fd7 100644 --- a/ecmascript/preset_env/package.json +++ b/ecmascript/preset_env/package.json @@ -2,5 +2,7 @@ "devDependencies": { "browserslist": "=4.7.3" }, - "browserslist": ["defaults"] + "browserslist": [ + "defaults" + ] } diff --git a/ecmascript/preset_env/src/query.js b/ecmascript/preset_env/src/query.js index c2c3f7d598c..55f0496d3c7 100644 --- a/ecmascript/preset_env/src/query.js +++ b/ecmascript/preset_env/src/query.js @@ -1,13 +1,20 @@ -const browserslist = require('browserslist') -let target = JSON.parse(process.argv[1]); +"use strict"; +var browserslist = require('browserslist'); + +var target = JSON.parse(process.argv[1]); target = target.browsers ? target.browsers : target; -target = Array.isArray(target) ? target : (typeof target === 'string' ? [target] : Object.keys(target).map((k) => `${k} ${target[k]}`)); -target = target.filter(v => !v.startsWith('esmodules') && !!v); +target = Array.isArray(target) ? target : typeof target === 'string' ? [target] : Object.keys(target).map(function (k) { + return k + " " + target[k]; +}); +target = target.filter(function (v) { + return !v.startsWith('esmodules') && !!v; +}); // console.log('Target: ', target); -// console.log('Target: ', target); - -const browsers = browserslist(target && target.length ? target : undefined, { - mobileToDesktop: true, +var browsers = browserslist(target && target.length ? target : undefined, { + mobileToDesktop: true +}); +browsers = browsers.filter(function (v) { + return !v.includes("TP"); }); console.log(JSON.stringify(browsers)); \ No newline at end of file diff --git a/ecmascript/preset_env/src/query.src.js b/ecmascript/preset_env/src/query.src.js new file mode 100644 index 00000000000..bbf54ecbf03 --- /dev/null +++ b/ecmascript/preset_env/src/query.src.js @@ -0,0 +1,14 @@ +const browserslist = require('browserslist') +let target = JSON.parse(process.argv[1]); + +target = target.browsers ? target.browsers : target; +target = Array.isArray(target) ? target : (typeof target === 'string' ? [target] : Object.keys(target).map((k) => `${k} ${target[k]}`)); +target = target.filter(v => !v.startsWith('esmodules') && !!v); + +// console.log('Target: ', target); + +let browsers = browserslist(target && target.length ? target : undefined, { + mobileToDesktop: true, +}); +browsers = browsers.filter((v) => !v.includes("TP")) +console.log(JSON.stringify(browsers)); \ No newline at end of file diff --git a/ecmascript/transforms/src/hygiene.rs b/ecmascript/transforms/src/hygiene.rs index 3db798bafd1..4074eb2fed1 100644 --- a/ecmascript/transforms/src/hygiene.rs +++ b/ecmascript/transforms/src/hygiene.rs @@ -55,12 +55,20 @@ impl<'a> Hygiene<'a> { fn add_used_ref(&mut self, ident: Ident) { let ctxt = ident.span.ctxt(); + self.current + .declared_symbols + .borrow_mut() + .entry(ident.sym.clone()) + .or_insert_with(Vec::new) + .push(ctxt); + // We rename declaration instead of usage let conflicts = self.current.conflicts(ident.sym.clone(), ctxt); if cfg!(debug_assertions) && LOG && !conflicts.is_empty() { eprintln!("Renaming from usage"); } + for cx in conflicts { self.rename(ident.sym.clone(), cx) } @@ -111,7 +119,7 @@ impl<'a> Hygiene<'a> { ctxt ); old.retain(|c| *c != ctxt); - debug_assert!(old.is_empty() || old.len() == 1); + // debug_assert!(old.is_empty() || old.len() == 1); let new = declared_symbols.entry(renamed.clone()).or_default(); new.push(ctxt); @@ -257,6 +265,11 @@ impl<'a> Fold for Hygiene<'a> { impl<'a> Fold for Hygiene<'a> { /// Invoked for `IdetifierRefrence` / `BindingIdentifier` fn fold(&mut self, i: Ident) -> Ident { + // Special case + if i.sym == js_word!("arguments") { + return i; + } + match self.ident_type { IdentType::Binding => self.add_declared_ref(i.clone()), IdentType::Ref => { @@ -400,6 +413,7 @@ impl<'a> Scope<'a> { } ctxts.retain(|c| *c != ctxt); + ctxts.dedup(); ctxts } @@ -443,6 +457,27 @@ impl<'a> Scope<'a> { } } +impl Fold for Hygiene<'_> { + fn fold(&mut self, c: Constructor) -> Constructor { + let old = self.ident_type; + self.ident_type = IdentType::Binding; + let params = c.params.fold_with(self); + self.ident_type = old; + + let body = c.body.map(|bs| bs.fold_children(self)); + let key = c.key.fold_with(self); + + let c = Constructor { + params, + body, + key, + ..c + }; + + self.apply_ops(c) + } +} + #[macro_export] macro_rules! track_ident { ($T:tt) => { @@ -480,25 +515,6 @@ macro_rules! track_ident { } } - impl<'a> Fold for $T<'a> { - fn fold(&mut self, c: Constructor) -> Constructor { - let old = self.ident_type; - self.ident_type = IdentType::Binding; - let params = c.params.fold_with(self); - self.ident_type = old; - - let body = c.body.fold_with(self); - let key = c.key.fold_with(self); - - Constructor { - params, - body, - key, - ..c - } - } - } - impl<'a> Fold for $T<'a> { fn fold(&mut self, f: SetterProp) -> SetterProp { let old = self.ident_type; diff --git a/ecmascript/transforms/src/hygiene/ops.rs b/ecmascript/transforms/src/hygiene/ops.rs index 5fa95622a6e..e4a98780f5d 100644 --- a/ecmascript/transforms/src/hygiene/ops.rs +++ b/ecmascript/transforms/src/hygiene/ops.rs @@ -45,6 +45,7 @@ impl<'a> Fold> for Operator<'a> { declare, }), })) => { + let class = class.fold_with(self); let orig_ident = ident.clone(); match self.rename_ident(ident) { Ok(ident) => { @@ -76,6 +77,7 @@ impl<'a> Fold> for Operator<'a> { declare, }), })) => { + let function = function.fold_with(self); let orig_ident = ident.clone(); match self.rename_ident(ident) { Ok(ident) => { diff --git a/ecmascript/transforms/src/hygiene/tests.rs b/ecmascript/transforms/src/hygiene/tests.rs index 3038048f92f..c0a8c7163d5 100644 --- a/ecmascript/transforms/src/hygiene/tests.rs +++ b/ecmascript/transforms/src/hygiene/tests.rs @@ -1,4 +1,5 @@ use super::*; +use crate::tests::HygieneVisualizer; use std::collections::HashMap; use swc_common::{hygiene::*, Fold, DUMMY_SP}; use swc_ecma_parser::Syntax; @@ -71,6 +72,14 @@ where crate::tests::Tester::run(|tester| { let module = op(tester)?; + match ::std::env::var("PRINT_HYGIENE") { + Ok(ref s) if s == "1" => { + let hygiene_src = tester.print(&module.clone().fold_with(&mut HygieneVisualizer)); + println!("----- Hygiene -----\n{}", hygiene_src); + } + _ => {} + } + let module = module.fold_with(&mut hygiene()); let actual = tester.print(&module); @@ -1164,3 +1173,93 @@ fn exported_class_1() { export { Foo1 as Foo };", ); } + +#[test] +fn issue_598() { + test_module( + |tester| { + let mark1 = Mark::fresh(Mark::root()); + let mark2 = Mark::fresh(Mark::root()); + + Ok(tester + .parse_module( + "actual1.js", + "export function foo() { + console.log(i18n(_templateObject())); + console.log(i18n(_templateObject())); +}", + )? + .fold_with(&mut OnceMarker::new(&[( + "_templateObject", + &[mark1, mark2], + )]))) + }, + "export function foo() { + console.log(i18n(_templateObject1())); + console.log(i18n(_templateObject())); +}", + ); +} + +#[test] +fn issue_598_2() { + test_module( + |tester| { + let mark1 = Mark::fresh(Mark::root()); + let mark2 = Mark::fresh(Mark::root()); + let mark3 = Mark::fresh(Mark::root()); + + Ok(tester + .parse_module( + "actual1.js", + "export function foo() { + console.log(i18n(_templateObject())); + console.log(i18n(_templateObject())); + console.log(i18n(_templateObject())); +}", + )? + .fold_with(&mut OnceMarker::new(&[( + "_templateObject", + &[mark1, mark2, mark3], + )]))) + }, + "export function foo() { + console.log(i18n(_templateObject1())); + console.log(i18n(_templateObject2())); + console.log(i18n(_templateObject())); +}", + ); +} + +#[test] +fn issue_598_3() { + test_module( + |tester| { + let mark1 = Mark::fresh(Mark::root()); + let mark2 = Mark::fresh(Mark::root()); + let mark3 = Mark::fresh(Mark::root()); + let mark4 = Mark::fresh(Mark::root()); + + Ok(tester + .parse_module( + "actual1.js", + "export function foo() { + console.log(i18n(_templateObject())); + console.log(i18n(_templateObject())); + console.log(i18n(_templateObject())); + console.log(i18n(_templateObject())); +}", + )? + .fold_with(&mut OnceMarker::new(&[( + "_templateObject", + &[mark1, mark2, mark3, mark4], + )]))) + }, + "export function foo() { + console.log(i18n(_templateObject1())); + console.log(i18n(_templateObject2())); + console.log(i18n(_templateObject3())); + console.log(i18n(_templateObject())); +}", + ); +} diff --git a/ecmascript/transforms/src/resolver.rs b/ecmascript/transforms/src/resolver.rs index b84001e0c47..6131e2f8908 100644 --- a/ecmascript/transforms/src/resolver.rs +++ b/ecmascript/transforms/src/resolver.rs @@ -491,3 +491,22 @@ impl Fold for Hoister<'_, '_> { node } } + +impl Fold for Resolver<'_> { + fn fold(&mut self, c: Constructor) -> Constructor { + let old = self.ident_type; + self.ident_type = IdentType::Binding; + let params = c.params.fold_with(self); + self.ident_type = old; + + let body = c.body.fold_with(self); + let key = c.key.fold_with(self); + + Constructor { + params, + body, + key, + ..c + } + } +} diff --git a/ecmascript/transforms/src/tests.rs b/ecmascript/transforms/src/tests.rs index 36161505c90..a4ef7b101eb 100644 --- a/ecmascript/transforms/src/tests.rs +++ b/ecmascript/transforms/src/tests.rs @@ -408,7 +408,7 @@ impl Fold for Normalizer { } } -struct HygieneVisualizer; +pub(crate) struct HygieneVisualizer; impl Fold for HygieneVisualizer { fn fold(&mut self, ident: Ident) -> Ident { Ident { diff --git a/ecmascript/transforms/tests/proposal_class_properties.rs b/ecmascript/transforms/tests/proposal_class_properties.rs index dcf67f7b8bd..955b6e665fe 100644 --- a/ecmascript/transforms/tests/proposal_class_properties.rs +++ b/ecmascript/transforms/tests/proposal_class_properties.rs @@ -3490,7 +3490,7 @@ var _myAsyncMethod1 = new WeakMap(); class MyClass3{ constructor(){ - _myAsyncMethod2.set(this, { + _myAsyncMethod.set(this, { writable: true, value: (function() { var _ref = _asyncToGenerator((function*() { @@ -3503,7 +3503,7 @@ class MyClass3{ }); } } -var _myAsyncMethod2 = new WeakMap(); +var _myAsyncMethod = new WeakMap(); export { MyClass3 as default } "# @@ -4059,8 +4059,8 @@ new SuperClass(); // ensure ComputedKey Method is still transformed class ComputedMethod extends Obj { constructor() { - var _temp; - var tmp = (_temp = super(), _defineProperty(this, "field", 1), _temp); + var _temp1; + var tmp = (_temp1 = super(), _defineProperty(this, "field", 1), _temp1); class B extends Obj { [tmp]() {} @@ -4082,9 +4082,9 @@ new ComputedMethod(); // ensure ComputedKey Field is still transformed class ComputedField extends Obj { constructor() { - var _temp; + var _temp2; - var _ref = (_temp = super(), _defineProperty(this, "field", 1), _temp); + var _ref = (_temp2 = super(), _defineProperty(this, "field", 1), _temp2); class B extends Obj { constructor() { @@ -4651,8 +4651,8 @@ export class Foo extends Bar { "#, r#" export class Foo extends Bar { - constructor(...args) { - super(...args); + constructor(...args1) { + super(...args1); _defineProperty(this, "test", args); }