feat(transformer/decorators): support static member (#2235)

This commit is contained in:
Dunqing 2024-01-31 19:11:27 +08:00 committed by GitHub
parent 3b85e1813b
commit a79988d5e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 133 additions and 100 deletions

1
Cargo.lock generated
View file

@ -1801,6 +1801,7 @@ dependencies = [
name = "oxc_transformer" name = "oxc_transformer"
version = "0.5.0" version = "0.5.0"
dependencies = [ dependencies = [
"bitflags 2.4.2",
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
"oxc_codegen", "oxc_codegen",

View file

@ -19,6 +19,7 @@ workspace = true
doctest = false doctest = false
[dependencies] [dependencies]
bitflags = { workspace = true }
oxc_ast = { workspace = true } oxc_ast = { workspace = true }
oxc_span = { workspace = true } oxc_span = { workspace = true }
oxc_allocator = { workspace = true } oxc_allocator = { workspace = true }

View file

@ -1,5 +1,6 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
use bitflags::bitflags;
use oxc_allocator::{Box, Vec}; use oxc_allocator::{Box, Vec};
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::{ast::*, AstBuilder};
use oxc_span::{Atom, SPAN}; use oxc_span::{Atom, SPAN};
@ -25,6 +26,30 @@ pub struct Decorators<'a> {
uid_map: HashMap<Atom, u32>, uid_map: HashMap<Atom, u32>,
} }
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;
}
}
impl DecoratorFlags {
pub fn get_flag_by_kind(kind: MethodDefinitionKind) -> Self {
match kind {
MethodDefinitionKind::Method => Self::Method,
MethodDefinitionKind::Get => Self::Getter,
MethodDefinitionKind::Set => Self::Setter,
MethodDefinitionKind::Constructor => unreachable!(),
}
}
}
#[derive(Debug, Clone, Copy, Default, Deserialize)] #[derive(Debug, Clone, Copy, Default, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DecoratorsOptions { pub struct DecoratorsOptions {
@ -251,6 +276,8 @@ impl<'a> Decorators<'a> {
let mut c_elements = self.ast.new_vec(); let mut c_elements = self.ast.new_vec();
let mut e_elements = self.ast.new_vec(); let mut e_elements = self.ast.new_vec();
let mut init_static_name = None;
// insert member decorators // insert member decorators
let mut member_decorators_vec = self.ast.new_vec(); let mut member_decorators_vec = self.ast.new_vec();
let mut class_decorators_argument = let mut class_decorators_argument =
@ -330,115 +357,108 @@ impl<'a> Decorators<'a> {
class.body.body.insert(0, static_block); class.body.body.insert(0, static_block);
} }
} else if has_member_decorator { } else if has_member_decorator {
let elements: std::vec::Vec<_> = { let get_decorator_info =
class |name: Option<Atom>, flag: u8, decorator: &Decorator<'a>, ast: &AstBuilder<'a>| {
.body let mut decorator_elements = ast.new_vec_with_capacity(2);
.body decorator_elements
.iter_mut() .push(ArrayExpressionElement::Expression(ast.copy(&decorator.expression)));
.enumerate()
.filter_map(|(index, element)| match element {
ClassElement::MethodDefinition(def) => {
if def.decorators.is_empty() {
None
} else {
Some((
index,
def.key.name(),
None,
def.r#static,
def.computed,
&def.decorators,
))
}
}
ClassElement::PropertyDefinition(def) => {
if def.decorators.is_empty() {
None
} else {
Some((
index,
def.key.name(),
self.ast.copy(&def.value),
def.r#static,
def.computed,
&def.decorators,
))
}
}
_ => None,
})
.collect()
};
let mut replace_elements = HashMap::new();
for (index, name, value, r#static, computed, decorators) in elements {
let init_name = self.get_unique_name(&if computed {
"init_computedKey".into()
} else {
format!("init_{}", name.clone().unwrap()).into()
});
decorators.iter().for_each(|decorator| {
e_elements.push(self.get_assignment_target_maybe_default(init_name.clone()));
let mut decorator_elements = self.ast.new_vec_with_capacity(2);
decorator_elements.push(ArrayExpressionElement::Expression( decorator_elements.push(ArrayExpressionElement::Expression(
self.ast.copy(&decorator.expression), ast.literal_number_expression(NumberLiteral::new(
));
decorator_elements.push(ArrayExpressionElement::Expression(
self.ast.literal_number_expression(NumberLiteral::new(
SPAN, SPAN,
0f64, 0f64,
if r#static { "8" } else { "0" }, ast.new_str(flag.to_string().as_str()),
oxc_syntax::NumberBase::Decimal, oxc_syntax::NumberBase::Decimal,
)), )),
)); ));
if let Some(name) = name.clone() { if let Some(name) = name {
decorator_elements.push(ArrayExpressionElement::Expression( decorator_elements.push(ArrayExpressionElement::Expression(
self.ast.literal_string_expression(StringLiteral::new(SPAN, name)), ast.literal_string_expression(StringLiteral::new(SPAN, name)),
)); ));
} }
member_decorators_vec.push(ArrayExpressionElement::Expression( ast.array_expression(SPAN, decorator_elements, None)
self.ast.array_expression(SPAN, decorator_elements, None), };
));
});
declarations.push(self.get_variable_declarator(init_name.clone())); let mut is_static = false;
class.body.body.iter_mut().for_each(|element| {
let mut arguments = if !element.has_decorator() {
self.ast.new_vec_single(Argument::Expression(self.ast.this_expression(SPAN))); return;
if let Some(default_value) = value {
arguments.push(Argument::Expression(default_value));
} }
match element {
replace_elements.insert( ClassElement::MethodDefinition(def) => {
index, if def.r#static {
self.ast.call_expression( is_static = def.r#static;
SPAN,
self.ast.identifier_reference_expression(IdentifierReference::new(
SPAN, init_name,
)),
arguments,
false,
None,
),
);
}
// replace the element with `name = init_name(this)`
for (index, element) in class.body.body.iter_mut().enumerate() {
if let Some(new_element) = replace_elements.remove(&index) {
match element {
ClassElement::PropertyDefinition(definition) => {
definition.decorators.clear();
definition.value = Some(new_element);
} }
ClassElement::MethodDefinition(definition) => { let name = def.key.name();
definition.decorators.clear(); let mut flag = DecoratorFlags::get_flag_by_kind(def.kind).bits();
if def.r#static {
flag += DecoratorFlags::Static.bits();
} }
_ => {}
def.decorators.drain(..).for_each(|decorator| {
member_decorators_vec.push(ArrayExpressionElement::Expression(
get_decorator_info(name.clone(), flag, &decorator, &self.ast),
));
});
} }
ClassElement::PropertyDefinition(def) => {
let flag = if def.r#static {
DecoratorFlags::Static
} else {
DecoratorFlags::Field
};
let name = def.key.name();
let init_name = self.get_unique_name(&if def.computed {
"init_computedKey".into()
} else {
format!("init_{}", name.clone().unwrap()).into()
});
e_elements
.push(self.get_assignment_target_maybe_default(init_name.clone()));
def.decorators.drain(..).for_each(|decorator| {
member_decorators_vec.push(ArrayExpressionElement::Expression(
get_decorator_info(
name.clone(),
flag.bits(),
&decorator,
&self.ast,
),
));
});
let mut arguments = self
.ast
.new_vec_single(Argument::Expression(self.ast.this_expression(SPAN)));
if let Some(value) = &mut def.value {
arguments.push(Argument::Expression(self.ast.move_expression(value)));
}
def.value = Some(self.ast.call_expression(
SPAN,
self.ast.identifier_reference_expression(IdentifierReference::new(
SPAN,
init_name.clone(),
)),
arguments,
false,
None,
));
declarations.push(self.get_variable_declarator(init_name));
}
_ => {}
} }
});
if is_static {
let name = self.get_unique_name(&"initStatic".into());
e_elements.push(self.get_assignment_target_maybe_default(name.clone()));
declarations.push(self.get_variable_declarator(name.clone()));
init_static_name = Some(name);
} }
} }
@ -500,6 +520,21 @@ impl<'a> Decorators<'a> {
let mut statements = self.ast.new_vec(); let mut statements = self.ast.new_vec();
statements.push(self.ast.expression_statement(SPAN, new_expr)); statements.push(self.ast.expression_statement(SPAN, new_expr));
if let Some(init_static_name) = init_static_name {
// call initStatic
let callee = self.ast.identifier_reference_expression(IdentifierReference::new(
SPAN,
init_static_name,
));
let arguments =
self.ast.new_vec_single(Argument::Expression(self.ast.this_expression(SPAN)));
statements.push(self.ast.expression_statement(
SPAN,
self.ast.call_expression(SPAN, callee, arguments, false, None),
));
}
let static_block = self.ast.static_block(SPAN, statements); let static_block = self.ast.static_block(SPAN, statements);
class.body.body.insert(0, static_block); class.body.body.insert(0, static_block);
} }

