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.
This commit is contained in:
강동윤 2020-01-22 10:43:59 +09:00 committed by GitHub
parent 08ce8d0293
commit 8ecbe14207
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 197 additions and 38 deletions

View File

@ -2,5 +2,7 @@
"devDependencies": {
"browserslist": "=4.7.3"
},
"browserslist": ["defaults"]
"browserslist": [
"defaults"
]
}

View File

@ -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));

View File

@ -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));

View File

@ -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<FnDecl> for Hygiene<'a> {
impl<'a> Fold<Ident> 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<Constructor> 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<Constructor> 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<SetterProp> for $T<'a> {
fn fold(&mut self, f: SetterProp) -> SetterProp {
let old = self.ident_type;

View File

@ -45,6 +45,7 @@ impl<'a> Fold<Vec<ModuleItem>> 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<Vec<ModuleItem>> for Operator<'a> {
declare,
}),
})) => {
let function = function.fold_with(self);
let orig_ident = ident.clone();
match self.rename_ident(ident) {
Ok(ident) => {

View File

@ -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()));
}",
);
}

View File

@ -491,3 +491,22 @@ impl Fold<Function> for Hoister<'_, '_> {
node
}
}
impl Fold<Constructor> 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
}
}
}

View File

@ -408,7 +408,7 @@ impl Fold<PatOrExpr> for Normalizer {
}
}
struct HygieneVisualizer;
pub(crate) struct HygieneVisualizer;
impl Fold<Ident> for HygieneVisualizer {
fn fold(&mut self, ident: Ident) -> Ident {
Ident {

View File

@ -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);
}