fix(es/compat): Handle private names from class properties pass (#8090)

**Related issue:**

 - Closes #7561
 - https://github.com/vercel/next.js/issues/56612
This commit is contained in:
Donny/강동윤 2023-11-03 07:18:17 +09:00 committed by GitHub
parent 407aa63cea
commit 83a5a0c612
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 2723 additions and 1103 deletions

1
Cargo.lock generated
View File

@ -4316,6 +4316,7 @@ dependencies = [
"swc_atoms",
"swc_common",
"swc_ecma_ast",
"swc_ecma_compat_es2022",
"swc_ecma_parser",
"swc_ecma_transforms_base",
"swc_ecma_transforms_testing",

View File

@ -223,8 +223,10 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> {
set_public_fields: assumptions.set_public_class_fields,
no_document_all: assumptions.no_document_all,
static_blocks_mark: Mark::new(),
pure_getter: assumptions.pure_getters,
}
}
},
self.unresolved_mark
),
should_enable(self.target, EsVersion::Es2022)
),

View File

@ -0,0 +1,19 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false
},
"target": "es2019",
"loose": false,
"minify": {
"compress": false,
"mangle": false
}
},
"module": {
"type": "commonjs"
},
"minify": false,
"isModule": false
}

View File

@ -0,0 +1,7 @@
class Foo {
#x;
test() {
this?.y.#x
}
}

View File

@ -0,0 +1,15 @@
var _class_private_field_get = require("@swc/helpers/_/_class_private_field_get");
var _class_private_field_init = require("@swc/helpers/_/_class_private_field_init");
var _x = /*#__PURE__*/ new WeakMap();
class Foo {
test() {
var _this, _this_y;
(_this = this) === null || _this === void 0 ? void 0 : _class_private_field_get._(_this_y = _this.y, _x);
}
constructor(){
_class_private_field_init._(this, _x, {
writable: true,
value: void 0
});
}
}

View File

@ -0,0 +1,19 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false
},
"target": "es2021",
"loose": false,
"minify": {
"compress": false,
"mangle": false
}
},
"module": {
"type": "commonjs"
},
"minify": false,
"isModule": false
}

View File

@ -0,0 +1,7 @@
class Foo {
#x;
test() {
this?.y.#x
}
}

View File

@ -0,0 +1,15 @@
var _class_private_field_get = require("@swc/helpers/_/_class_private_field_get");
var _class_private_field_init = require("@swc/helpers/_/_class_private_field_init");
var _x = /*#__PURE__*/ new WeakMap();
class Foo {
test() {
var _this, _this_y;
(_this = this) === null || _this === void 0 ? void 0 : _class_private_field_get._(_this_y = _this.y, _x);
}
constructor(){
_class_private_field_init._(this, _x, {
writable: true,
value: void 0
});
}
}

View File

@ -0,0 +1,19 @@
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"jsx": false
},
"target": "es2022",
"loose": false,
"minify": {
"compress": false,
"mangle": false
}
},
"module": {
"type": "commonjs"
},
"minify": false,
"isModule": false
}

View File

@ -0,0 +1,7 @@
class Foo {
#x;
test() {
this?.y.#x
}
}

View File

@ -0,0 +1,6 @@
class Foo {
#x;
test() {
this?.y.#x;
}
}

View File

