diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index 2d308cb1a..9aee870d9 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -611,6 +611,20 @@ impl<'a> AstBuilder<'a> { }) } + pub fn private_in_expression( + &self, + span: Span, + left: PrivateIdentifier, + right: Expression<'a>, + ) -> Expression<'a> { + Expression::PrivateInExpression(self.alloc(PrivateInExpression { + span, + left, + operator: BinaryOperator::In, + right, + })) + } + pub fn private_field_expression( &self, span: Span, diff --git a/crates/oxc_transformer/src/proposals/decorators.rs b/crates/oxc_transformer/src/proposals/decorators.rs index e07d96131..ad37ec767 100644 --- a/crates/oxc_transformer/src/proposals/decorators.rs +++ b/crates/oxc_transformer/src/proposals/decorators.rs @@ -29,17 +29,30 @@ pub struct Decorators<'a> { bitflags! { #[derive(Debug, Clone, Copy)] pub struct DecoratorFlags: u8 { - const Field = 0; - const Accessor = 1; - const Method = 2; - const Getter = 3; - const Setter = 4; - const Static = 8; - const DecoratorsHaveThis = 16; + // flag is 0 + const Field = 1 << 0; + // flag is 1 + const Accessor = 1 << 1; + // flag is 2 + const Method = 1 << 2; + // flag is 3 + const Getter = 1 << 3; + // flag is 4 + const Setter = 1 << 4; + // flag is 8 + const Static = 1 << 5; + // flag is 16 + const DecoratorsHaveThis = 1 << 6; } } impl DecoratorFlags { + pub fn is_setter(self) -> bool { + self.contains(Self::Setter) + } + pub fn is_static(self) -> bool { + self.contains(Self::Static) + } pub fn get_flag_by_kind(kind: MethodDefinitionKind) -> Self { match kind { MethodDefinitionKind::Method => Self::Method, @@ -48,6 +61,28 @@ impl DecoratorFlags { MethodDefinitionKind::Constructor => unreachable!(), } } + pub fn to_value(self) -> u8 { + if self.contains(DecoratorFlags::DecoratorsHaveThis) { + return 16; + } + let mut value: u8 = 0; + if self.contains(DecoratorFlags::Accessor) { + value += 1; + } + if self.contains(DecoratorFlags::Method) { + value += 2; + } + if self.contains(DecoratorFlags::Getter) { + value += 3; + } + if self.contains(DecoratorFlags::Setter) { + value += 4; + } + if self.contains(DecoratorFlags::Static) { + value += 8; + } + value + } } #[derive(Debug, Clone, Copy, Default, Deserialize)] @@ -297,6 +332,8 @@ impl<'a> Decorators<'a> { let mut c_elements = self.ast.new_vec(); let mut e_elements = self.ast.new_vec(); + let mut private_in_expressions = self.ast.new_vec(); + let mut init_static_name = None; // insert member decorators @@ -393,9 +430,9 @@ impl<'a> Decorators<'a> { } else { is_proto = true; } - let mut flag = DecoratorFlags::get_flag_by_kind(def.kind).bits(); + let mut flag = DecoratorFlags::get_flag_by_kind(def.kind); if def.r#static { - flag += DecoratorFlags::Static.bits(); + flag |= DecoratorFlags::Static; } def.decorators.iter().for_each(|decorator| { @@ -412,6 +449,54 @@ impl<'a> Decorators<'a> { def.decorators.clear(); if def.key.is_private_identifier() { + { + if flag.is_setter() && !flag.is_static() { + // _ => #a in _; + private_in_expressions.push(self.ast.arrow_expression( + SPAN, + true, + false, + self.ast.formal_parameters( + SPAN, + FormalParameterKind::ArrowFormalParameters, + self.ast.new_vec_single(self.ast.formal_parameter( + SPAN, + self.ast.binding_pattern( + self.ast.binding_pattern_identifier( + BindingIdentifier::new(SPAN, "_".into()), + ), + None, + false, + ), + None, + false, + self.ast.new_vec(), + )), + None, + ), + self.ast.function_body( + SPAN, + self.ast.new_vec(), + self.ast.new_vec_single(self.ast.expression_statement( + SPAN, + self.ast.private_in_expression( + SPAN, + PrivateIdentifier::new( + SPAN, + def.key.private_name().unwrap(), + ), + self.ast.identifier_reference_expression( + IdentifierReference::new(SPAN, "_".into()), + ), + ), + )), + ), + None, + None, + )); + } + } + name = self.get_unique_name(&if def.computed { "init_computedKey".into() } else { @@ -496,7 +581,7 @@ impl<'a> Decorators<'a> { def.decorators.iter().for_each(|decorator| { member_decorators_vec.push(ArrayExpressionElement::Expression( - self.get_decorator_info(&def.key, None, flag.bits(), decorator), + self.get_decorator_info(&def.key, None, flag, decorator), )); }); def.decorators.clear(); @@ -630,6 +715,15 @@ impl<'a> Decorators<'a> { None, ))); arguments.push(class_decorators_argument); + if !private_in_expressions.is_empty() { + // classDecsHaveThis + arguments.push(Argument::Expression(self.ast.literal_number_expression( + // TODO: use correct number instead of `0` + self.ast.number_literal(SPAN, 0f64, "0", oxc_syntax::NumberBase::Decimal), + ))); + // instanceBrand + arguments.extend(private_in_expressions.into_iter().map(Argument::Expression)); + } let mut call_expr = self.ast.call_expression(SPAN, callee, arguments, false, None); @@ -754,7 +848,7 @@ impl<'a> Decorators<'a> { &self, key: &PropertyKey<'a>, value: Option>>, - flag: u8, + flag: DecoratorFlags, decorator: &Decorator<'a>, ) -> Expression<'a> { let name = key.name(); @@ -766,7 +860,7 @@ impl<'a> Decorators<'a> { self.ast.literal_number_expression(NumericLiteral::new( SPAN, 0f64, - self.ast.new_str(flag.to_string().as_str()), + self.ast.new_str(flag.to_value().to_string().as_str()), oxc_syntax::NumberBase::Decimal, )), )); diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 30f46b947..4153e54d1 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 344/1369 +Passed: 346/1369 # All Passed: * babel-plugin-transform-numeric-separator @@ -911,7 +911,7 @@ Passed: 344/1369 * spread-transform/transform-to-babel-extend/input.js * spread-transform/transform-to-object-assign/input.js -# babel-plugin-proposal-decorators (20/190) +# babel-plugin-proposal-decorators (22/190) * 2018-09-transformation/async-generator-method/input.js * 2018-09-transformation/class-decorators-yield-await/input.js * 2021-12-accessors/context-name/input.js @@ -1033,7 +1033,6 @@ Passed: 344/1369 * 2023-05-getters--to-es2015/public/input.js * 2023-05-getters--to-es2015/static-private/input.js * 2023-05-getters--to-es2015/static-public/input.js -* 2023-05-getters-and-setters/private/input.js * 2023-05-getters-and-setters--to-es2015/private/input.js * 2023-05-getters-and-setters--to-es2015/public/input.js * 2023-05-getters-and-setters--to-es2015/static-private/input.js @@ -1071,7 +1070,6 @@ Passed: 344/1369 * 2023-05-ordering/initializers-and-static-blocks/input.js * 2023-05-ordering--to-es2015/initializers-and-static-blocks/input.js * 2023-05-setters/context-name/input.js -* 2023-05-setters/private/input.js * 2023-05-setters--to-es2015/context-name/input.js * 2023-05-setters--to-es2015/private/input.js * 2023-05-setters--to-es2015/public/input.js