fix(es/compat): Handle private fields in nested classes (#3431)

This commit is contained in:
Austaras 2022-02-03 16:54:30 +08:00 committed by GitHub
parent 2ea81db19f
commit 01500a54e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 318 additions and 379 deletions

View File

@ -1,4 +1,6 @@
class Foo {
#ws;
#ws2;
get connected() {
return this.#ws2 && this.#ws.readyState === _ws1.default.OPEN;
}

View File

@ -9,4 +9,16 @@ class Foo {
get connected() {
return _classPrivateFieldGet(this, _ws2) && _classPrivateFieldGet(this, _ws).readyState === _ws1.default.OPEN;
}
constructor(){
_ws.set(this, {
writable: true,
value: void 0
});
_ws2.set(this, {
writable: true,
value: void 0
});
}
}
var _ws = new WeakMap();
var _ws2 = new WeakMap();

View File

@ -1,21 +0,0 @@
function _classPrivateFieldGet(receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver).value;
}
// @strict: true
// @target: es6
class A {
method(thing) {
_classPrivateFieldGet(thing, _foo); // OK
_classPrivateFieldGet(thing, _bar); // Error
}
constructor(){
_foo.set(this, {
writable: true,
value: true
});
}
}
var _foo = new WeakMap();

View File

@ -1,48 +0,0 @@
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _classPrivateFieldGet(receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver).value;
}
function _defineProperties(target, props) {
for(var i = 0; i < props.length; i++){
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
var A = // @strict: true
// @target: es6
/*#__PURE__*/ function() {
"use strict";
function A() {
_classCallCheck(this, A);
_foo.set(this, {
writable: true,
value: true
});
}
_createClass(A, [
{
key: "method",
value: function method(thing) {
_classPrivateFieldGet(thing, _foo); // OK
_classPrivateFieldGet(thing, _bar); // Error
}
}
]);
return A;
}();
var _foo = new WeakMap();

View File

@ -1,30 +0,0 @@
function _classPrivateFieldGet(receiver, privateMap) {
if (!privateMap.has(receiver)) throw new TypeError("attempted to get private field on non-instance");
return privateMap.get(receiver).value;
}
function _defineProperties(target, props) {
for(var i = 0; i < props.length; i++){
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || !1, descriptor.configurable = !0, "value" in descriptor && (descriptor.writable = !0), Object.defineProperty(target, descriptor.key, descriptor);
}
}
var A = function() {
"use strict";
var Constructor, protoProps, staticProps;
function A() {
!function(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}(this, A), _foo.set(this, {
writable: !0,
value: !0
});
}
return Constructor = A, protoProps = [
{
key: "method",
value: function(thing) {
_classPrivateFieldGet(thing, _foo), _classPrivateFieldGet(thing, _bar);
}
}
], _defineProperties(Constructor.prototype, protoProps), staticProps && _defineProperties(Constructor, staticProps), A;
}(), _foo = new WeakMap();

View File

@ -1,29 +0,0 @@
function _classPrivateFieldSet(receiver, privateMap, value) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to set private field on non-instance");
}
var descriptor = privateMap.get(receiver);
if (!descriptor.writable) {
throw new TypeError("attempted to set read only private field");
}
descriptor.value = value;
return value;
}
var _key;
// @strict: true
// @target: es6
class A {
constructor(message){
_foo.set(this, {
writable: true,
value: 3
});
this[_key] // Error (private identifiers should not prevent circularity checking for computeds)
= this["#bar"];
_classPrivateFieldSet(this, _f, 3 // Error (index signatures do not implicitly declare private names)
);
this["#foo"] = 3; // Okay (type has index signature and "#foo" does not collide with private identifier #foo)
}
}
var _foo = new WeakMap();
_key = "#bar";

View File

@ -1,2 +0,0 @@
var _key;
new WeakMap(), _key = "#bar";

View File