@ -5,10 +5,9 @@ import { _ as _class_private_field_set } from "@swc/helpers/_/_class_private_fie
var _fieldFunc = /*#__PURE__*/ new WeakMap(), _fieldFunc2 = /*#__PURE__*/ new WeakMap();
class A {
test() {
var _class_private_field_get1;
var _this_getInstance;
var _this, _this1, _ref, _this_getInstance;
_class_private_field_get(this, _fieldFunc).call(this);
(_class_private_field_get1 = _class_private_field_get(this, _fieldFunc)) === null || _class_private_field_get1 === void 0 ? void 0 : _class_private_field_get1.call(this);
(_this = _class_private_field_get(_ref = _this1 = this, _fieldFunc)) === null || _this === void 0 ? void 0 : _this.call(_this1);
const func = _class_private_field_get(this, _fieldFunc);
func();
new (_class_private_field_get(this, _fieldFunc))();

View File

@ -2,9 +2,9 @@
import { _ as _class_static_private_field_spec_get } from "@swc/helpers/_/_class_static_private_field_spec_get";
class A {
test() {
var _class_static_private_field_spec_get1;
var _this;
_class_static_private_field_spec_get(A, A, _fieldFunc).call(A);
(_class_static_private_field_spec_get1 = _class_static_private_field_spec_get(A, A, _fieldFunc)) === null || _class_static_private_field_spec_get1 === void 0 ? void 0 : _class_static_private_field_spec_get1.call(A);
(_this = _class_static_private_field_spec_get(A, A, _fieldFunc)) === null || _this === void 0 ? void 0 : _this.call(A);
const func = _class_static_private_field_spec_get(A, A, _fieldFunc);
func();
new (_class_static_private_field_spec_get(A, A, _fieldFunc))();

View File

@ -17,6 +17,7 @@ serde = { version = "1.0.188", features = ["derive"] }
swc_atoms = { version = "0.6.0", path = "../swc_atoms" }
swc_common = { version = "0.33.2", path = "../swc_common" }
swc_ecma_ast = { version = "0.110.2", path = "../swc_ecma_ast" }
swc_ecma_compat_es2022 = { version = "0.1.9", path = "../swc_ecma_compat_es2022" }
swc_ecma_transforms_base = { version = "0.134.13", path = "../swc_ecma_transforms_base" }
swc_ecma_utils = { version = "0.124.11", path = "../swc_ecma_utils" }
swc_ecma_visit = { version = "0.96.2", path = "../swc_ecma_visit" }

View File

@ -1,26 +1,16 @@
use std::mem;
use serde::Deserialize;
use swc_common::{util::take::Take, Mark, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::{
alias_ident_for, prepend_stmt, quote_ident, undefined, ExprFactory, StmtLike,
};
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
use swc_common::Mark;
use swc_ecma_compat_es2022::optional_chaining_impl::optional_chaining_impl;
use swc_ecma_visit::{as_folder, Fold, VisitMut};
pub fn optional_chaining(c: Config, unresolved_mark: Mark) -> impl Fold + VisitMut {
as_folder(OptChaining {
c,
unresolved: SyntaxContext::empty().apply_mark(unresolved_mark),
..Default::default()
})
}
#[derive(Default)]
struct OptChaining {
vars: Vec<VarDeclarator>,
unresolved: SyntaxContext,
c: Config,
as_folder(optional_chaining_impl(
swc_ecma_compat_es2022::optional_chaining_impl::Config {
no_document_all: c.no_document_all,
pure_getter: c.pure_getter,
},
unresolved_mark,
))
}
#[derive(Debug, Clone, Copy, Default, Deserialize)]
@ -31,435 +21,3 @@ pub struct Config {
#[serde(default)]
pub pure_getter: bool,
}
impl VisitMut for OptChaining {
noop_visit_mut_type!();
fn visit_mut_block_stmt_or_expr(&mut self, expr: &mut BlockStmtOrExpr) {
if let BlockStmtOrExpr::Expr(e) = expr {
let mut stmt = BlockStmt {
span: DUMMY_SP,
stmts: vec![Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(e.take()),
})],
};
stmt.visit_mut_with(self);
// If there are optional chains in this expression, then the visitor will have
// injected an VarDecl statement and we need to transform into a
// block. If not, then we can keep the expression.
match &mut stmt.stmts[..] {
[Stmt::Return(ReturnStmt { arg: Some(e), .. })] => {
*expr = BlockStmtOrExpr::Expr(e.take())
}
_ => *expr = BlockStmtOrExpr::BlockStmt(stmt),
}
} else {
expr.visit_mut_children_with(self);
}
}
fn visit_mut_expr(&mut self, e: &mut Expr) {
match e {
// foo?.bar -> foo == null ? void 0 : foo.bar
Expr::OptChain(v) => {
let data = self.gather(v.take(), vec![]);
*e = self.construct(data, false);
}
Expr::Unary(UnaryExpr {
arg,
op: op!("delete"),
..
}) => {
match &mut **arg {
// delete foo?.bar -> foo == null ? true : delete foo.bar
Expr::OptChain(v) => {
let data = self.gather(v.take(), vec![]);
*e = self.construct(data, true);
}
_ => e.visit_mut_children_with(self),
}
}
e => e.visit_mut_children_with(self),
}
}
fn visit_mut_pat(&mut self, n: &mut Pat) {
// The default initializer of an assignment pattern must not leak the memo
// variable into the enclosing scope.
// function(a, b = a?.b) {} -> function(a, b = (() => var _a; …)()) {}
let Pat::Assign(a) = n else {
n.visit_mut_children_with(self);
return;
};
let uninit = self.vars.take();
a.right.visit_mut_with(self);
// If we found an optional chain, we need to transform into an arrow IIFE to
// capture the memo variable.
if !self.vars.is_empty() {
let stmts = vec![
Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
declare: false,
kind: VarDeclKind::Var,
decls: mem::take(&mut self.vars),
}))),
Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(a.right.take()),
}),
];
a.right = Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![],
body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts,
})),
is_async: false,
is_generator: false,
type_params: Default::default(),
return_type: Default::default(),
})
.as_callee(),
args: vec![],
type_args: Default::default(),
}));
}
self.vars = uninit;
a.left.visit_mut_with(self);
}
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
self.visit_mut_stmt_like(n);
}
fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
self.visit_mut_stmt_like(n);
}
}
#[derive(Debug, Clone)]
enum Memo {
Cache(Ident),
Raw(Box<Expr>),
}
impl Memo {
fn into_expr(self) -> Expr {
match self {
Memo::Cache(i) => Expr::Ident(i),
Memo::Raw(e) => *e,
}
}
}
#[derive(Debug)]
enum Gathering {
Call(CallExpr),
Member(MemberExpr),
OptCall(CallExpr, Memo),
OptMember(MemberExpr, Memo),
}
impl OptChaining {
/// Transforms the left-nested structure into a flat vec. The obj/callee
/// of every node in the chain will be Invalid, to be replaced with a
/// constructed node in the construct step.
/// The top member/call will be first, and the deepest obj/callee will be
/// last.
fn gather(
&mut self,
v: OptChainExpr,
mut chain: Vec<Gathering>,
) -> (Expr, usize, Vec<Gathering>) {
let mut current = v;
let mut count = 0;
loop {
let OptChainExpr {
optional, mut base, ..
} = current;
if optional {
count += 1;
}
let next;
match &mut *base {
OptChainBase::Member(m) => {
next = m.obj.take();
m.prop.visit_mut_with(self);
chain.push(if optional {
Gathering::OptMember(m.take(), self.memoize(&next, false))
} else {
Gathering::Member(m.take())
});
}
OptChainBase::Call(c) => {
next = c.callee.take();
c.args.visit_mut_with(self);
// I don't know why c is an OptCall instead of a CallExpr.
chain.push(if optional {
Gathering::OptCall(c.take().into(), self.memoize(&next, true))
} else {
Gathering::Call(c.take().into())
});
}
}
match *next {
Expr::OptChain(next) => {
current = next;
}
mut base => {
base.visit_mut_children_with(self);
return (base, count, chain);
}
}
}
}
/// Constructs a rightward nested conditional expression out of our
/// flattened chain.
fn construct(&mut self, data: (Expr, usize, Vec<Gathering>), is_delete: bool) -> Expr {
let (mut current, count, chain) = data;
// Stores partially constructed CondExprs for us to assemble later on.
let mut committed_cond = Vec::with_capacity(count);
// Stores the memo used to construct an optional chain, so that it can be used
// as the this context of an optional call:
// foo?.bar?.() ->
// (_foo = foo) == null
// ? void 0
// : (_foo_bar = _foo.bar) == null
// ? void 0 : _foo_bar.call(_foo)
let mut ctx = None;
// In the first pass, we construct a "current" node and several committed
// CondExprs. The conditionals will have an invalid alt, waiting for the
// second pass to properly construct them.
// We reverse iterate so that we can construct a rightward conditional
// `(_a = a) == null ? void 0 : (_a_b = _a.b) == null ? void 0 : _a_b.c`
// instead of a leftward one
// `(_a_b = (_a = a) == null ? void 0 : _a.b) == null ? void 0 : _a_b.c`
for v in chain.into_iter().rev() {
current = match v {
Gathering::Call(mut c) => {
c.callee = current.as_callee();
ctx = None;
Expr::Call(c)
}
Gathering::Member(mut m) => {
m.obj = Box::new(current);
ctx = None;
Expr::Member(m)
}
Gathering::OptCall(mut c, memo) => {
let mut call = false;
// foo.bar?.() -> (_foo_bar == null) ? void 0 : _foo_bar.call(foo)
match &mut current {
Expr::Member(m) => {
call = true;
let this = ctx.unwrap_or_else(|| {
let this = self.memoize(&m.obj, true);
match &this {
Memo::Cache(i) => {
m.obj = Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: i.clone().into(),
right: m.obj.take(),
}));
this
}
Memo::Raw(_) => this,
}
});
c.args.insert(0, this.into_expr().as_arg());
}
Expr::SuperProp(s) => {
call = true;
c.args.insert(0, ThisExpr { span: s.obj.span }.as_arg());
}
_ => {}
}
committed_cond.push(CondExpr {
span: DUMMY_SP,
test: init_and_eq_null_or_undefined(&memo, current, self.c.no_document_all),
cons: if is_delete {
true.into()
} else {
undefined(DUMMY_SP)
},
alt: Take::dummy(),
});
c.callee = if call {
memo.into_expr()
.make_member(quote_ident!("call"))
.as_callee()
} else {
memo.into_expr().as_callee()
};
ctx = None;
Expr::Call(c)
}
Gathering::OptMember(mut m, memo) => {
committed_cond.push(CondExpr {
span: DUMMY_SP,
test: init_and_eq_null_or_undefined(&memo, current, self.c.no_document_all),
cons: if is_delete {
true.into()
} else {
undefined(DUMMY_SP)
},
alt: Take::dummy(),
});
ctx = Some(memo.clone());
m.obj = memo.into_expr().into();
Expr::Member(m)
}
};
}
// At this point, `current` is the right-most expression `_a_b.c` in `a?.b?.c`
if is_delete {
current = Expr::Unary(UnaryExpr {
span: DUMMY_SP,
op: op!("delete"),
arg: Box::new(current),
});
}
// We now need to reverse iterate the conditionals to construct out tree.
for mut cond in committed_cond.into_iter().rev() {
cond.alt = Box::new(current);
current = Expr::Cond(cond)
}
current
}
fn should_memo(&self, expr: &Expr, is_call: bool) -> bool {
fn is_simple_member(e: &Expr) -> bool {
match e {
Expr::Ident(_) => true,
Expr::SuperProp(s) if !s.prop.is_computed() => true,
Expr::Member(m) if !m.prop.is_computed() => is_simple_member(&m.obj),
_ => false,
}
}
match expr {
Expr::Ident(i) if i.span.ctxt != self.unresolved => false,
_ => {
if is_call && self.c.pure_getter {
!is_simple_member(expr)
} else {
true
}
}
}
}
fn memoize(&mut self, expr: &Expr, is_call: bool) -> Memo {
if self.should_memo(expr, is_call) {
let memo = alias_ident_for(expr, "_this");
self.vars.push(VarDeclarator {
span: DUMMY_SP,
name: memo.clone().into(),
init: None,
definite: false,
});
Memo::Cache(memo)
} else {
Memo::Raw(Box::new(expr.to_owned()))
}
}
fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
where
T: Send + Sync + StmtLike + VisitMutWith<Self>,
Vec<T>: VisitMutWith<Self>,
{
let uninit = self.vars.take();
for stmt in stmts.iter_mut() {
stmt.visit_mut_with(self);
}
if !self.vars.is_empty() {
prepend_stmt(
stmts,
T::from_stmt(
VarDecl {
span: DUMMY_SP,
declare: false,
kind: VarDeclKind::Var,
decls: mem::take(&mut self.vars),
}
.into(),
),
);
}
self.vars = uninit;
}
}
fn init_and_eq_null_or_undefined(i: &Memo, init: Expr, no_document_all: bool) -> Box<Expr> {
let lhs = match i {
Memo::Cache(i) => Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(i.clone().into()),
right: Box::new(init),
})),
Memo::Raw(e) => e.to_owned(),
};
if no_document_all {
return Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: lhs,
op: op!("=="),
right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
}));
}
let null_cmp = Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: lhs,
op: op!("==="),
right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
}));
let left_expr = match i {
Memo::Cache(i) => Box::new(i.clone().into()),
Memo::Raw(e) => e.to_owned(),
};
let void_cmp = Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: left_expr,
op: op!("==="),
right: undefined(DUMMY_SP),
}));
Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: null_cmp,
op: op!("||"),
right: void_cmp,
}))
}