View file

@ -1,4 +1,4 @@
Passed: 331/1369 Passed: 335/1369
# All Passed: # All Passed:
* babel-plugin-transform-numeric-separator * babel-plugin-transform-numeric-separator
@ -911,7 +911,7 @@ Passed: 331/1369
* spread-transform/transform-to-babel-extend/input.js * spread-transform/transform-to-babel-extend/input.js
* spread-transform/transform-to-object-assign/input.js * spread-transform/transform-to-object-assign/input.js
# babel-plugin-proposal-decorators (7/190) # babel-plugin-proposal-decorators (11/190)
* 2018-09-transformation/async-generator-method/input.js * 2018-09-transformation/async-generator-method/input.js
* 2018-09-transformation/class-decorators-yield-await/input.js * 2018-09-transformation/class-decorators-yield-await/input.js
* 2021-12-accessors/context-name/input.js * 2021-12-accessors/context-name/input.js
@ -1032,7 +1032,6 @@ Passed: 331/1369
* 2023-05-getters/private/input.js * 2023-05-getters/private/input.js
* 2023-05-getters/public/input.js * 2023-05-getters/public/input.js
* 2023-05-getters/static-private/input.js * 2023-05-getters/static-private/input.js
* 2023-05-getters/static-public/input.js
* 2023-05-getters--to-es2015/context-name/input.js * 2023-05-getters--to-es2015/context-name/input.js
* 2023-05-getters--to-es2015/private/input.js * 2023-05-getters--to-es2015/private/input.js
* 2023-05-getters--to-es2015/public/input.js * 2023-05-getters--to-es2015/public/input.js
@ -1041,7 +1040,6 @@ Passed: 331/1369
* 2023-05-getters-and-setters/private/input.js * 2023-05-getters-and-setters/private/input.js
* 2023-05-getters-and-setters/public/input.js * 2023-05-getters-and-setters/public/input.js
* 2023-05-getters-and-setters/static-private/input.js * 2023-05-getters-and-setters/static-private/input.js
* 2023-05-getters-and-setters/static-public/input.js
* 2023-05-getters-and-setters--to-es2015/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/public/input.js
* 2023-05-getters-and-setters--to-es2015/static-private/input.js * 2023-05-getters-and-setters--to-es2015/static-private/input.js
@ -1050,7 +1048,6 @@ Passed: 331/1369
* 2023-05-methods/private/input.js * 2023-05-methods/private/input.js
* 2023-05-methods/public/input.js * 2023-05-methods/public/input.js
* 2023-05-methods/static-private/input.js * 2023-05-methods/static-private/input.js
* 2023-05-methods/static-public/input.js
* 2023-05-methods--to-es2015/context-name/input.js * 2023-05-methods--to-es2015/context-name/input.js
* 2023-05-methods--to-es2015/private/input.js * 2023-05-methods--to-es2015/private/input.js
* 2023-05-methods--to-es2015/public/input.js * 2023-05-methods--to-es2015/public/input.js
@ -1084,7 +1081,6 @@ Passed: 331/1369
* 2023-05-setters/private/input.js * 2023-05-setters/private/input.js
* 2023-05-setters/public/input.js * 2023-05-setters/public/input.js
* 2023-05-setters/static-private/input.js * 2023-05-setters/static-private/input.js
* 2023-05-setters/static-public/input.js
* 2023-05-setters--to-es2015/context-name/input.js * 2023-05-setters--to-es2015/context-name/input.js
* 2023-05-setters--to-es2015/private/input.js * 2023-05-setters--to-es2015/private/input.js
* 2023-05-setters--to-es2015/public/input.js * 2023-05-setters--to-es2015/public/input.js