@ -1,34 +0,0 @@
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _classPrivateFieldSet(receiver, privateMap, value) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to set private field on non-instance");
}
var descriptor = privateMap.get(receiver);
if (!descriptor.writable) {
throw new TypeError("attempted to set read only private field");
}
descriptor.value = value;
return value;
}
var _key;
var A = function A(message) {
"use strict";
_classCallCheck(this, A);
_foo.set(this, {
writable: true,
value: 3
});
// @strict: true
// @target: es6
this[_key] // Error (private identifiers should not prevent circularity checking for computeds)
= this["#bar"];
_classPrivateFieldSet(this, _f, 3 // Error (index signatures do not implicitly declare private names)
);
this["#foo"] = 3; // Okay (type has index signature and "#foo" does not collide with private identifier #foo)
};
var _foo = new WeakMap();
_key = "#bar";

View File

@ -1,15 +0,0 @@
var _key, A = function(message) {
"use strict";
!function(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}(this, A), _foo.set(this, {
writable: !0,
value: 3
}), this[_key] = this["#bar"], (function(receiver, privateMap, value) {
if (!privateMap.has(receiver)) throw new TypeError("attempted to set private field on non-instance");
var descriptor = privateMap.get(receiver);
if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
descriptor.value = 3;
})(this, _f, 3), this["#foo"] = 3;
}, _foo = new WeakMap();
_key = "#bar";

View File

@ -1,15 +0,0 @@
function _classPrivateFieldGet(receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver).value;
}
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @Filename: privateNameImplicitDeclaration.js
class C {
constructor(){
_classPrivateFieldGet(/** @type {string} */ this, _x);
}
}

View File

@ -1,16 +0,0 @@
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _classPrivateFieldGet(receiver, privateMap) {
if (!privateMap.has(receiver)) {
throw new TypeError("attempted to get private field on non-instance");
}
return privateMap.get(receiver).value;
}
var C = function C() {
"use strict";
_classCallCheck(this, C);
_classPrivateFieldGet(/** @type {string} */ this, _x);
};

View File

@ -1,9 +0,0 @@
var C = function() {
"use strict";
!function(instance, Constructor) {
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
}(this, C), (function(receiver, privateMap) {
if (!privateMap.has(receiver)) throw new TypeError("attempted to get private field on non-instance");
privateMap.get(receiver).value;
})(this, _x);
};

View File

@ -20,6 +20,10 @@ use testing::{NormalizedOutput, Tester};
"propertyAccessChain\\.3.ts",
"objectRestNegative.ts",
"objectRestPropertyMustBeLast.ts",
// shall panic because not finding private field
"privateNameAndAny.ts",
"privateNameAndIndexSignature.ts",
"privateNameImplicitDeclaration.ts"
)
)]
#[testing::fixture("../swc_ecma_parser/tests/tsc/**/*.tsx")]

View File