View File

@ -43,12 +43,17 @@ mod used_name;
/// # Impl note
///
/// We use custom helper to handle export default class
pub fn class_properties<C: Comments>(cm: Option<C>, config: Config) -> impl Fold + VisitMut {
pub fn class_properties<C: Comments>(
cm: Option<C>,
config: Config,
unresolved_mark: Mark,
) -> impl Fold + VisitMut {
as_folder(ClassProperties {
c: config,
cm,
private: PrivateRecord::new(),
extra: ClassExtra::default(),
unresolved_mark,
})
}
@ -58,6 +63,7 @@ pub struct Config {
pub set_public_fields: bool,
pub constant_super: bool,
pub no_document_all: bool,
pub pure_getter: bool,
pub static_blocks_mark: Mark,
}
@ -68,6 +74,7 @@ impl Default for Config {
set_public_fields: false,
constant_super: false,
no_document_all: false,
pure_getter: false,
static_blocks_mark: Mark::new(),
}
}
@ -78,6 +85,7 @@ struct ClassProperties<C: Comments> {
c: Config,
cm: Option<C>,
private: PrivateRecord,
unresolved_mark: Mark,
}
#[derive(Default)]
@ -551,7 +559,12 @@ impl<C: Comments> ClassProperties<C> {
span: c_span,
mut expr,
}) if should_create_vars_for_method_names && !is_literal(&*expr) => {
vars.extend(visit_private_in_expr(&mut expr, &self.private, self.c));
vars.extend(visit_private_in_expr(
&mut expr,
&self.private,
self.c,
self.unresolved_mark,
));
expr.visit_mut_with(&mut ClassNameTdzFolder {
class_name: &class_ident,
@ -598,6 +611,7 @@ impl<C: Comments> ClassProperties<C> {
&mut key.expr,
&self.private,
self.c,
self.unresolved_mark,
));
let (ident, aliased) = if let Expr::Ident(i) = &*key.expr {
if used_key_names.contains(&i.sym) {
@ -626,7 +640,12 @@ impl<C: Comments> ClassProperties<C> {
value.visit_mut_with(&mut NewTargetInProp);
vars.extend(visit_private_in_expr(&mut value, &self.private, self.c));
vars.extend(visit_private_in_expr(
&mut value,
&self.private,
self.c,
self.unresolved_mark,
));
if prop.is_static {
if let (Some(super_class), None) = (&mut class.super_class, &super_ident) {
@ -706,7 +725,12 @@ impl<C: Comments> ClassProperties<C> {
in_pat: false,
});
}
vars.extend(visit_private_in_expr(&mut *value, &self.private, self.c));
vars.extend(visit_private_in_expr(
&mut *value,
&self.private,
self.c,
self.unresolved_mark,
));
}
prop.value.visit_with(&mut UsedNameCollector {
@ -945,6 +969,7 @@ impl<C: Comments> ClassProperties<C> {
vars: vec![],
private_access_type: Default::default(),
c: self.c,
unresolved_mark: self.unresolved_mark,
});
let mut extra_stmts = extra_inits.into_init_static(class_ident.clone());
@ -956,6 +981,7 @@ impl<C: Comments> ClassProperties<C> {
vars: vec![],
private_access_type: Default::default(),
c: self.c,
unresolved_mark: self.unresolved_mark,
});
self.private.pop();

View File

@ -9,14 +9,12 @@ use swc_common::{
};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_utils::{
alias_ident_for, alias_if_required, opt_chain_test, prepend_stmt, quote_ident, undefined,
ExprFactory,
};
use swc_ecma_utils::{alias_ident_for, alias_if_required, prepend_stmt, quote_ident, ExprFactory};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use swc_trace_macro::swc_trace;
use super::Config;
use crate::optional_chaining_impl::optional_chaining_impl;
pub(super) struct Private {
pub mark: Mark,
@ -171,6 +169,7 @@ pub(super) struct PrivateAccessVisitor<'a> {
pub private: &'a PrivateRecord,
pub private_access_type: PrivateAccessType,
pub c: Config,
pub unresolved_mark: Mark,
}
macro_rules! take_vars {
@ -213,6 +212,36 @@ impl<'a> VisitMut for PrivateAccessVisitor<'a> {
take_vars!(visit_mut_constructor, Constructor);
fn visit_mut_expr(&mut self, e: &mut Expr) {
if let Expr::OptChain(opt) = e {
let is_private_access = match &*opt.base {
OptChainBase::Member(MemberExpr {
prop: MemberProp::PrivateName(..),
..
}) => true,
OptChainBase::Call(OptCall { callee, .. }) => matches!(
&**callee,
Expr::Member(MemberExpr {
prop: MemberProp::PrivateName(..),
..
})
),
_ => false,
};
if is_private_access {
let mut v = optional_chaining_impl(
crate::optional_chaining_impl::Config {
no_document_all: self.c.no_document_all,
pure_getter: self.c.pure_getter,
},
self.unresolved_mark,
);
e.visit_mut_with(&mut v);
assert!(!e.is_opt_chain(), "optional chaining should be removed");
self.vars.extend(v.take_vars());
}
}
if self.c.private_as_properties {
if let Expr::Member(MemberExpr {
span,
@ -434,102 +463,11 @@ impl<'a> VisitMut for PrivateAccessVisitor<'a> {
}
}
Expr::OptChain(OptChainExpr {
base,
optional,
span,
}) if match &**base {
OptChainBase::Call(call) => call.callee.is_member(),
_ => false,
} =>
{
let call = match &mut **base {
OptChainBase::Call(call) => call,
_ => unreachable!(),
};
let mut callee = call.callee.take().member().unwrap();
callee.visit_mut_with(self);
call.args.visit_mut_with(self);
let (expr, this) = self.visit_mut_private_get(&mut callee, None);
if let Some(this) = this {
let args = iter::once(this.as_arg()).chain(call.args.take()).collect();
let call = OptCall {
span: *span,
callee: Box::new(
OptChainExpr {
span: *span,
optional: *optional,
base: Box::new(OptChainBase::Member(MemberExpr {
span: call.span,
obj: Box::new(expr),
prop: MemberProp::Ident(quote_ident!("call")),
})),
}
.into(),
),
args,
type_args: call.type_args.take(),
};
*e = Expr::OptChain(OptChainExpr {
span: *span,
optional: false,
base: Box::new(OptChainBase::Call(call)),
})
} else {
call.callee = Box::new(expr);
}
}
Expr::Member(member_expr) => {
member_expr.visit_mut_children_with(self);
*e = self.visit_mut_private_get(member_expr, None).0;
}
Expr::OptChain(OptChainExpr { base, span, .. })
if matches!(
&**base,
OptChainBase::Member(MemberExpr {
prop: MemberProp::PrivateName(..),
..
},)
) =>
{
let member = match &mut **base {
OptChainBase::Member(
member @ MemberExpr {
prop: MemberProp::PrivateName(..),
..
},
) => member,
_ => {
unreachable!()
}
};
member.visit_mut_children_with(self);
let (ident, aliased) = alias_if_required(&member.obj, "_ref");
if aliased {
self.vars.push(VarDeclarator {
span: DUMMY_SP,
name: ident.clone().into(),
init: None,
definite: false,
});
}
let (expr, _) = self.visit_mut_private_get(member, None);
*e = Expr::Cond(CondExpr {
span: *span,
test: Box::new(opt_chain_test(
Box::new(ident.clone().into()),
Box::new(ident.into()),
*span,
self.c.no_document_all,
)),
cons: undefined(DUMMY_SP),
alt: Box::new(expr),
})
}
_ => e.visit_mut_children_with(self),
};
}
@ -557,12 +495,14 @@ pub(super) fn visit_private_in_expr(
expr: &mut Expr,
private: &PrivateRecord,
config: Config,
unresolved_mark: Mark,
) -> Vec<VarDeclarator> {
let mut priv_visitor = PrivateAccessVisitor {
private,
vars: vec![],
private_access_type: Default::default(),
c: config,
unresolved_mark,
};
expr.visit_mut_with(&mut priv_visitor);

View File

@ -1,6 +1,6 @@
#![allow(clippy::vec_box)]
use swc_common::{chain, comments::Comments};
use swc_common::{chain, comments::Comments, Mark};
use swc_ecma_compat_common::regexp::{self, regexp};
use swc_ecma_visit::Fold;
@ -10,10 +10,11 @@ pub use self::{
};
pub mod class_properties;
pub mod optional_chaining_impl;
pub mod private_in_object;
pub mod static_blocks;
pub fn es2022<C: Comments>(cm: Option<C>, config: Config) -> impl Fold {
pub fn es2022<C: Comments>(cm: Option<C>, config: Config, unresolved_mark: Mark) -> impl Fold {
chain!(
regexp(regexp::Config {
dot_all_regex: true,
@ -25,7 +26,7 @@ pub fn es2022<C: Comments>(cm: Option<C>, config: Config) -> impl Fold {
unicode_regex: false,
}),
static_blocks(config.class_properties.static_blocks_mark),
class_properties(cm, config.class_properties),
class_properties(cm, config.class_properties, unresolved_mark),
private_in_object(),
)
}

View File

@ -0,0 +1,470 @@
use std::mem;
use swc_common::{util::take::Take, Mark, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::{
alias_ident_for, prepend_stmt, quote_ident, undefined, ExprFactory, StmtLike,
};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
/// Not a public API and may break any time. Don't use it directly.
pub fn optional_chaining_impl(c: Config, unresolved_mark: Mark) -> OptionalChaining {
OptionalChaining {
c,
unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
..Default::default()
}
}
#[derive(Default)]
pub struct OptionalChaining {
vars: Vec<VarDeclarator>,
unresolved_ctxt: SyntaxContext,
c: Config,
}
impl OptionalChaining {
pub fn take_vars(&mut self) -> Vec<VarDeclarator> {
mem::take(&mut self.vars)
}
}
/// Not a public API and may break any time. Don't use it directly.
#[derive(Debug, Clone, Copy, Default)]
pub struct Config {
pub no_document_all: bool,
pub pure_getter: bool,
}
impl VisitMut for OptionalChaining {
noop_visit_mut_type!();
fn visit_mut_block_stmt_or_expr(&mut self, expr: &mut BlockStmtOrExpr) {
if let BlockStmtOrExpr::Expr(e) = expr {
let mut stmt = BlockStmt {
span: DUMMY_SP,
stmts: vec![Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(e.take()),
})],
};
stmt.visit_mut_with(self);
// If there are optional chains in this expression, then the visitor will have
// injected an VarDecl statement and we need to transform into a
// block. If not, then we can keep the expression.
match &mut stmt.stmts[..] {
[Stmt::Return(ReturnStmt { arg: Some(e), .. })] => {
*expr = BlockStmtOrExpr::Expr(e.take())
}
_ => *expr = BlockStmtOrExpr::BlockStmt(stmt),
}
} else {
expr.visit_mut_children_with(self);
}
}
fn visit_mut_expr(&mut self, e: &mut Expr) {
match e {
// foo?.bar -> foo == null ? void 0 : foo.bar
Expr::OptChain(v) => {
let data = self.gather(v.take(), vec![]);
*e = self.construct(data, false);
}
Expr::Unary(UnaryExpr {
arg,
op: op!("delete"),
..
}) => {
match &mut **arg {
// delete foo?.bar -> foo == null ? true : delete foo.bar
Expr::OptChain(v) => {
let data = self.gather(v.take(), vec![]);
*e = self.construct(data, true);
}
_ => e.visit_mut_children_with(self),
}
}
e => e.visit_mut_children_with(self),
}
}
fn visit_mut_pat(&mut self, n: &mut Pat) {
// The default initializer of an assignment pattern must not leak the memo
// variable into the enclosing scope.
// function(a, b = a?.b) {} -> function(a, b = (() => var _a; …)()) {}
let Pat::Assign(a) = n else {
n.visit_mut_children_with(self);
return;
};
let uninit = self.vars.take();
a.right.visit_mut_with(self);
// If we found an optional chain, we need to transform into an arrow IIFE to
// capture the memo variable.
if !self.vars.is_empty() {
let stmts = vec![
Stmt::Decl(Decl::Var(Box::new(VarDecl {
span: DUMMY_SP,
declare: false,
kind: VarDeclKind::Var,
decls: mem::take(&mut self.vars),
}))),
Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(a.right.take()),
}),
];
a.right = Box::new(Expr::Call(CallExpr {
span: DUMMY_SP,
callee: Expr::Arrow(ArrowExpr {
span: DUMMY_SP,
params: vec![],
body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts,
})),
is_async: false,
is_generator: false,
type_params: Default::default(),
return_type: Default::default(),
})
.as_callee(),
args: vec![],
type_args: Default::default(),
}));
}
self.vars = uninit;
a.left.visit_mut_with(self);
}
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
self.visit_mut_stmt_like(n);
}
fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
self.visit_mut_stmt_like(n);
}
}
#[derive(Debug, Clone)]
enum Memo {
Cache(Ident),
Raw(Box<Expr>),
}
impl Memo {
fn into_expr(self) -> Expr {
match self {
Memo::Cache(i) => Expr::Ident(i),
Memo::Raw(e) => *e,
}
}
}
#[derive(Debug)]
enum Gathering {
Call(CallExpr),
Member(MemberExpr),
OptCall(CallExpr, Memo),
OptMember(MemberExpr, Memo),
}
impl OptionalChaining {
/// Transforms the left-nested structure into a flat vec. The obj/callee
/// of every node in the chain will be Invalid, to be replaced with a
/// constructed node in the construct step.
/// The top member/call will be first, and the deepest obj/callee will be
/// last.
fn gather(
&mut self,
v: OptChainExpr,
mut chain: Vec<Gathering>,
) -> (Expr, usize, Vec<Gathering>) {
let mut current = v;
let mut count = 0;
loop {
let OptChainExpr {
optional, mut base, ..
} = current;
if optional {
count += 1;
}
let next;
match &mut *base {
OptChainBase::Member(m) => {
next = m.obj.take();
m.prop.visit_mut_with(self);
chain.push(if optional {
Gathering::OptMember(m.take(), self.memoize(&next, false))
} else {
Gathering::Member(m.take())
});
}
OptChainBase::Call(c) => {
next = c.callee.take();
c.args.visit_mut_with(self);
// I don't know why c is an OptCall instead of a CallExpr.
chain.push(if optional {
Gathering::OptCall(c.take().into(), self.memoize(&next, true))
} else {
Gathering::Call(c.take().into())
});
}
}
match *next {
Expr::OptChain(next) => {
current = next;
}
mut base => {
base.visit_mut_children_with(self);
return (base, count, chain);
}
}
}
}
/// Constructs a rightward nested conditional expression out of our
/// flattened chain.
fn construct(&mut self, data: (Expr, usize, Vec<Gathering>), is_delete: bool) -> Expr {
let (mut current, count, chain) = data;
// Stores partially constructed CondExprs for us to assemble later on.
let mut committed_cond = Vec::with_capacity(count);
// Stores the memo used to construct an optional chain, so that it can be used
// as the this context of an optional call:
// foo?.bar?.() ->
// (_foo = foo) == null
// ? void 0
// : (_foo_bar = _foo.bar) == null
// ? void 0 : _foo_bar.call(_foo)
let mut ctx = None;
// In the first pass, we construct a "current" node and several committed
// CondExprs. The conditionals will have an invalid alt, waiting for the
// second pass to properly construct them.
// We reverse iterate so that we can construct a rightward conditional
// `(_a = a) == null ? void 0 : (_a_b = _a.b) == null ? void 0 : _a_b.c`
// instead of a leftward one
// `(_a_b = (_a = a) == null ? void 0 : _a.b) == null ? void 0 : _a_b.c`
for v in chain.into_iter().rev() {
current = match v {
Gathering::Call(mut c) => {
c.callee = current.as_callee();
ctx = None;
Expr::Call(c)
}
Gathering::Member(mut m) => {
m.obj = Box::new(current);
ctx = None;
Expr::Member(m)
}
Gathering::OptCall(mut c, memo) => {
let mut call = false;
// foo.bar?.() -> (_foo_bar == null) ? void 0 : _foo_bar.call(foo)
match &mut current {
Expr::Member(m) => {
call = true;
let this = ctx.unwrap_or_else(|| {
let this = self.memoize(&m.obj, true);
match &this {
Memo::Cache(i) => {
m.obj = Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: i.clone().into(),
right: m.obj.take(),
}));
this
}
Memo::Raw(_) => this,
}
});
c.args.insert(0, this.into_expr().as_arg());
}
Expr::SuperProp(s) => {
call = true;
c.args.insert(0, ThisExpr { span: s.obj.span }.as_arg());
}
_ => {}
}
committed_cond.push(CondExpr {
span: DUMMY_SP,
test: init_and_eq_null_or_undefined(&memo, current, self.c.no_document_all),
cons: if is_delete {
true.into()
} else {
undefined(DUMMY_SP)
},
alt: Take::dummy(),
});
c.callee = if call {
memo.into_expr()
.make_member(quote_ident!("call"))
.as_callee()
} else {
memo.into_expr().as_callee()
};
ctx = None;
Expr::Call(c)
}
Gathering::OptMember(mut m, memo) => {
committed_cond.push(CondExpr {
span: DUMMY_SP,
test: init_and_eq_null_or_undefined(&memo, current, self.c.no_document_all),
cons: if is_delete {
true.into()
} else {
undefined(DUMMY_SP)
},
alt: Take::dummy(),
});
ctx = Some(memo.clone());
m.obj = memo.into_expr().into();
Expr::Member(m)
}
};
}
// At this point, `current` is the right-most expression `_a_b.c` in `a?.b?.c`
if is_delete {
current = Expr::Unary(UnaryExpr {
span: DUMMY_SP,
op: op!("delete"),
arg: Box::new(current),
});
}
// We now need to reverse iterate the conditionals to construct out tree.
for mut cond in committed_cond.into_iter().rev() {
cond.alt = Box::new(current);
current = Expr::Cond(cond)
}
current
}
fn should_memo(&self, expr: &Expr, is_call: bool) -> bool {
fn is_simple_member(e: &Expr) -> bool {
match e {
Expr::This(..) => true,
Expr::Ident(_) => true,
Expr::SuperProp(s) if !s.prop.is_computed() => true,
Expr::Member(m) if !m.prop.is_computed() => is_simple_member(&m.obj),
_ => false,
}
}
match expr {
Expr::Ident(i) if i.span.ctxt != self.unresolved_ctxt => false,
_ => {
if is_call && self.c.pure_getter {
!is_simple_member(expr)
} else {
true
}
}
}
}
fn memoize(&mut self, expr: &Expr, is_call: bool) -> Memo {
if self.should_memo(expr, is_call) {
let memo = alias_ident_for(expr, "_this");
self.vars.push(VarDeclarator {
span: DUMMY_SP,
name: memo.clone().into(),
init: None,
definite: false,
});
Memo::Cache(memo)
} else {
Memo::Raw(Box::new(expr.to_owned()))
}
}
fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
where
T: Send + Sync + StmtLike + VisitMutWith<Self>,
Vec<T>: VisitMutWith<Self>,
{
let uninit = self.vars.take();
for stmt in stmts.iter_mut() {
stmt.visit_mut_with(self);
}
if !self.vars.is_empty() {
prepend_stmt(
stmts,
T::from_stmt(
VarDecl {
span: DUMMY_SP,
declare: false,
kind: VarDeclKind::Var,
decls: mem::take(&mut self.vars),
}
.into(),
),
);
}
self.vars = uninit;
}
}
fn init_and_eq_null_or_undefined(i: &Memo, init: Expr, no_document_all: bool) -> Box<Expr> {
let lhs = match i {
Memo::Cache(i) => Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(i.clone().into()),
right: Box::new(init),
})),
Memo::Raw(e) => e.to_owned(),
};
if no_document_all {
return Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: lhs,
op: op!("=="),
right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
}));
}
let null_cmp = Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: lhs,
op: op!("==="),
right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
}));
let left_expr = match i {
Memo::Cache(i) => Box::new(i.clone().into()),
Memo::Raw(e) => e.to_owned(),
};
let void_cmp = Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: left_expr,
op: op!("==="),
right: undefined(DUMMY_SP),
}));
Box::new(Expr::Bin(BinExpr {
span: DUMMY_SP,
left: null_cmp,
op: op!("||"),
right: void_cmp,
}))
}

