mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 17:54:15 +03:00
fix(es/compat): Handle private fields in nested classes (#3431)
This commit is contained in:
parent
2ea81db19f
commit
01500a54e0
@ -1,4 +1,6 @@
|
||||
class Foo {
|
||||
#ws;
|
||||
#ws2;
|
||||
get connected() {
|
||||
return this.#ws2 && this.#ws.readyState === _ws1.default.OPEN;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
@ -1 +0,0 @@
|
||||
new WeakMap();
|
@ -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();
|
@ -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();
|
@ -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";
|
@ -1,2 +0,0 @@
|
||||
var _key;
|
||||
new WeakMap(), _key = "#bar";
|
@ -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";
|
@ -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";
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
};
|
@ -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);
|
||||
};
|
@ -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")]
|
||||
|
@ -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 {
|
||||
|
@ -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()]
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user