@ -2,12 +2,11 @@
use self::{
class_name_tdz::ClassNameTdzFolder,
private_field::{BrandCheckHandler, FieldAccessFolder},
private_field::{BrandCheckHandler, FieldAccessFolder, Private, PrivateKind, PrivateRecord},
this_in_static::ThisInStaticFolder,
used_name::UsedNameCollector,
};
use std::collections::HashSet;
use swc_common::{util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
use swc_common::{collections::AHashSet, util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::{helper, perf::Check};
use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
@ -35,8 +34,7 @@ mod used_name;
pub fn class_properties(config: Config) -> impl Fold + VisitMut {
as_folder(ClassProperties {
config,
mark: Mark::root(),
method_mark: Mark::root(),
private: PrivateRecord::new(),
})
}
@ -45,11 +43,9 @@ pub struct Config {
pub loose: bool,
}
#[derive(Clone)]
struct ClassProperties {
config: Config,
mark: Mark,
method_mark: Mark,
private: PrivateRecord,
}
#[fast_path(ShouldWork)]
@ -111,8 +107,6 @@ impl VisitMut for ClassProperties {
}
fn visit_mut_expr(&mut self, expr: &mut Expr) {
expr.visit_mut_children_with(self);
if let Expr::Class(ClassExpr { ident, class }) = expr {
let ident = ident.take().unwrap_or_else(|| private_ident!("_class"));
let mut stmts = vec![];
@ -168,6 +162,8 @@ impl VisitMut for ClassProperties {
args: vec![],
type_args: Default::default(),
});
} else {
expr.visit_mut_children_with(self);
};
}
}
@ -183,8 +179,6 @@ impl ClassProperties {
match T::try_into_stmt(stmt) {
Err(node) => match node.try_into_module_decl() {
Ok(mut decl) => {
decl.visit_mut_children_with(self);
match decl {
ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
span,
@ -261,16 +255,18 @@ impl ClassProperties {
);
buf.extend(stmts.into_iter().map(T::from_stmt));
}
_ => buf.push(match T::try_from_module_decl(decl) {
Ok(t) => t,
Err(..) => unreachable!(),
}),
_ => {
decl.visit_mut_children_with(self);
buf.push(match T::try_from_module_decl(decl) {
Ok(t) => t,
Err(..) => unreachable!(),
})
}
};
}
Err(..) => unreachable!(),
},
Ok(mut stmt) => {
stmt.visit_mut_children_with(self);
// Fold class
match stmt {
Stmt::Decl(Decl::Class(ClassDecl {
@ -290,7 +286,10 @@ impl ClassProperties {
buf.push(T::from_stmt(Stmt::Decl(Decl::Class(decl))));
buf.extend(stmts.into_iter().map(T::from_stmt));
}
_ => buf.push(T::from_stmt(stmt)),
_ => {
stmt.visit_mut_children_with(self);
buf.push(T::from_stmt(stmt))
}
}
}
}
@ -307,8 +306,38 @@ impl ClassProperties {
mut class: Class,
) -> (Vec<VarDeclarator>, ClassDecl, Vec<Stmt>) {
// Create one mark per class
self.mark = Mark::fresh(Mark::root());
self.method_mark = Mark::fresh(Mark::root());
let private = Private {
mark: Mark::fresh(Mark::root()),
class_name: class_ident.clone(),
ident: class
.body
.iter()
.filter_map(|member| match member {
ClassMember::PrivateMethod(method) => Some((
method.key.id.sym.clone(),
PrivateKind {
is_method: true,
is_static: method.is_static,
},
)),
ClassMember::PrivateProp(prop) => Some((
prop.key.id.sym.clone(),
PrivateKind {
is_method: false,
is_static: prop.is_static,
},
)),
_ => None,
})
.collect(),
};
self.private.push(private);
// we must collect outer class's private first
class.visit_mut_children_with(self);
let has_super = class.super_class.is_some();
@ -322,48 +351,11 @@ impl ClassProperties {
let mut constructor = None;
let mut used_names = vec![];
let mut used_key_names = vec![];
let mut names_used_for_brand_checks = HashSet::default();
let statics = {
let mut s = HashSet::default();
for member in &class.body {
match member {
ClassMember::PrivateMethod(method) => {
if method.is_static {
s.insert(method.key.id.sym.clone());
}
}
ClassMember::PrivateProp(prop) => {
if prop.is_static {
s.insert(prop.key.id.sym.clone());
}
}
_ => {}
}
}
s
};
let private_methods = {
let mut s = HashSet::default();
for member in &class.body {
if let ClassMember::PrivateMethod(method) = member {
s.insert(method.key.id.sym.clone());
}
}
s
};
let mut names_used_for_brand_checks = AHashSet::default();
class.body.visit_mut_with(&mut BrandCheckHandler {
mark: self.mark,
class_name: &class_ident,
names: &mut names_used_for_brand_checks,
statics: &statics,
private: &self.private,
});
for member in class.body {
@ -505,7 +497,7 @@ impl ClassProperties {
let ident = Ident::new(
format!("_{}", prop.key.id.sym).into(),
// We use `self.mark` for private variables.
prop.key.span.apply_mark(self.mark),
prop.key.span.apply_mark(self.private.curr_mark()),
);
prop.value.visit_with(&mut UsedNameCollector {
used_names: &mut used_names,
@ -594,30 +586,31 @@ impl ClassProperties {
}
ClassMember::PrivateMethod(method) => {
let is_static = method.is_static;
let prop_span = method.span;
let fn_name = Ident::new(
method.key.id.sym.clone(),
method
.span
.with_ctxt(SyntaxContext::empty())
.apply_mark(self.method_mark),
.apply_mark(self.private.curr_mark()),
);
let should_use_map =
matches!(method.kind, MethodKind::Getter | MethodKind::Setter)
&& names_used_for_brand_checks.contains(&method.key.id.sym)
&& !statics.contains(&method.key.id.sym);
&& !is_static;
let weak_coll_var = Ident::new(
format!("_{}", method.key.id.sym).into(),
// We use `self.mark` for private variables.
method.key.span.apply_mark(self.mark),
method.key.span.apply_mark(self.private.curr_mark()),
);
method.function.visit_with(&mut UsedNameCollector {
used_names: &mut used_names,
});
if should_use_map || !statics.contains(&method.key.id.sym) {
if should_use_map || !is_static {
vars.push(VarDeclarator {
span: DUMMY_SP,
definite: false,
@ -650,7 +643,7 @@ impl ClassProperties {
props: vec![get, set],
};
let obj = if statics.contains(&method.key.id.sym) {
let obj = if is_static {
let var_name = private_ident!("static_method");
vars.push(VarDeclarator {
@ -674,7 +667,7 @@ impl ClassProperties {
args: vec![ThisExpr { span: DUMMY_SP }.as_arg(), obj],
type_args: Default::default(),
})));
} else if !statics.contains(&method.key.id.sym) {
} else if !is_static {
// Add `_get.add(this);` to the constructor where `_get` is the name of the
// weak set.
constructor_exprs.push(Box::new(Expr::Call(CallExpr {
@ -712,27 +705,21 @@ impl ClassProperties {
}
private_method_fn_decls.visit_mut_with(&mut FieldAccessFolder {
mark: self.mark,
method_mark: self.method_mark,
private_methods: &private_methods,
statics: &statics,
private: &self.private,
vars: vec![],
class_name: &class_ident,
in_assign_pat: false,
});
extra_stmts.extend(private_method_fn_decls);
members.visit_mut_with(&mut FieldAccessFolder {
mark: self.mark,
method_mark: self.method_mark,
private_methods: &private_methods,
statics: &statics,
private: &self.private,
vars: vec![],
class_name: &class_ident,
in_assign_pat: false,
});
self.private.pop();
(
vars,
ClassDecl {

View File

@ -1,21 +1,66 @@
use std::iter;
use swc_atoms::JsWord;
use swc_common::{collections::AHashSet, util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
use swc_common::{
collections::{AHashMap, AHashSet},
util::take::Take,
Mark, Spanned, SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::*;
use swc_ecma_transforms_base::helper;
use swc_ecma_utils::{alias_ident_for, alias_if_required, prepend, quote_ident, ExprFactory};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
pub(super) struct BrandCheckHandler<'a> {
/// Mark for the private `WeakSet` variable.
pub(super) struct Private {
pub mark: Mark,
pub class_name: Ident,
pub ident: AHashMap<JsWord, PrivateKind>,
}
pub class_name: &'a Ident,
pub(super) struct PrivateRecord(Vec<Private>);
impl PrivateRecord {
pub fn new() -> Self {
PrivateRecord(Vec::new())
}
pub fn curr_class(&self) -> &Ident {
&self.0.last().unwrap().class_name
}
pub fn curr_mark(&self) -> Mark {
self.0.last().unwrap().mark
}
pub fn push(&mut self, p: Private) {
self.0.push(p)
}
pub fn pop(&mut self) {
self.0.pop();
}
pub fn get(&self, name: &JsWord) -> (Mark, PrivateKind, &Ident) {
for p in self.0.iter().rev() {
if let Some(kind) = p.ident.get(name) {
return (p.mark, kind.clone(), &p.class_name);
}
}
// TODO: better error information with span
panic!("Private name #{name} is not defined.");
}
}
#[derive(Clone, PartialEq)]
pub(super) struct PrivateKind {
pub is_static: bool,
pub is_method: bool,
}
pub(super) struct BrandCheckHandler<'a> {
/// Private names used for brand checks.
pub names: &'a mut AHashSet<JsWord>,
pub statics: &'a AHashSet<JsWord>,
pub private: &'a PrivateRecord,
}
impl VisitMut for BrandCheckHandler<'_> {
@ -31,20 +76,14 @@ impl VisitMut for BrandCheckHandler<'_> {
left,
right,
}) if left.is_private_name() => {
let n = match &**left {
Expr::PrivateName(ref n) => n,
_ => {
unreachable!()
}
};
let n = left.as_private_name().unwrap();
if let Expr::Ident(right) = &**right {
if self.class_name.sym == right.sym
&& self.class_name.span.ctxt == right.span.ctxt
{
let curr_class = self.private.curr_class();
if curr_class.sym == right.sym && curr_class.span.ctxt == right.span.ctxt {
*e = Expr::Bin(BinExpr {
span: *span,
op: op!("==="),
left: Box::new(Expr::Ident(self.class_name.clone())),
left: Box::new(Expr::Ident(curr_class.clone())),
right: Box::new(Expr::Ident(right.clone())),
});
return;
@ -53,22 +92,20 @@ impl VisitMut for BrandCheckHandler<'_> {
self.names.insert(n.id.sym.clone());
let is_static = self.statics.contains(&n.id.sym);
let (mark, kind, class_name) = self.private.get(&n.id.sym);
if is_static {
if kind.is_static {
*e = Expr::Bin(BinExpr {
span: *span,
op: op!("==="),
left: right.take(),
right: Box::new(Expr::Ident(self.class_name.clone())),
right: Box::new(Expr::Ident(class_name.clone())),
});
return;
}
let weak_coll_ident = Ident::new(
format!("_{}", n.id.sym).into(),
n.id.span.apply_mark(self.mark),
);
let weak_coll_ident =
Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
*e = Expr::Call(CallExpr {
span: *span,
@ -84,14 +121,8 @@ impl VisitMut for BrandCheckHandler<'_> {
}
pub(super) struct FieldAccessFolder<'a> {
/// Mark for the private `WeakSet` variable.
pub mark: Mark,
pub method_mark: Mark,
pub class_name: &'a Ident,
pub private_methods: &'a AHashSet<JsWord>,
pub vars: Vec<VarDeclarator>,
pub statics: &'a AHashSet<JsWord>,
pub private: &'a PrivateRecord,
pub in_assign_pat: bool,
}
@ -158,17 +189,14 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
let obj = arg.obj.clone();
let is_static = self.statics.contains(&n.id.sym);
let ident = Ident::new(
format!("_{}", n.id.sym).into(),
n.id.span.apply_mark(self.mark),
);
let (mark, kind, class_name) = self.private.get(&n.id.sym);
let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
let var = alias_ident_for(&obj, "_ref");
let this = if matches!(*obj, Expr::This(..)) {
ThisExpr { span: DUMMY_SP }.as_arg()
} else if is_static {
} else if kind.is_static {
obj.as_arg()
} else {
self.vars.push(VarDeclarator {
@ -234,19 +262,14 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
.as_arg()
};
let expr = if is_static {
let expr = if kind.is_static {
Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(
class_static_private_field_spec_set,
"classStaticPrivateFieldSpecSet"
),
args: vec![
this,
self.class_name.clone().as_arg(),
ident.as_arg(),
value,
],
args: vec![this, class_name.clone().as_arg(), ident.as_arg(), value],
type_args: Default::default(),
})
@ -297,11 +320,8 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
let obj = left.obj.clone();
let is_static = self.statics.contains(&n.id.sym);
let ident = Ident::new(
format!("_{}", n.id.sym).into(),
n.id.span.apply_mark(self.mark),
);
let (mark, kind, class_name) = self.private.get(&n.id.sym);
let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
let var = alias_ident_for(&obj, "_ref");
@ -339,19 +359,14 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
.as_arg()
};
if is_static {
if kind.is_static {
*e = Expr::Call(CallExpr {
span: DUMMY_SP,
callee: helper!(
class_static_private_field_spec_set,
"classStaticPrivateFieldSpecSet"
),
args: vec![
this,
self.class_name.clone().as_arg(),
ident.as_arg(),
value,
],
args: vec![this, class_name.clone().as_arg(), ident.as_arg(), value],
type_args: Default::default(),
});
@ -488,21 +503,15 @@ impl<'a> FieldAccessFolder<'a> {
let mut obj = e.obj.take();
let is_method = self.private_methods.contains(&n.id.sym);
let is_static = self.statics.contains(&n.id.sym);
let (mark, kind, class_name) = self.private.get(&n.id.sym);
let method_name = Ident::new(
n.id.sym.clone(),
n.id.span
.with_ctxt(SyntaxContext::empty())
.apply_mark(self.method_mark),
);
let ident = Ident::new(
format!("_{}", n.id.sym).into(),
n.id.span.apply_mark(self.mark),
n.id.span.with_ctxt(SyntaxContext::empty()).apply_mark(mark),
);
let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
if is_static {
if is_method {
if kind.is_static {
if kind.is_method {
let h = helper!(
class_static_private_method_get,
"classStaticPrivateMethodGet"
@ -514,12 +523,12 @@ impl<'a> FieldAccessFolder<'a> {
callee: h,
args: vec![
obj.as_arg(),
self.class_name.clone().as_arg(),
class_name.clone().as_arg(),
method_name.as_arg(),
],
type_args: Default::default(),
}),
Some(Expr::Ident(self.class_name.clone())),
Some(Expr::Ident(class_name.clone())),
);
}
@ -532,14 +541,10 @@ impl<'a> FieldAccessFolder<'a> {
Expr::Call(CallExpr {
span: DUMMY_SP,
callee: get,
args: vec![
obj.as_arg(),
self.class_name.clone().as_arg(),
ident.as_arg(),
],
args: vec![obj.as_arg(), class_name.clone().as_arg(), ident.as_arg()],
type_args: Default::default(),
}),
Some(Expr::Ident(self.class_name.clone())),
Some(Expr::Ident(class_name.clone())),
)
} else {
if self.in_assign_pat {
@ -564,7 +569,7 @@ impl<'a> FieldAccessFolder<'a> {
};
}
let get = if is_method {
let get = if kind.is_method {
helper!(class_private_method_get, "classPrivateMethodGet")
} else {
helper!(class_private_field_get, "classPrivateFieldGet")
@ -572,7 +577,7 @@ impl<'a> FieldAccessFolder<'a> {
match &*obj {
Expr::This(this) => (
if is_method {
if kind.is_method {
CallExpr {
span: DUMMY_SP,
callee: get,
@ -622,7 +627,7 @@ impl<'a> FieldAccessFolder<'a> {
var.clone().as_arg()
};
let args = if is_method {
let args = if kind.is_method {
vec![first_arg, ident.as_arg(), method_name.as_arg()]
} else {
vec![first_arg, ident.as_arg()]

View File

@ -4930,6 +4930,8 @@ test!(
issue_1333_1,
"
class Foo {
#ws;
#ws2;
get connected() {
return this.#ws2 && this.#ws.readyState === _ws1.default.OPEN;
}
@ -4941,7 +4943,19 @@ test!(
return _classPrivateFieldGet(this, _ws2) && _classPrivateFieldGet(this, _ws).readyState \
=== _ws1.default.OPEN;
}
constructor(){
_ws.set(this, {
writable: true,
value: void 0
});
_ws2.set(this, {
writable: true,
value: void 0
});
}
}
var _ws = new WeakMap();
var _ws2 = new WeakMap();
"
);
@ -4952,6 +4966,8 @@ test!(
"
class Test {
#ws;
#serialization;
#seq;
_packet(raw) {
/** @type {DiscordPacket} */
@ -5112,9 +5128,19 @@ test!(
writable: true,
value: void 0
});
_serialization.set(this, {
writable: true,
value: void 0
});
_seq.set(this, {
writable: true,
value: void 0
});
}
}
var _ws = new WeakMap();
var _serialization = new WeakMap();
var _seq = new WeakMap();
"
);
@ -5125,6 +5151,7 @@ test!(
"
class Test {
#ws;
#serialization;
_packet(raw) {
/** @type {DiscordPacket} */
@ -5165,10 +5192,14 @@ test!(
writable: true,
value: void 0
});
_serialization.set(this, {
writable: true,
value: void 0
});
}
}
var _ws = new WeakMap();
var _serialization = new WeakMap();
"
);
@ -5179,6 +5210,7 @@ test!(
"
class Test {
#ws;
#serialization;
_packet(raw) {
/** @type {DiscordPacket} */
@ -5206,9 +5238,14 @@ test!(
writable: true,
value: void 0
});
_serialization.set(this, {
writable: true,
value: void 0
});
}
}
var _ws = new WeakMap();
var _serialization = new WeakMap();
"
);
@ -5218,6 +5255,7 @@ test!(
issue_1333_5,
"
class Test {
#serialization;
_packet(raw) {
pak = this.#serialization.decode(raw);
}
@ -5228,7 +5266,14 @@ test!(
_packet(raw) {
pak = _classPrivateFieldGet(this, _serialization).decode(raw);
}
constructor(){
_serialization.set(this, {
writable: true,
value: void 0
});
}
}
var _serialization = new WeakMap();
"
);
@ -5238,6 +5283,7 @@ test!(
issue_1333_6,
"
class Test {
#serialization;
_packet(raw) {
this.#serialization.decode(raw);
}
@ -5248,7 +5294,14 @@ test!(
_packet(raw) {
_classPrivateFieldGet(this, _serialization).decode(raw);
}
constructor(){
_serialization.set(this, {
writable: true,
value: void 0
});
}
}
var _serialization = new WeakMap();
"
);
@ -5638,6 +5691,7 @@ test!(
issue_3229_1,
"
class A {
#D;
B() {
1;
C.#D++;
@ -5653,7 +5707,14 @@ class A {
_classPrivateFieldSet(_C = C, _D, (_this_D = +_classPrivateFieldGet(_C, _D)) + 1), _this_D;
E(function() {});
}
constructor(){
_D.set(this, {
writable: true,
value: void 0
});
}
}
var _D = new WeakMap();
"
);
@ -5663,6 +5724,7 @@ test!(
issue_3229_2,
"
class A {
#b;
foo() {
A.#b += 123
class B {
@ -5680,9 +5742,96 @@ class A {
foo() {}
}
}
constructor(){
_b.set(this, {
writable: true,
value: void 0
});
}
}
var _b = new WeakMap();
"
);
test!(
syntax(),
|_| class_properties(class_properties::Config { loose: false }),
issue_3368,
"
class A {
#a = 'fff'
static #b = 123
foo() {
return class B {
bar() {
console.log(this.#a, this.#b, this.#bar)
}
}
}
#bar() {}
}
",
"
var _bar = new WeakSet();
class A {
foo() {
return class B {
bar() {
console.log(_classPrivateFieldGet(this, _a), _classStaticPrivateFieldSpecGet(this, \
A, _b), _classPrivateMethodGet(this, _bar, bar));
}
};
}
constructor(){
_a.set(this, {
writable: true,
value: 'fff'
});
_bar.add(this);
}
}
var _a = new WeakMap();
var _b = {
writable: true,
value: 123
};
function bar() {}
"
);
test!(
syntax(),
|_| class_properties(class_properties::Config { loose: false }),
nested_class_in_arrow,
"
const a = () => class {
a = 123
foo() {
return class B {
b = 456
}
}
}
",
"
const a = ()=>{
class _class {
foo() {
return class B {
constructor() {
_defineProperty(this, 'b', 456);
}
};
}
constructor(){
_defineProperty(this, 'a', 123);
}
}
return _class;
};
"
);
#[testing::fixture("tests/fixture/classes/**/exec.js")]
fn exec(input: PathBuf) {
let src = read_to_string(&input).unwrap();

View File

@ -2,7 +2,7 @@ let Foo = function() {
"use strict";
function Foo() {
_classCallCheck(this, Foo);
_foo1.set(this, {
_foo.set(this, {
writable: true,
value: 1
});
@ -35,12 +35,12 @@ let Foo = function() {
return Nested;
}();
var _bar1 = new WeakMap();
_foo1.has(this);
_foo.has(this);
_bar.has(this);
}
}
]);
return Foo;
}();
var _foo1 = new WeakMap();
var _foo = new WeakMap();
var _bar = new WeakMap();

View File

@ -2,7 +2,7 @@ let Foo = function() {
"use strict";
function Foo() {
_classCallCheck(this, Foo);
_foo1.set(this, {
_foo.set(this, {
writable: true,
value: 1
});
@ -25,10 +25,10 @@ let Foo = function() {
]);
return Nested;
}();
_foo1.has(this);
_foo.has(this);
}
}
]);
return Foo;
}();
var _foo1 = new WeakMap();
var _foo = new WeakMap();