diff --git a/crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/.get-only-setter/exec.js b/crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/get-only-setter/exec.js similarity index 100% rename from crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/.get-only-setter/exec.js rename to crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/get-only-setter/exec.js diff --git a/crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/.reassignment/exec.js b/crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/reassignment/exec.js similarity index 100% rename from crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/.reassignment/exec.js rename to crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/reassignment/exec.js diff --git a/crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/.set-only-getter/exec.js b/crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/set-only-getter/exec.js similarity index 100% rename from crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/.set-only-getter/exec.js rename to crates/swc/tests/babel-exec/packages/babel-plugin-proposal-private-methods/test/fixtures/accessors/set-only-getter/exec.js diff --git a/crates/swc/tests/exec.rs b/crates/swc/tests/exec.rs index ea38d5da0bfa..44713de26d9a 100644 --- a/crates/swc/tests/exec.rs +++ b/crates/swc/tests/exec.rs @@ -57,7 +57,7 @@ fn init_helpers() -> Arc { let helper_dir = project_root.join("packages").join("swc-helpers"); let yarn = find_executable("yarn").expect("failed to find yarn"); - let npm = find_executable("npm").expect("failed to find yarn"); + let npm = find_executable("npm").expect("failed to find npm"); { let mut cmd = if cfg!(target_os = "windows") { let mut c = Command::new("cmd"); diff --git a/crates/swc/tests/tsc-references/privateNameAccessors.1.normal.js b/crates/swc/tests/tsc-references/privateNameAccessors.1.normal.js index 07cf90c781ff..ccf844eb755e 100644 --- a/crates/swc/tests/tsc-references/privateNameAccessors.1.normal.js +++ b/crates/swc/tests/tsc-references/privateNameAccessors.1.normal.js @@ -2,6 +2,7 @@ import _class_private_field_get from "@swc/helpers/src/_class_private_field_get.mjs"; import _class_private_field_init from "@swc/helpers/src/_class_private_field_init.mjs"; import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; +import _read_only_error from "@swc/helpers/src/_read_only_error.mjs"; var _prop = /*#__PURE__*/ new WeakMap(), _roProp = /*#__PURE__*/ new WeakMap(); class A1 { constructor(name){ @@ -14,7 +15,7 @@ class A1 { set: void 0 }); _class_private_field_set(this, _prop, ""); - _class_private_field_set(this, _roProp, ""); // Error + this, _read_only_error("#roProp"); // Error console.log(_class_private_field_get(this, _prop)); console.log(_class_private_field_get(this, _roProp)); } diff --git a/crates/swc/tests/tsc-references/privateNameMethodAssignment.1.normal.js b/crates/swc/tests/tsc-references/privateNameMethodAssignment.1.normal.js index 03a7577520f7..d1c5e1813c4f 100644 --- a/crates/swc/tests/tsc-references/privateNameMethodAssignment.1.normal.js +++ b/crates/swc/tests/tsc-references/privateNameMethodAssignment.1.normal.js @@ -1,18 +1,18 @@ //// [privateNameMethodAssignment.ts] -import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; import _class_private_field_update from "@swc/helpers/src/_class_private_field_update.mjs"; import _class_private_method_get from "@swc/helpers/src/_class_private_method_get.mjs"; import _class_private_method_init from "@swc/helpers/src/_class_private_method_init.mjs"; +import _read_only_error from "@swc/helpers/src/_read_only_error.mjs"; import _class_private_field_destructure from "@swc/helpers/src/_class_private_field_destructure.mjs"; var _method = /*#__PURE__*/ new WeakSet(); class A3 { constructor(a, b){ _class_private_method_init(this, _method); - _class_private_field_set(this, _method, ()=>{} // Error, not writable - ); - _class_private_field_set(a, _method, ()=>{}); // Error, not writable - _class_private_field_set(b, _method, ()=>{} //Error, not writable - ); + this, _read_only_error("#method") // Error, not writable + ; + a, _read_only_error("#method"); // Error, not writable + b, _read_only_error("#method") //Error, not writable + ; ({ x: _class_private_field_destructure(this, _method).value } = { x: ()=>{} }); //Error, not writable diff --git a/crates/swc/tests/tsc-references/privateNameReadonly.1.normal.js b/crates/swc/tests/tsc-references/privateNameReadonly.1.normal.js index 51a52d40cfeb..fe0e31874c75 100644 --- a/crates/swc/tests/tsc-references/privateNameReadonly.1.normal.js +++ b/crates/swc/tests/tsc-references/privateNameReadonly.1.normal.js @@ -1,10 +1,10 @@ //// [privateNameReadonly.ts] -import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; import _class_private_method_init from "@swc/helpers/src/_class_private_method_init.mjs"; +import _read_only_error from "@swc/helpers/src/_read_only_error.mjs"; var _bar, _class; const C = (_bar = /*#__PURE__*/ new WeakSet(), _class = class { foo() { - _class_private_field_set(this, _bar, console.log("should log this then throw")); + this, console.log("should log this then throw"), _read_only_error("#bar"); } constructor(){ _class_private_method_init(this, _bar); diff --git a/crates/swc/tests/tsc-references/privateNameReadonly.2.minified.js b/crates/swc/tests/tsc-references/privateNameReadonly.2.minified.js index 8daf07be51ca..c785fbc02f3f 100644 --- a/crates/swc/tests/tsc-references/privateNameReadonly.2.minified.js +++ b/crates/swc/tests/tsc-references/privateNameReadonly.2.minified.js @@ -1,10 +1,10 @@ //// [privateNameReadonly.ts] var _bar; -import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; import _class_private_method_init from "@swc/helpers/src/_class_private_method_init.mjs"; +import _read_only_error from "@swc/helpers/src/_read_only_error.mjs"; let C = (_bar = new WeakSet(), class { foo() { - _class_private_field_set(this, _bar, console.log("should log this then throw")); + console.log("should log this then throw"), _read_only_error("#bar"); } constructor(){ _class_private_method_init(this, _bar); diff --git a/crates/swc/tests/tsc-references/privateNameSetterNoGetter.1.normal.js b/crates/swc/tests/tsc-references/privateNameSetterNoGetter.1.normal.js index 5c271e349cd6..0256bb2e1b3d 100644 --- a/crates/swc/tests/tsc-references/privateNameSetterNoGetter.1.normal.js +++ b/crates/swc/tests/tsc-references/privateNameSetterNoGetter.1.normal.js @@ -1,11 +1,11 @@ //// [privateNameSetterNoGetter.ts] -import _class_private_field_get from "@swc/helpers/src/_class_private_field_get.mjs"; import _class_private_field_init from "@swc/helpers/src/_class_private_field_init.mjs"; import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; +import _write_only_error from "@swc/helpers/src/_write_only_error.mjs"; var _x, _class; const C = (_x = /*#__PURE__*/ new WeakMap(), _class = class { m() { - _class_private_field_set(this, _x, _class_private_field_get(this, _x) + 2); // Error + _class_private_field_set(this, _x, (this, _write_only_error("#x")) + 2); // Error } constructor(){ _class_private_field_init(this, _x, { diff --git a/crates/swc/tests/tsc-references/privateNameSetterNoGetter.2.minified.js b/crates/swc/tests/tsc-references/privateNameSetterNoGetter.2.minified.js index 0defd0f8eff9..e792162bda9b 100644 --- a/crates/swc/tests/tsc-references/privateNameSetterNoGetter.2.minified.js +++ b/crates/swc/tests/tsc-references/privateNameSetterNoGetter.2.minified.js @@ -1,11 +1,11 @@ //// [privateNameSetterNoGetter.ts] var _x; -import _class_private_field_get from "@swc/helpers/src/_class_private_field_get.mjs"; import _class_private_field_init from "@swc/helpers/src/_class_private_field_init.mjs"; import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; +import _write_only_error from "@swc/helpers/src/_write_only_error.mjs"; let C = (_x = new WeakMap(), class { m() { - _class_private_field_set(this, _x, _class_private_field_get(this, _x) + 2); + _class_private_field_set(this, _x, _write_only_error("#x") + 2); } constructor(){ _class_private_field_init(this, _x, { diff --git a/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.1.normal.js b/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.1.normal.js index 42587975c49d..a4572599ed9a 100644 --- a/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.1.normal.js +++ b/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.1.normal.js @@ -1,8 +1,8 @@ //// [privateWriteOnlyAccessorRead.ts] -import _class_private_field_get from "@swc/helpers/src/_class_private_field_get.mjs"; import _class_private_field_init from "@swc/helpers/src/_class_private_field_init.mjs"; import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; import _extends from "@swc/helpers/src/_extends.mjs"; +import _write_only_error from "@swc/helpers/src/_write_only_error.mjs"; import _class_private_field_destructure from "@swc/helpers/src/_class_private_field_destructure.mjs"; var _value = /*#__PURE__*/ new WeakMap(), _valueRest = /*#__PURE__*/ new WeakMap(), _valueOne = /*#__PURE__*/ new WeakMap(), _valueCompound = /*#__PURE__*/ new WeakMap(); class Test { @@ -10,14 +10,14 @@ class Test { const foo = { bar: 1 }; - console.log(_class_private_field_get(this, _value)); // error + console.log((this, _write_only_error("#value"))); // error _class_private_field_set(this, _value, { foo }); // ok _class_private_field_set(this, _value, { foo }); // ok - _class_private_field_get(this, _value).foo = foo; // error + (this, _write_only_error("#value")).foo = foo; // error ({ o: _class_private_field_destructure(this, _value).value } = { o: { foo @@ -27,15 +27,15 @@ class Test { _tmp = { foo }, _class_private_field_destructure(this, _value).value = _extends({}, _tmp), _tmp; //ok - ({ foo: _class_private_field_get(this, _value).foo } = { + ({ foo: (this, _write_only_error("#value")).foo } = { foo }); //error var _tmp1; _tmp1 = { foo - }, _class_private_field_get(this, _value).foo = _extends({}, _tmp1.foo), ({ foo: {} } = _tmp1), _tmp1; //error + }, (this, _write_only_error("#value")).foo = _extends({}, _tmp1.foo), ({ foo: {} } = _tmp1), _tmp1; //error let r = { - o: _class_private_field_get(this, _value) + o: (this, _write_only_error("#value")) }; //error [_class_private_field_destructure(this, _valueOne).value, ..._class_private_field_destructure(this, _valueRest).value] = [ 1, @@ -43,10 +43,10 @@ class Test { 3 ]; let arr = [ - _class_private_field_get(this, _valueOne), - ..._class_private_field_get(this, _valueRest) + (this, _write_only_error("#valueOne")), + ...(this, _write_only_error("#valueRest")) ]; - _class_private_field_set(this, _valueCompound, _class_private_field_get(this, _valueCompound) + 3); + _class_private_field_set(this, _valueCompound, (this, _write_only_error("#valueCompound")) + 3); } constructor(){ _class_private_field_init(this, _value, { diff --git a/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.2.minified.js b/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.2.minified.js index 304196d3bd2c..ca07dfdb8b7b 100644 --- a/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.2.minified.js +++ b/crates/swc/tests/tsc-references/privateWriteOnlyAccessorRead.2.minified.js @@ -1,8 +1,8 @@ //// [privateWriteOnlyAccessorRead.ts] -import _class_private_field_get from "@swc/helpers/src/_class_private_field_get.mjs"; import _class_private_field_init from "@swc/helpers/src/_class_private_field_init.mjs"; import _class_private_field_set from "@swc/helpers/src/_class_private_field_set.mjs"; import _extends from "@swc/helpers/src/_extends.mjs"; +import _write_only_error from "@swc/helpers/src/_write_only_error.mjs"; import _class_private_field_destructure from "@swc/helpers/src/_class_private_field_destructure.mjs"; var _value = new WeakMap(), _valueRest = new WeakMap(), _valueOne = new WeakMap(), _valueCompound = new WeakMap(); function set_value(v) {} @@ -15,28 +15,28 @@ new class { let foo = { bar: 1 }; - console.log(_class_private_field_get(this, _value)), _class_private_field_set(this, _value, { + console.log(_write_only_error("#value")), _class_private_field_set(this, _value, { foo }), _class_private_field_set(this, _value, { foo - }), _class_private_field_get(this, _value).foo = foo, ({ o: _class_private_field_destructure(this, _value).value } = { + }), _write_only_error("#value").foo = foo, ({ o: _class_private_field_destructure(this, _value).value } = { o: { foo } }), _class_private_field_destructure(this, _value).value = _extends({}, { foo - }), ({ foo: _class_private_field_get(this, _value).foo } = { + }), ({ foo: _write_only_error("#value").foo } = { foo }), _tmp = { foo - }, _class_private_field_get(this, _value).foo = _extends({}, _tmp.foo), _class_private_field_get(this, _value), [_class_private_field_destructure(this, _valueOne).value, ..._class_private_field_destructure(this, _valueRest).value] = [ + }, _write_only_error("#value").foo = _extends({}, _tmp.foo), _write_only_error("#value"), [_class_private_field_destructure(this, _valueOne).value, ..._class_private_field_destructure(this, _valueRest).value] = [ 1, 2, 3 ], [ - _class_private_field_get(this, _valueOne), - ..._class_private_field_get(this, _valueRest) - ], _class_private_field_set(this, _valueCompound, _class_private_field_get(this, _valueCompound) + 3); + _write_only_error("#valueOne"), + ..._write_only_error("#valueRest") + ], _class_private_field_set(this, _valueCompound, _write_only_error("#valueCompound") + 3); } constructor(){ _class_private_field_init(this, _value, { diff --git a/crates/swc_ecma_transforms_base/src/helpers/_class_apply_descriptor_update.js b/crates/swc_ecma_transforms_base/src/helpers/_class_apply_descriptor_update.js index db089b33ee2c..30c007f08f2d 100644 --- a/crates/swc_ecma_transforms_base/src/helpers/_class_apply_descriptor_update.js +++ b/crates/swc_ecma_transforms_base/src/helpers/_class_apply_descriptor_update.js @@ -1,5 +1,8 @@ function _classApplyDescriptorUpdate(receiver, descriptor) { if (descriptor.set) { + if (!descriptor.get) { + throw new TypeError("attempted to read set only private field"); + } if (!("__destrWrapper" in descriptor)) { descriptor.__destrWrapper = { set value(v) { diff --git a/crates/swc_ecma_transforms_base/src/helpers/_read_only_error.js b/crates/swc_ecma_transforms_base/src/helpers/_read_only_error.js index 465c7c3bd799..0ac70dd5e1d2 100644 --- a/crates/swc_ecma_transforms_base/src/helpers/_read_only_error.js +++ b/crates/swc_ecma_transforms_base/src/helpers/_read_only_error.js @@ -1,3 +1,3 @@ function _readOnlyError(name) { - throw new Error("\"" + name + "\" is read-only"); + throw new TypeError("\"" + name + "\" is read-only"); } diff --git a/crates/swc_ecma_transforms_base/src/helpers/_write_only_error.js b/crates/swc_ecma_transforms_base/src/helpers/_write_only_error.js new file mode 100644 index 000000000000..aa16305ca053 --- /dev/null +++ b/crates/swc_ecma_transforms_base/src/helpers/_write_only_error.js @@ -0,0 +1,3 @@ +function _writeOnlyError(name) { + throw new TypeError("\"" + name + "\" is write-only"); +} diff --git a/crates/swc_ecma_transforms_base/src/helpers/mod.rs b/crates/swc_ecma_transforms_base/src/helpers/mod.rs index a9bb77eab168..4a31fe7bc449 100644 --- a/crates/swc_ecma_transforms_base/src/helpers/mod.rs +++ b/crates/swc_ecma_transforms_base/src/helpers/mod.rs @@ -334,6 +334,7 @@ define_helpers!(Helpers { set_prototype_of, is_native_function ), + write_only_error: (), class_private_field_destructure: ( class_extract_field_descriptor, diff --git a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs index d041e33d878c..0181dbfc9054 100644 --- a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs +++ b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs @@ -70,6 +70,15 @@ pub(super) struct PrivateKind { } impl PrivateKind { + fn is_readonly(&self) -> bool { + self.is_method && !self.has_setter + } + + fn is_writeonly(&self) -> bool { + // a private method can still be read + self.is_method && !self.has_getter && self.has_setter + } + fn is_method(&self) -> bool { self.is_method && !self.has_getter && !self.has_setter } @@ -274,9 +283,9 @@ impl<'a> VisitMut for PrivateAccessVisitor<'a> { let var = alias_ident_for(&obj, "_ref"); let this = if matches!(*obj, Expr::This(..)) { - ThisExpr { span: DUMMY_SP }.as_arg() + Box::new(ThisExpr { span: DUMMY_SP }.into()) } else if *op == op!("=") { - obj.as_arg() + obj } else { self.vars.push(VarDeclarator { span: DUMMY_SP, @@ -284,27 +293,31 @@ impl<'a> VisitMut for PrivateAccessVisitor<'a> { init: None, definite: false, }); - AssignExpr { - span: obj.span(), - left: PatOrExpr::Pat(var.clone().into()), - op: op!("="), - right: obj, - } - .as_arg() + Box::new( + AssignExpr { + span: obj.span(), + left: PatOrExpr::Pat(var.clone().into()), + op: op!("="), + right: obj, + } + .into(), + ) }; let value = if *op == op!("=") { - right.take().as_arg() + right.take() } else { let left = Box::new(self.visit_mut_private_get(&mut left, Some(var)).0); - BinExpr { - span: DUMMY_SP, - left, - op: op.to_update().unwrap(), - right: right.take(), - } - .as_arg() + Box::new( + BinExpr { + span: DUMMY_SP, + left, + op: op.to_update().unwrap(), + right: right.take(), + } + .into(), + ) }; if kind.is_static { @@ -314,17 +327,34 @@ impl<'a> VisitMut for PrivateAccessVisitor<'a> { class_static_private_field_spec_set, "classStaticPrivateFieldSpecSet" ), - args: vec![this, class_name.clone().as_arg(), ident.as_arg(), value], + args: vec![ + this.as_arg(), + class_name.clone().as_arg(), + ident.as_arg(), + value.as_arg(), + ], type_args: Default::default(), }); + } else if kind.is_readonly() { + let err = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: helper!(read_only_error, "readOnlyError"), + args: vec![format!("#{}", n.id.sym).as_arg()], + type_args: None, + }) + .into(); + *e = Expr::Seq(SeqExpr { + span: *span, + exprs: vec![this, value, err], + }); } else { let set = helper!(class_private_field_set, "classPrivateFieldSet"); *e = Expr::Call(CallExpr { span: DUMMY_SP, callee: set, - args: vec![this, ident.as_arg(), value], + args: vec![this.as_arg(), ident.as_arg(), value.as_arg()], type_args: Default::default(), }); @@ -544,6 +574,8 @@ impl<'a> PrivateAccessVisitor<'a> { return (Expr::dummy(), None); } + if kind.is_writeonly() {} + let method_name = Ident::new( n.id.sym.clone(), n.id.span.with_ctxt(SyntaxContext::empty()).apply_mark(mark), @@ -642,7 +674,7 @@ impl<'a> PrivateAccessVisitor<'a> { "classPrivateFieldDestructureSet" ); - return ( + ( CallExpr { span: DUMMY_SP, callee: set, @@ -652,12 +684,12 @@ impl<'a> PrivateAccessVisitor<'a> { } .make_member(quote_ident!("value")), Some(*obj), - ); + ) } PrivateAccessType::Update => { let set = helper!(class_private_field_update, "classPrivateFieldUpdate"); - return ( + ( CallExpr { span: DUMMY_SP, callee: set, @@ -667,87 +699,113 @@ impl<'a> PrivateAccessVisitor<'a> { } .make_member(quote_ident!("value")), Some(*obj), - ); + ) } - _ => {} - } - - let get = if self.c.private_as_properties { - helper!(class_private_field_loose_base, "classPrivateFieldLooseBase") - } else if kind.is_method() { - helper!(class_private_method_get, "classPrivateMethodGet") - } else { - helper!(class_private_field_get, "classPrivateFieldGet") - }; - match &*obj { - Expr::This(this) => ( - if kind.is_method() && !self.c.private_as_properties { + PrivateAccessType::Get if kind.is_writeonly() => { + let helper = helper!(write_only_error, "writeOnlyError"); + let expr = Box::new( CallExpr { span: DUMMY_SP, - callee: get, - args: vec![obj.clone().as_arg(), ident.as_arg(), method_name.as_arg()], - type_args: Default::default(), + callee: helper, + args: vec![format!("#{}", n.id.sym).as_arg()], + type_args: None, } - .into() - } else { - CallExpr { + .into(), + ); + ( + SeqExpr { span: DUMMY_SP, - callee: get, - args: vec![this.as_arg(), ident.as_arg()], - - type_args: Default::default(), - } - .into() - }, - Some(Expr::This(*this)), - ), - _ => { - let mut aliased = false; - let var = obj_alias.unwrap_or_else(|| { - let (var, a) = alias_if_required(&obj, "_ref"); - if a { - aliased = true; - self.vars.push(VarDeclarator { - span: DUMMY_SP, - name: var.clone().into(), - init: None, - definite: false, - }); + exprs: vec![obj.clone(), expr], } - var - }); + .into(), + Some(*obj), + ) + } - let first_arg = if is_alias_initialized { - var.clone().as_arg() - } else if aliased { - AssignExpr { - span: DUMMY_SP, - left: PatOrExpr::Pat(var.clone().into()), - op: op!("="), - right: obj.take(), - } - .as_arg() + PrivateAccessType::Get => { + let get = if self.c.private_as_properties { + helper!(class_private_field_loose_base, "classPrivateFieldLooseBase") + } else if kind.is_method() { + helper!(class_private_method_get, "classPrivateMethodGet") } else { - var.clone().as_arg() + helper!(class_private_field_get, "classPrivateFieldGet") }; - let args = if kind.is_method() { - vec![first_arg, ident.as_arg(), method_name.as_arg()] - } else { - vec![first_arg, ident.as_arg()] - }; + match &*obj { + Expr::This(this) => ( + if kind.is_method() && !self.c.private_as_properties { + CallExpr { + span: DUMMY_SP, + callee: get, + args: vec![ + obj.clone().as_arg(), + ident.as_arg(), + method_name.as_arg(), + ], + type_args: Default::default(), + } + .into() + } else { + CallExpr { + span: DUMMY_SP, + callee: get, + args: vec![this.as_arg(), ident.as_arg()], + + type_args: Default::default(), + } + .into() + }, + Some(Expr::This(*this)), + ), + _ => { + let mut aliased = false; + let var = obj_alias.unwrap_or_else(|| { + let (var, a) = alias_if_required(&obj, "_ref"); + if a { + aliased = true; + self.vars.push(VarDeclarator { + span: DUMMY_SP, + name: var.clone().into(), + init: None, + definite: false, + }); + } + var + }); - ( - CallExpr { - span: DUMMY_SP, - callee: get, - args, - type_args: Default::default(), + let first_arg = if is_alias_initialized { + var.clone().as_arg() + } else if aliased { + AssignExpr { + span: DUMMY_SP, + left: PatOrExpr::Pat(var.clone().into()), + op: op!("="), + right: obj.take(), + } + .as_arg() + } else { + var.clone().as_arg() + }; + + let args = if kind.is_method() { + vec![first_arg, ident.as_arg(), method_name.as_arg()] + } else { + vec![first_arg, ident.as_arg()] + }; + + ( + CallExpr { + span: DUMMY_SP, + callee: get, + args, + type_args: Default::default(), + } + .into(), + Some(Expr::Ident(var)), + ) } - .into(), - Some(Expr::Ident(var)), - ) + } } } } diff --git a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs index 15a508729eb1..9afd597dff44 100644 --- a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs +++ b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs @@ -6549,6 +6549,100 @@ function set_privateFieldValue(newValue) { "# ); +test!( + syntax(), + |t| class_properties(Some(t.comments.clone()), Default::default()), + set_only_getter, + r#" +class Cl { + #privateField = 0; + counter = 0; + + get #privateFieldValue() { + return this.#privateField; + } + + get self() { + this.counter++; + return this; + } + + constructor() { + this.self.#privateFieldValue = 1; + ([this.self.#privateFieldValue] = [1]); + } +} + +const cl = new Cl(); +"#, + r##" +var _privateField = new WeakMap(), _privateFieldValue = new WeakMap(); +class Cl { + get self() { + this.counter++; + return this; + } + constructor(){ + _classPrivateFieldInit(this, _privateFieldValue, { + get: get_privateFieldValue, + set: void 0 + }); + _classPrivateFieldInit(this, _privateField, { + writable: true, + value: 0 + }); + _defineProperty(this, "counter", 0); + this.self, _readOnlyError("#privateFieldValue"); + [_classPrivateFieldDestructureSet(this.self, _privateFieldValue).value] = [ + 1 + ]; + } +} +function get_privateFieldValue() { + return _classPrivateFieldGet(this, _privateField); +} +const cl = new Cl(); +"## +); + +test!( + syntax(), + |t| class_properties(Some(t.comments.clone()), Default::default()), + get_only_setter, + r#" +class Cl { + #privateField = 0; + + set #privateFieldValue(newValue) { + this.#privateField = newValue; + } + + constructor() { + this.publicField = this.#privateFieldValue; + } +} +"#, + r##" +var _privateField = new WeakMap(), _privateFieldValue = new WeakMap(); +class Cl { + constructor(){ + _classPrivateFieldInit(this, _privateFieldValue, { + get: void 0, + set: set_privateFieldValue + }); + _classPrivateFieldInit(this, _privateField, { + writable: true, + value: 0 + }); + this.publicField = (this, _writeOnlyError("#privateFieldValue")); + } +} +function set_privateFieldValue(newValue) { + _classPrivateFieldSet(this, _privateField, newValue); +} +"## +); + #[testing::fixture("tests/classes/**/exec.js")] fn exec(input: PathBuf) { let src = read_to_string(&input).unwrap(); diff --git a/package.json b/package.json index 8e7d9eafe3d7..054a6fe9685a 100644 --- a/package.json +++ b/package.json @@ -178,6 +178,5 @@ "types.js", "postinstall.js", "bindings/binding_core_wasm/pkg/binding_core_wasm.d.ts" - ], - "packageManager": "yarn@3.2.4" + ] } diff --git a/packages/swc-helpers/src/_class_apply_descriptor_update.mjs b/packages/swc-helpers/src/_class_apply_descriptor_update.mjs index 67385d05afc1..082d460e8025 100644 --- a/packages/swc-helpers/src/_class_apply_descriptor_update.mjs +++ b/packages/swc-helpers/src/_class_apply_descriptor_update.mjs @@ -1,5 +1,8 @@ export default function _classApplyDescriptorUpdate(receiver, descriptor) { if (descriptor.set) { + if (!descriptor.get) { + throw new TypeError("attempted to read set only private field"); + } if (!("__destrWrapper" in descriptor)) { descriptor.__destrWrapper = { set value(v) { diff --git a/packages/swc-helpers/src/_read_only_error.mjs b/packages/swc-helpers/src/_read_only_error.mjs index b7ab3308b7a6..f98941b69ca3 100644 --- a/packages/swc-helpers/src/_read_only_error.mjs +++ b/packages/swc-helpers/src/_read_only_error.mjs @@ -1,3 +1,3 @@ export default function _readOnlyError(name) { - throw new Error("\"" + name + "\" is read-only"); + throw new TypeError("\"" + name + "\" is read-only"); } diff --git a/packages/swc-helpers/src/_write_only_error.mjs b/packages/swc-helpers/src/_write_only_error.mjs new file mode 100644 index 000000000000..6e5c8927e62f --- /dev/null +++ b/packages/swc-helpers/src/_write_only_error.mjs @@ -0,0 +1,3 @@ +export default function _writeOnlyError(name) { + throw new TypeError("\"" + name + "\" is write-only"); +}