View File

@ -9,8 +9,10 @@ struct ClassStaticBlock {
mark: Mark,
}
pub fn static_blocks(mark: Mark) -> impl Fold + VisitMut {
as_folder(ClassStaticBlock { mark })
pub fn static_blocks(static_block_mark: Mark) -> impl Fold + VisitMut {
as_folder(ClassStaticBlock {
mark: static_block_mark,
})
}
#[swc_trace]

View File

@ -147,7 +147,9 @@ where
constant_super: loose || assumptions.constant_super,
no_document_all: loose || assumptions.no_document_all,
static_blocks_mark,
}
pure_getter: loose || assumptions.pure_getters,
},
unresolved_mark
)
);
let pass = add!(pass, PrivatePropertyInObject, es2022::private_in_object());

View File

@ -37,10 +37,18 @@ fn syntax(decorators_before_export: bool) -> Syntax {
}
fn tr(t: &Tester) -> impl Fold {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
decorators(Default::default()),
class_fields_use_set(true),
class_properties(Some(t.comments.clone()), Default::default()),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
}
@ -74,7 +82,8 @@ fn simple_strip(t: &Tester, config: Config) -> impl Fold {
class_properties::Config {
set_public_fields: true,
..Default::default()
}
},
unresolved_mark
)
)
}
@ -1612,13 +1621,23 @@ expect(el).toEqual(Object.defineProperty({
// legacy_class_constructors_return_new_constructor
test_exec!(
syntax(true),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_constructors_return_new_constructor_exec,
r#"
function dec(cls){
@ -1641,13 +1660,23 @@ expect(typeof Parent.prototype.child).toBe("function");
// legacy_class_prototype_methods_numeric_props
test_exec!(
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_prototype_methods_numeric_props_exec,
r#"
function dec(target, name, descriptor) {
@ -1669,13 +1698,23 @@ test_exec!(
// I tested using typescript playground and node js
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_static_properties_mutate_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -1784,13 +1823,23 @@ expect(Example._).toBe("__8__");
// legacy_class_static_methods_string_props
test_exec!(
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_static_methods_string_props_exec,
r#"
function dec(target, name, descriptor) {
@ -1811,13 +1860,23 @@ class Example {
test_exec!(
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_prototype_properties_string_literal_properties_exec,
r#"
function dec(target, name, descriptor) {
@ -1858,13 +1917,23 @@ test_exec!(
// I tested on typescript playground
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_prototype_methods_mutate_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -1991,13 +2060,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_properties_numeric_props_exec,
r#"
function dec(target, name, descriptor){
@ -2018,13 +2097,23 @@ const inst = {
test_exec!(
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_prototype_properties_return_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -2135,13 +2224,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_properties_string_props_exec,
r#"
function dec(target, name, descriptor){
@ -2163,13 +2262,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_properties_return_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -2276,13 +2385,23 @@ expect(inst._).toBe("__8__");
// legacy_class_prototype_methods_string_props
test_exec!(
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_prototype_methods_string_props_exec,
r#"
function dec(target, name, descriptor) {
@ -2302,13 +2421,23 @@ class Example {
// legacy_class_prototype_methods_return_descriptor
test_exec!(
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_prototype_methods_return_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -2437,13 +2566,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_ordering_reverse_order_exec,
r#"
const calls = [];
@ -2481,13 +2620,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_methods_numeric_props_exec,
r#"
function dec(target, name, descriptor){
@ -2510,13 +2659,23 @@ test_exec!(
// I tested using typescript playground and node js
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_static_properties_return_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -2630,13 +2789,23 @@ test_exec!(
// below correctly.
ignore,
syntax(true),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_export_default_exec,
r#"
const calls = [];
@ -2660,13 +2829,23 @@ expect(calls).toEqual(["Foo"]);
// legacy_class_ordering_reverse_order
test_exec!(
syntax(true),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_ordering_reverse_order_exec,
r#"
const calls = [];
@ -2707,13 +2886,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(true),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_methods_mutate_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -2836,13 +3025,23 @@ expect(inst._()).toBe("__8__");
// legacy_class_static_methods_return_descriptor
test_exec!(
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_static_methods_return_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -2968,13 +3167,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_methods_return_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -3099,13 +3308,23 @@ test_exec!(
// Legacy decorator for object literals
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_object_methods_string_props_exec,
r#"
function dec(target, name, descriptor){
@ -3128,13 +3347,23 @@ const inst = {
test_exec!(
ignore,
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_prototype_properties_child_classes_properties_exec,
r#"
function dec(target, name, descriptor){
@ -3171,13 +3400,23 @@ expect(inst.prop2).toBe("__4__");
// legacy_class_static_methods_mutate_descriptor
test_exec!(
syntax(false),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
)
},
legacy_class_static_methods_mutate_descriptor_exec,
r#"
function dec(target, name, descriptor) {
@ -3381,14 +3620,24 @@ test!(
// See: https://github.com/swc-project/swc/issues/421
ignore,
Default::default(),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
classes(Some(t.comments.clone()), Default::default())
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
classes(Some(t.comments.clone()), Default::default())
)
},
decorators_legacy_interop_local_define_property,
r#"
function dec() {}

View File

@ -528,14 +528,24 @@ test!(
// See: https://github.com/swc-project/swc/issues/421
ignore,
syntax(),
|t| chain!(
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(Some(t.comments.clone()), Default::default()),
classes(Some(t.comments.clone()), Default::default()),
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
decorators(decorators::Config {
legacy: true,
..Default::default()
}),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
classes(Some(t.comments.clone()), Default::default()),
)
},
decorators_legacy_interop_strict,
r#"
function dec() {}

View File

@ -1,6 +1,6 @@
var Extended = /*#__PURE__*/ function(Base) {
var Extended = /*#__PURE__*/ function(Base1) {
"use strict";
_inherits(Extended, Base);
_inherits(Extended, Base1);
var _super = _create_super(Extended);
function Extended() {
_class_call_check(this, Extended);

View File

@ -1,6 +1,6 @@
var Extended = /*#__PURE__*/ function(Base) {
var Extended = /*#__PURE__*/ function(Base1) {
"use strict";
_inherits(Extended, Base);
_inherits(Extended, Base1);
var _super = _create_super(Extended);
function Extended() {
_class_call_check(this, Extended);

View File

@ -1,8 +1,8 @@
var _priv = /*#__PURE__*/ new WeakMap();
class Foo {
search() {
var _class_private_field_get1;
(_class_private_field_get1 = _class_private_field_get(this, _priv)) === null || _class_private_field_get1 === void 0 ? void 0 : _class_private_field_get1.call(this);
var _this, _this1, _ref;
(_this = _class_private_field_get(_ref = _this1 = this, _priv)) === null || _this === void 0 ? void 0 : _this.call(_this1);
}
constructor(){
_class_private_field_init(this, _priv, {

View File

@ -1,7 +1,8 @@
var _fieldFunc = /*#__PURE__*/ new WeakMap();
class A {
test() {
_class_private_field_get(this, _fieldFunc)?.call(this);
var _this, _this1, _ref;
(_this = _class_private_field_get(_ref = _this1 = this, _fieldFunc)) === null || _this === void 0 ? void 0 : _this.call(_this1);
}
constructor(){
_class_private_field_init(this, _fieldFunc, {

View File

@ -27,7 +27,7 @@
});
(function() {
class Baz {
constructor(force){
constructor(force1){
var _this = this;
_define_property(this, "fn", function() {
return console.log(_this);

View File

@ -0,0 +1,7 @@
class Foo {
#x;
test() {
this?.y.#x;
}
}

View File

@ -0,0 +1,13 @@
var _x = /*#__PURE__*/ new WeakMap();
class Foo {
test() {
var _this, _this_y;
(_this = this) === null || _this === void 0 ? void 0 : _class_private_field_get(_this_y = _this.y, _x);
}
constructor(){
_class_private_field_init(this, _x, {
writable: true,
value: void 0
});
}
}

View File

@ -1,6 +1,6 @@
let Foo = /*#__PURE__*/ function(UnknownNativeClass) {
let Foo = /*#__PURE__*/ function(UnknownNativeClass1) {
"use strict";
_inherits(Foo, UnknownNativeClass);
_inherits(Foo, UnknownNativeClass1);
var _super = _create_super(Foo);
function Foo() {
_class_call_check(this, Foo);

View File

@ -0,0 +1,18 @@
let Extended = /*#__PURE__*/ function(Base1) {
"use strict";
_inherits(Extended, Base1);
var _super = _create_super(Extended);
function Extended() {
_class_call_check(this, Extended);
return _super.apply(this, arguments);
}
_create_class(Extended, [
{
key: "getNext",
value: function getNext() {
return _get(_get_prototype_of(Extended.prototype), "getNext", this).call(this, 114514) + 114514;
}
}
]);
return Extended;
}(Base);

View File

@ -0,0 +1,18 @@
let Extended = /*#__PURE__*/ function(Base1) {
"use strict";
_inherits(Extended, Base1);
var _super = _create_super(Extended);
function Extended() {
_class_call_check(this, Extended);
return _super.apply(this, arguments);
}
_create_class(Extended, [
{
key: "getNext",
value: function getNext() {
return _get(_get_prototype_of(Extended.prototype), "getNext", this).call(this, 114514);
}
}
]);
return Extended;
}(Base);

View File

@ -3940,10 +3940,16 @@ test!(
test!(
syntax(),
|t| {
let unresolved_mark = Mark::fresh(Mark::root());
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
es2022::es2022(Some(t.comments.clone()), Default::default()),
resolver(unresolved_mark, top_level_mark, true),
es2022::es2022(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
es2018::es2018(Default::default()),
es2017::es2017(
Default::default(),
@ -3967,11 +3973,21 @@ test!(
test!(
syntax(),
|t| {
let global_mark = Mark::fresh(Mark::root());
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
class_properties(Some(t.comments.clone()), Default::default()),
es2015::es2015(global_mark, Some(t.comments.clone()), Default::default()),
resolver(unresolved_mark, top_level_mark, true),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
es2015::es2015(
unresolved_mark,
Some(t.comments.clone()),
Default::default()
),
)
},
issue_1660_5,
@ -4005,11 +4021,21 @@ test!(
test!(
syntax(),
|t| {
let global_mark = Mark::fresh(Mark::root());
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
class_properties(Some(t.comments.clone()), Default::default()),
es2015::es2015(global_mark, Some(t.comments.clone()), Default::default()),
resolver(unresolved_mark, top_level_mark, true),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
es2015::es2015(
unresolved_mark,
Some(t.comments.clone()),
Default::default()
),
)
},
issue_1959_1,
@ -4025,11 +4051,21 @@ test!(
test!(
syntax(),
|t| {
let global_mark = Mark::fresh(Mark::root());
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
class_properties(Some(t.comments.clone()), Default::default()),
es2015::es2015(global_mark, Some(t.comments.clone()), Default::default()),
resolver(unresolved_mark, top_level_mark, true),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
es2015::es2015(
unresolved_mark,
Some(t.comments.clone()),
Default::default()
),
)
},
issue_1959_2,
@ -4048,8 +4084,16 @@ fn exec(input: PathBuf) {
compare_stdout(
Default::default(),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
class_properties(Some(t.comments.clone()), Default::default()),
resolver(unresolved_mark, top_level_mark, true),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
classes(Some(t.comments.clone()), Default::default())
)
},
@ -4064,8 +4108,16 @@ fn fixture(input: PathBuf) {
test_fixture(
Default::default(),
&|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
class_properties(Some(t.comments.clone()), Default::default()),
resolver(unresolved_mark, top_level_mark, true),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
classes(Some(t.comments.clone()), Default::default())
)
},

View File

@ -1406,14 +1406,25 @@ test!(
test!(
Syntax::default(),
|t| {
let mark = Mark::fresh(Mark::root());
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
es2022(Some(t.comments.clone()), Default::default()),
resolver(unresolved_mark, top_level_mark, true),
es2022(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
es2021(),
es2018(Default::default()),
es2017(Default::default(), Some(t.comments.clone()), mark),
es2017(
Default::default(),
Some(t.comments.clone()),
unresolved_mark
),
es2016(),
es2015::<SingleThreadedComments>(mark, None, Default::default()),
es2015::<SingleThreadedComments>(unresolved_mark, None, Default::default()),
)
},
issue_1799_5,

View File

@ -2,7 +2,7 @@ use std::{fs::read_to_string, path::PathBuf};
use serde::Deserialize;
use swc_common::{chain, Mark};
use swc_ecma_transforms_base::pass::noop;
use swc_ecma_transforms_base::resolver;
use swc_ecma_transforms_compat::{
es2015::{arrow, classes, new_target::new_target},
es2022::class_properties,
@ -11,7 +11,10 @@ use swc_ecma_transforms_testing::{exec_tr, parse_options, test, test_fixture, Te
use swc_ecma_visit::Fold;
fn get_passes(t: &Tester, plugins: &[PluginConfig]) -> Box<dyn Fold> {
let mut pass: Box<dyn Fold> = Box::new(noop());
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
let mut pass: Box<dyn Fold> = Box::new(resolver(unresolved_mark, top_level_mark, true));
for plugin in plugins {
let (name, option) = match plugin {
@ -45,7 +48,9 @@ fn get_passes(t: &Tester, plugins: &[PluginConfig]) -> Box<dyn Fold> {
private_as_properties: loose,
no_document_all: loose,
static_blocks_mark: Mark::new(),
}
pure_getter: loose,
},
unresolved_mark,
)
));
}

View File

@ -1362,10 +1362,20 @@ test!(
test_exec!(
Syntax::default(),
|t| chain!(
class_properties(Some(t.comments.clone()), Default::default()),
async_to_generator(Default::default(), Some(t.comments.clone()), Mark::new())
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
async_to_generator(Default::default(), Some(t.comments.clone()), Mark::new())
)
},
issue_1341_1_exec,
"
class A {
@ -1400,10 +1410,20 @@ test!(
test_exec!(
Syntax::default(),
|t| chain!(
class_properties(Some(t.comments.clone()), Default::default()),
async_to_generator(Default::default(), Some(t.comments.clone()), Mark::new())
),
|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, true),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
async_to_generator(Default::default(), Some(t.comments.clone()), Mark::new())
)
},
issue_1341_2_exec,
"
class A {
@ -2068,7 +2088,11 @@ fn exec(input: PathBuf) {
chain!(
resolver(unresolved_mark, top_level_mark, false),
class_properties(Some(t.comments.clone()), Default::default()),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
async_to_generator(
Default::default(),
Some(t.comments.clone()),
@ -2091,7 +2115,11 @@ fn exec_regenerator(input: PathBuf) {
chain!(
resolver(unresolved_mark, top_level_mark, false),
class_properties(Some(t.comments.clone()), Default::default()),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
async_to_generator(
Default::default(),
Some(t.comments.clone()),

View File

@ -3,7 +3,10 @@ use std::{fs::read_to_string, path::PathBuf};
use swc_common::{chain, Mark};
use swc_ecma_parser::Syntax;
use swc_ecma_transforms_base::resolver;
use swc_ecma_transforms_compat::es2020::{optional_chaining, optional_chaining::Config};
use swc_ecma_transforms_compat::{
es2020::{optional_chaining, optional_chaining::Config},
es2022::class_properties,
};
use swc_ecma_transforms_testing::{compare_stdout, test, test_exec, test_fixture};
use swc_ecma_visit::Fold;
@ -275,11 +278,19 @@ fn fixture(input: PathBuf) {
test_fixture(
Default::default(),
&|_| {
&|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
class_properties(
Some(t.comments.clone()),
swc_ecma_transforms_compat::es2022::class_properties::Config {
private_as_properties: false,
..Default::default()
},
unresolved_mark
),
optional_chaining(Default::default(), unresolved_mark)
)
},
@ -295,11 +306,22 @@ fn fixture_loose(input: PathBuf) {
test_fixture(
Default::default(),
&|_| {
&|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
resolver(unresolved_mark, top_level_mark, false),
class_properties(
Some(t.comments.clone()),
swc_ecma_transforms_compat::es2022::class_properties::Config {
private_as_properties: false,
pure_getter: true,
no_document_all: true,
..Default::default()
},
unresolved_mark
),
optional_chaining(
Config {
no_document_all: true,

View File

@ -2,7 +2,7 @@ use std::path::PathBuf;
use serde::Deserialize;
use swc_common::{chain, Mark};
use swc_ecma_transforms_base::pass::noop;
use swc_ecma_transforms_base::resolver;
use swc_ecma_transforms_compat::{
es2015::classes,
es2022::{class_properties, private_in_object},
@ -33,7 +33,11 @@ fn fixture(input: PathBuf) {
test_fixture(
Default::default(),
&|t| {
let mut pass: Box<dyn Fold> = Box::new(noop());
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
let mut pass: Box<dyn Fold> =
Box::new(resolver(unresolved_mark, top_level_mark, false));
let mut class_props = false;
@ -60,9 +64,11 @@ fn fixture(input: PathBuf) {
constant_super: loose,
no_document_all: loose,
private_as_properties: loose,
pure_getter: loose,
static_blocks_mark: Mark::new(),
}
)
},
unresolved_mark
),
));
}
}
@ -79,8 +85,10 @@ fn fixture(input: PathBuf) {
constant_super: loose,
no_document_all: loose,
private_as_properties: loose,
pure_getter: loose,
static_blocks_mark: Mark::new(),
}
},
unresolved_mark,
)
));
}

View File

@ -1,7 +1,8 @@
use std::path::PathBuf;
use swc_common::chain;
use swc_common::{chain, Mark};
use swc_ecma_parser::Syntax;
use swc_ecma_transforms_base::resolver;
use swc_ecma_transforms_compat::es2022::{class_properties, static_blocks};
use swc_ecma_transforms_testing::test_fixture;
use swc_ecma_visit::Fold;
@ -14,14 +15,21 @@ fn fixture(input: PathBuf) {
test_fixture(
Syntax::Es(Default::default()),
&|t| {
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
let config = class_properties::Config::default();
let pass: Box<dyn Fold> = if input.to_string_lossy().contains("class-properties") {
Box::new(chain!(
resolver(unresolved_mark, top_level_mark, false),
static_blocks(config.static_blocks_mark),
class_properties(Some(t.comments.clone()), config)
class_properties(Some(t.comments.clone()), config, unresolved_mark)
))
} else {
Box::new(static_blocks(config.static_blocks_mark))
Box::new(chain!(
resolver(unresolved_mark, top_level_mark, false),
static_blocks(config.static_blocks_mark)
))
};
pass
},

View File

@ -1,7 +1,13 @@
var _x = /*#__PURE__*/ new WeakMap();
class Foo {
#x;
test() {
var _this;
(_this = this) == null ? void 0 : _this.y.#x;
var _this, _this_y;
(_this = this) == null ? void 0 : _class_private_field_get(_this_y = _this.y, _x);
}
constructor(){
_class_private_field_init(this, _x, {
writable: true,
value: void 0
});
}
}

View File

@ -0,0 +1,7 @@
class Foo {
#x;
test() {
this?.y.#x
}
}

View File

@ -0,0 +1,13 @@
var _x = /*#__PURE__*/ new WeakMap();
class Foo {
test() {
var _this, _this_y;
(_this = this) === null || _this === void 0 ? void 0 : _class_private_field_get(_this_y = _this.y, _x);
}
constructor(){
_class_private_field_init(this, _x, {
writable: true,
value: void 0
});
}
}

View File

@ -0,0 +1,9 @@
// @target: es2015
class A {
#fieldFunc = function () { this.x = 10; };
x = 1;
test() {
this.#fieldFunc?.();
}
}

View File

@ -0,0 +1,17 @@
// @target: es2015
var _fieldFunc = /*#__PURE__*/ new WeakMap();
class A {
test() {
var _this, _this1, _ref;
(_this = _class_private_field_get(_ref = _this1 = this, _fieldFunc)) === null || _this === void 0 ? void 0 : _this.call(_this1);
}
constructor(){
_class_private_field_init(this, _fieldFunc, {
writable: true,
value: function() {
this.x = 10;
}
});
_define_property(this, "x", 1);
}
}

View File

@ -506,7 +506,8 @@ test!(
class_properties::Config {
set_public_fields: true,
..Default::default()
}
},
unresolved_mark
),
dce(Default::default(), unresolved_mark),
inlining(Default::default())
@ -553,7 +554,11 @@ test!(
decorators(Default::default()),
resolver(unresolved_mark, top_level_mark, false),
strip(top_level_mark),
class_properties(Some(t.comments.clone()), Default::default()),
class_properties(
Some(t.comments.clone()),
Default::default(),
unresolved_mark
),
Repeat::new(chain!(
expr_simplifier(unresolved_mark, Default::default()),
inlining::inlining(Default::default()),

View File

@ -477,7 +477,8 @@ test!(
class_properties::Config {
set_public_fields: true,
..Default::default()
}
},
unresolved_mark
),
tr()
)
@ -516,7 +517,8 @@ test!(
class_properties::Config {
set_public_fields: true,
..Default::default()
}
},
unresolved_mark
),
tr(),
)
@ -598,7 +600,8 @@ test!(
class_properties::Config {
set_public_fields: true,
..Default::default()
}
},
unresolved_mark
),
tr(),
)

View File

@ -2072,7 +2072,8 @@ test!(
class_properties::Config {
set_public_fields: true,
..Default::default()
}
},
unresolved_mark
),
inlining(Default::default())
)
@ -2110,7 +2111,8 @@ test!(
class_properties::Config {
set_public_fields: true,
..Default::default()
}
},
unresolved_mark
),
inlining(Default::default())
)

View File

@ -142,7 +142,8 @@ fn create_pass(comments: Rc<SingleThreadedComments>, input: &Path) -> Box<dyn Fo
));
add!(swc_ecma_transforms_compat::es2022::class_properties(
Some(comments.clone()),
Default::default()
Default::default(),
unresolved_mark
));
continue;
}
@ -150,7 +151,8 @@ fn create_pass(comments: Rc<SingleThreadedComments>, input: &Path) -> Box<dyn Fo
"proposal-private-methods" => {
add!(swc_ecma_transforms_compat::es2022::class_properties(
Some(comments.clone()),
Default::default()
Default::default(),
unresolved_mark
));
continue;
}

View File

@ -21,7 +21,7 @@ fn module(cm: Lrc<SourceMap>) -> Module {
parser.parse_module().map_err(|_| ()).unwrap()
}
fn run<V>(b: &mut Bencher, tr: impl Fn() -> V)
fn run<V>(b: &mut Bencher, tr: impl Fn(Mark) -> V)
where
V: Fold,
{
@ -37,7 +37,7 @@ where
let module = module.clone();
helpers::HELPERS.set(&Default::default(), || {
let mut tr = tr();
let mut tr = tr(unresolved_mark);
black_box(module.fold_with(&mut tr));
});
@ -64,7 +64,7 @@ fn base(b: &mut Bencher) {
m
}
}
run(b, || Noop);
run(b, |_| Noop);
}
fn common_typescript(b: &mut Bencher) {
@ -89,7 +89,7 @@ fn common_typescript(b: &mut Bencher) {
}
fn common_reserved_word(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::reserved_words::reserved_words()
});
}
@ -132,16 +132,17 @@ fn single_tr_group(c: &mut Criterion) {
}
fn es2020(b: &mut Bencher) {
run(b, || {
run(b, |unresolved_mark| {
swc_ecma_transforms_compat::es2022(
Some(SingleThreadedComments::default()),
Default::default(),
unresolved_mark,
)
});
}
fn es2020_nullish_coalescing(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2020::nullish_coalescing(
swc_ecma_transforms_compat::es2020::nullish_coalescing::Config {
no_document_all: false,
@ -151,38 +152,42 @@ fn es2020_nullish_coalescing(b: &mut Bencher) {
}
fn es2020_optional_chaining(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2020::optional_chaining(Default::default(), Mark::new())
});
}
fn es2022_class_properties(b: &mut Bencher) {
run(b, || {
run(b, |unresolved_mark| {
swc_ecma_transforms_compat::es2022::class_properties(
Some(SingleThreadedComments::default()),
Default::default(),
unresolved_mark,
)
});
}
fn es2018(b: &mut Bencher) {
run(b, || swc_ecma_transforms_compat::es2018(Default::default()));
run(
b,
|_| swc_ecma_transforms_compat::es2018(Default::default()),
);
}
fn es2018_object_rest_spread(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2018::object_rest_spread(Default::default())
});
}
fn es2019_optional_catch_binding(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2019::optional_catch_binding()
});
}
fn es2017(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2017(
Default::default(),
Some(SingleThreadedComments::default()),
@ -192,7 +197,7 @@ fn es2017(b: &mut Bencher) {
}
fn es2017_async_to_generator(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2017::async_to_generator(
Default::default(),
Some(SingleThreadedComments::default()),
@ -202,17 +207,17 @@ fn es2017_async_to_generator(b: &mut Bencher) {
}
fn es2016(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2016);
run(b, |_| swc_ecma_transforms_compat::es2016());
}
fn es2016_exponentiation(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2016::exponentiation);
run(b, |_| swc_ecma_transforms_compat::es2016::exponentiation());
}
fn es2015(b: &mut Bencher) {
run(b, || {
run(b, |unresolved_mark| {
swc_ecma_transforms_compat::es2015(
Mark::fresh(Mark::root()),
unresolved_mark,
Some(SingleThreadedComments::default()),
Default::default(),
)
@ -220,23 +225,26 @@ fn es2015(b: &mut Bencher) {
}
fn es2015_arrow(b: &mut Bencher) {
run(b, || swc_ecma_transforms_compat::es2015::arrow(Mark::new()));
run(
b,
|_| swc_ecma_transforms_compat::es2015::arrow(Mark::new()),
);
}
fn es2015_block_scoped_fn(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::block_scoped_functions()
});
}
fn es2015_block_scoping(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::block_scoping(Mark::new())
});
}
fn es2015_classes(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::classes(
Some(SingleThreadedComments::default()),
Default::default(),
@ -245,68 +253,72 @@ fn es2015_classes(b: &mut Bencher) {
}
fn es2015_computed_props(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::computed_properties(Default::default())
});
}
fn es2015_destructuring(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::destructuring(Default::default())
});
}
fn es2015_duplicate_keys(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2015::duplicate_keys);
run(b, |_| swc_ecma_transforms_compat::es2015::duplicate_keys());
}
fn es2015_parameters(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::parameters(Default::default(), Mark::new())
});
}
fn es2015_fn_name(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2015::function_name);
run(b, |_| swc_ecma_transforms_compat::es2015::function_name());
}
fn es2015_for_of(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::for_of(Default::default())
});
}
fn es2015_instanceof(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2015::instance_of);
run(b, |_| swc_ecma_transforms_compat::es2015::instance_of());
}
fn es2015_shorthand_property(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2015::shorthand);
run(b, |_| swc_ecma_transforms_compat::es2015::shorthand());
}
fn es2015_spread(b: &mut Bencher) {
run(b, || {
run(b, |_| {
swc_ecma_transforms_compat::es2015::spread(Default::default())
});
}
fn es2015_sticky_regex(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2015::sticky_regex);
run(b, |_| swc_ecma_transforms_compat::es2015::sticky_regex());
}
fn es2015_typeof_symbol(b: &mut Bencher) {
run(b, swc_ecma_transforms_compat::es2015::typeof_symbol);
run(b, |_| swc_ecma_transforms_compat::es2015::typeof_symbol());
}
fn es3(b: &mut Bencher) {
run(b, || swc_ecma_transforms_compat::es3(Default::default()));
run(b, |_| swc_ecma_transforms_compat::es3(Default::default()));
}
fn full_es2016(b: &mut Bencher) {
run(b, || {
run(b, |unresolved_mark| {
let cmt = SingleThreadedComments::default();
chain!(
swc_ecma_transforms_compat::es2022(Some(cmt.clone()), Default::default()),
swc_ecma_transforms_compat::es2022(
Some(cmt.clone()),
Default::default(),
unresolved_mark
),
swc_ecma_transforms_compat::es2019(),
swc_ecma_transforms_compat::es2018(Default::default()),
swc_ecma_transforms_compat::es2017(Default::default(), Some(cmt), Mark::new()),
@ -316,10 +328,14 @@ fn full_es2016(b: &mut Bencher) {
}
fn full_es2017(b: &mut Bencher) {
run(b, || {
run(b, |unresolved_mark| {
let cmt = SingleThreadedComments::default();
chain!(
swc_ecma_transforms_compat::es2022(Some(cmt.clone()), Default::default()),
swc_ecma_transforms_compat::es2022(
Some(cmt.clone()),
Default::default(),
unresolved_mark
),
swc_ecma_transforms_compat::es2019(),
swc_ecma_transforms_compat::es2018(Default::default()),
swc_ecma_transforms_compat::es2017(Default::default(), Some(cmt), Mark::new()),
@ -328,11 +344,12 @@ fn full_es2017(b: &mut Bencher) {
}
fn full_es2018(b: &mut Bencher) {
run(b, || {
run(b, |unresolved_mark| {
chain!(
swc_ecma_transforms_compat::es2022(
Some(SingleThreadedComments::default()),
Default::default()
Default::default(),
unresolved_mark
),
swc_ecma_transforms_compat::es2019(),
swc_ecma_transforms_compat::es2018(Default::default()),

View File

@ -46,16 +46,21 @@ fn tr_config(
}
fn properties(t: &Tester, loose: bool) -> impl Fold {
let mark = Mark::new();
let static_blocks_mark = Mark::new();
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
chain!(
static_blocks(mark),
resolver(unresolved_mark, top_level_mark, false),
static_blocks(static_blocks_mark),
class_properties(
Some(t.comments.clone()),
class_properties::Config {
static_blocks_mark: mark,
static_blocks_mark,
set_public_fields: loose,
..Default::default()
},
unresolved_mark
)
)
}