From ce861507cd8cac7c6d9e410f4e09373cdd87ef22 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Fri, 6 Oct 2023 21:38:12 +0800 Subject: [PATCH 1/9] feat(transformer): Shorthand Properties --- crates/oxc_transformer/src/es2015/mod.rs | 3 ++ .../src/es2015/shorthand_properties.rs | 37 +++++++++++++++++++ crates/oxc_transformer/src/lib.rs | 11 ++++++ 3 files changed, 51 insertions(+) create mode 100644 crates/oxc_transformer/src/es2015/mod.rs create mode 100644 crates/oxc_transformer/src/es2015/shorthand_properties.rs diff --git a/crates/oxc_transformer/src/es2015/mod.rs b/crates/oxc_transformer/src/es2015/mod.rs new file mode 100644 index 0000000000000..7ff2ccadb763f --- /dev/null +++ b/crates/oxc_transformer/src/es2015/mod.rs @@ -0,0 +1,3 @@ +mod shorthand_properties; + +pub use shorthand_properties::ShorthandProperties; diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs new file mode 100644 index 0000000000000..a8dfd0a59667e --- /dev/null +++ b/crates/oxc_transformer/src/es2015/shorthand_properties.rs @@ -0,0 +1,37 @@ +use oxc_ast::{ast::*, AstBuilder}; +use oxc_span::GetSpan; + +use std::rc::Rc; + +/// ES2015: Shorthand Properties +/// +/// References: +/// * +/// * +pub struct ShorthandProperties<'a> { + ast: Rc>, +} + +impl<'a> ShorthandProperties<'a> { + pub fn new(ast: Rc>) -> Self { + Self { ast } + } + + pub fn transform_object_property<'b>(&mut self, obj_prop: &'b mut ObjectProperty<'a>) { + if obj_prop.shorthand { + obj_prop.shorthand = false; + + if obj_prop.key.is_specific_id("__proto__") { + debug_assert!( + !obj_prop.computed, + "shorthand and computed cannot be true at the same time" + ); + obj_prop.computed = true; + + let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() }; + let expr = self.ast.literal_string_expression(proto); + obj_prop.key = PropertyKey::Expression(expr); + } + } + } +} diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index b771e9f9e94d4..94a34042d54d5 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -7,6 +7,7 @@ //! * //! * +mod es2015; mod es2016; mod es2019; mod es2021; @@ -19,6 +20,7 @@ use oxc_ast::{ast::*, AstBuilder, VisitMut}; use oxc_span::SourceType; use std::rc::Rc; +use es2015::ShorthandProperties; use es2016::ExponentiationOperator; use es2019::OptionalCatchBinding; use es2021::LogicalAssignmentOperators; @@ -39,6 +41,8 @@ pub struct Transformer<'a> { es2019_optional_catch_binding: Option>, // es2016 es2016_exponentiation_operator: Option>, + // es2015 + es2015_shorthand_properties: Option>, } impl<'a> Transformer<'a> { @@ -66,6 +70,9 @@ impl<'a> Transformer<'a> { if options.target < TransformTarget::ES2016 { t.es2016_exponentiation_operator.replace(ExponentiationOperator::new(Rc::clone(&ast))); } + if options.target < TransformTarget::ES2015 { + t.es2015_shorthand_properties.replace(ShorthandProperties::new(Rc::clone(&ast))); + } t } @@ -92,4 +99,8 @@ impl<'a, 'b> VisitMut<'a, 'b> for Transformer<'a> { } self.visit_statements(&mut clause.body.body); } + + fn visit_object_property(&mut self, prop: &'b mut ObjectProperty<'a>) { + self.es2015_shorthand_properties.as_mut().map(|t| t.transform_object_property(prop)); + } } From 1ceaaed6b751640bffe1522c10ed2314e7f223d1 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Fri, 6 Oct 2023 22:15:01 +0800 Subject: [PATCH 2/9] fix: visit children of `ObjectProperty` --- crates/oxc_transformer/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 94a34042d54d5..ca941a6d03be9 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -102,5 +102,11 @@ impl<'a, 'b> VisitMut<'a, 'b> for Transformer<'a> { fn visit_object_property(&mut self, prop: &'b mut ObjectProperty<'a>) { self.es2015_shorthand_properties.as_mut().map(|t| t.transform_object_property(prop)); + + self.visit_property_key(&mut prop.key); + self.visit_expression(&mut prop.value); + if let Some(init) = &mut prop.init { + self.visit_expression(init); + } } } From de2dcfded886ba157685ea398c350885aab8b617 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Fri, 6 Oct 2023 22:19:26 +0800 Subject: [PATCH 3/9] chore: early return, less nested --- .../src/es2015/shorthand_properties.rs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs index a8dfd0a59667e..b82c1775b7447 100644 --- a/crates/oxc_transformer/src/es2015/shorthand_properties.rs +++ b/crates/oxc_transformer/src/es2015/shorthand_properties.rs @@ -18,20 +18,21 @@ impl<'a> ShorthandProperties<'a> { } pub fn transform_object_property<'b>(&mut self, obj_prop: &'b mut ObjectProperty<'a>) { - if obj_prop.shorthand { - obj_prop.shorthand = false; - - if obj_prop.key.is_specific_id("__proto__") { - debug_assert!( - !obj_prop.computed, - "shorthand and computed cannot be true at the same time" - ); - obj_prop.computed = true; - - let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() }; - let expr = self.ast.literal_string_expression(proto); - obj_prop.key = PropertyKey::Expression(expr); - } + if !obj_prop.shorthand { + return; } + + obj_prop.shorthand = false; + + if !obj_prop.key.is_specific_id("__proto__") { + return; + } + + debug_assert!(!obj_prop.computed, "shorthand and computed cannot be true at the same time"); + obj_prop.computed = true; + + let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() }; + let expr = self.ast.literal_string_expression(proto); + obj_prop.key = PropertyKey::Expression(expr); } } From c81faebb51db1f0cad69561504931513954c4bdb Mon Sep 17 00:00:00 2001 From: magic-akari Date: Fri, 6 Oct 2023 22:52:01 +0800 Subject: [PATCH 4/9] fix: `shorthand` of method --- crates/oxc_transformer/src/es2015/shorthand_properties.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs index b82c1775b7447..e8a3181e49fa6 100644 --- a/crates/oxc_transformer/src/es2015/shorthand_properties.rs +++ b/crates/oxc_transformer/src/es2015/shorthand_properties.rs @@ -18,17 +18,17 @@ impl<'a> ShorthandProperties<'a> { } pub fn transform_object_property<'b>(&mut self, obj_prop: &'b mut ObjectProperty<'a>) { - if !obj_prop.shorthand { + if !obj_prop.shorthand && !obj_prop.method { return; } obj_prop.shorthand = false; + obj_prop.method = false; - if !obj_prop.key.is_specific_id("__proto__") { + if !obj_prop.key.is_specific_id("__proto__") || obj_prop.computed { return; } - debug_assert!(!obj_prop.computed, "shorthand and computed cannot be true at the same time"); obj_prop.computed = true; let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() }; From c42656633bd0a3a5f48c39e552d293344f19e958 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Fri, 6 Oct 2023 23:16:35 +0800 Subject: [PATCH 5/9] fix: handle string literal of `__proto__` --- crates/oxc_ast/src/ast/js.rs | 7 +++++++ crates/oxc_transformer/src/es2015/shorthand_properties.rs | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 394ed08cac265..14bbe1adf5ded 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -420,6 +420,13 @@ impl<'a> PropertyKey<'a> { _ => false, } } + + pub fn is_specific_string_literal(&self, string: &str) -> bool { + match self { + PropertyKey::Expression(expr) => expr.is_specific_string_literal(string), + _ => false, + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs index e8a3181e49fa6..50e20186bcc8c 100644 --- a/crates/oxc_transformer/src/es2015/shorthand_properties.rs +++ b/crates/oxc_transformer/src/es2015/shorthand_properties.rs @@ -25,7 +25,13 @@ impl<'a> ShorthandProperties<'a> { obj_prop.shorthand = false; obj_prop.method = false; - if !obj_prop.key.is_specific_id("__proto__") || obj_prop.computed { + if obj_prop.computed { + return; + } + + if !obj_prop.key.is_specific_id("__proto__") + && !obj_prop.key.is_specific_string_literal("__proto__") + { return; } From cf7fe6fd38fa23baafea89dad6c46ec997f1d348 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Fri, 6 Oct 2023 23:24:12 +0800 Subject: [PATCH 6/9] chore: early return for `"__proto__"() {}` --- .../src/es2015/shorthand_properties.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs index 50e20186bcc8c..392f9f2ab41b6 100644 --- a/crates/oxc_transformer/src/es2015/shorthand_properties.rs +++ b/crates/oxc_transformer/src/es2015/shorthand_properties.rs @@ -29,14 +29,23 @@ impl<'a> ShorthandProperties<'a> { return; } - if !obj_prop.key.is_specific_id("__proto__") - && !obj_prop.key.is_specific_string_literal("__proto__") - { + let is_proto_id = obj_prop.key.is_specific_id("__proto__"); + let is_proto_string = obj_prop.key.is_specific_string_literal("__proto__"); + + if !is_proto_id && !is_proto_string { return; } obj_prop.computed = true; + if is_proto_string { + // input: + // "__proto__"() {} + // output: + // ["__proto__"]: function() {} + return; + } + let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() }; let expr = self.ast.literal_string_expression(proto); obj_prop.key = PropertyKey::Expression(expr); From 52481268ff77a38da64d3e0089e9d1bf5980e21d Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 7 Oct 2023 14:28:39 +0800 Subject: [PATCH 7/9] chore: add test results --- crates/oxc_transformer/src/options.rs | 1 + tasks/transform_conformance/babel.snap.md | 11 ++++++++++- tasks/transform_conformance/src/lib.rs | 6 ++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index 0059aaffb0ffb..c633716f6b165 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -7,6 +7,7 @@ pub struct TransformOptions { /// See #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] pub enum TransformTarget { + ES5, ES2015, ES2016, ES2019, diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 7a1c45439971d..1a879b2590a6f 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 86/1071 +Passed: 91/1078 # babel-plugin-transform-class-properties * Failed: assumption-constantSuper/complex-super-class/input.js @@ -750,6 +750,15 @@ Passed: 86/1071 * Failed: regression/4403/input.js * Passed: exponentiation-operator/binary/input.js +# babel-plugin-transform-shorthand-properties +* Failed: shorthand-properties/method-type-annotations/input.js +* Failed: shorthand-properties/shorthand-comments/input.js +* Passed: shorthand-properties/method-plain/input.js +* Passed: shorthand-properties/proto/input.js +* Passed: shorthand-properties/shorthand-mixed/input.js +* Passed: shorthand-properties/shorthand-multiple/input.js +* Passed: shorthand-properties/shorthand-single/input.js + # babel-plugin-transform-typescript * Failed: class/abstract-class-decorated/input.ts * Failed: class/abstract-class-decorated-method/input.ts diff --git a/tasks/transform_conformance/src/lib.rs b/tasks/transform_conformance/src/lib.rs index 0e02a5eb023e1..764d9a3d8b644 100644 --- a/tasks/transform_conformance/src/lib.rs +++ b/tasks/transform_conformance/src/lib.rs @@ -54,6 +54,8 @@ pub fn babel(options: &BabelOptions) { "babel-plugin-transform-async-to-generator", // ES2016 "babel-plugin-transform-exponentiation-operator", + // ES2015 + "babel-plugin-transform-shorthand-properties", // TypeScript "babel-plugin-transform-typescript", // React @@ -133,7 +135,7 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool { let expected = output_path.and_then(|path| fs::read_to_string(path).ok()); if let Some(expected) = &expected { let transform_options = TransformOptions { - target: TransformTarget::ES2015, + target: TransformTarget::ES5, react: Some(TransformReactOptions::default()), }; let program = allocator.alloc(ret.program); @@ -146,7 +148,7 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool { let passed = trim_transformed == trim_expected; if filtered { println!("Expected:\n"); - println!("{expected:?}\n"); + println!("{expected}\n"); println!("Transformed:\n"); println!("{transformed}\n"); println!("Diff:\n"); From 6ed809dbdc98eb91c1ea325f9d86c3c128f85779 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 7 Oct 2023 16:35:38 +0800 Subject: [PATCH 8/9] chore: add more comments --- .../src/es2015/shorthand_properties.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs index 392f9f2ab41b6..f5deed2a8400c 100644 --- a/crates/oxc_transformer/src/es2015/shorthand_properties.rs +++ b/crates/oxc_transformer/src/es2015/shorthand_properties.rs @@ -26,19 +26,28 @@ impl<'a> ShorthandProperties<'a> { obj_prop.method = false; if obj_prop.computed { + // all computed key can never be transformed to `__proto__` setter unexpectedly return; } - let is_proto_id = obj_prop.key.is_specific_id("__proto__"); + // We should handle the edge case of `__proto__` property. + // All shorthand properties with key `__proto__` can never be `__proto__` setter. + // But the transformed result can be `__proto__` setter unexpectedly. + // It's easy to fix it by using computed property. + let is_proto_string = obj_prop.key.is_specific_string_literal("__proto__"); - if !is_proto_id && !is_proto_string { + if !is_proto_string && obj_prop.key.is_specific_id("__proto__") { return; } + // We reach here, it means that the key is `__proto__` or `"__proto__"`. + // Transform `__proto__`/`"__proto__"` to computed property. obj_prop.computed = true; if is_proto_string { + // After the transformation, the string literal `"__proto__"` is already expected result. + // // input: // "__proto__"() {} // output: @@ -46,6 +55,8 @@ impl<'a> ShorthandProperties<'a> { return; } + // Convert `[__proto__]` to `["__proto__"]` + let proto = StringLiteral { span: obj_prop.key.span(), value: "__proto__".into() }; let expr = self.ast.literal_string_expression(proto); obj_prop.key = PropertyKey::Expression(expr); From 83bb58232051796dde04c55008ee31e813f295c3 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Sat, 7 Oct 2023 16:37:45 +0800 Subject: [PATCH 9/9] fix --- crates/oxc_transformer/src/es2015/shorthand_properties.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_transformer/src/es2015/shorthand_properties.rs b/crates/oxc_transformer/src/es2015/shorthand_properties.rs index f5deed2a8400c..9442412890c55 100644 --- a/crates/oxc_transformer/src/es2015/shorthand_properties.rs +++ b/crates/oxc_transformer/src/es2015/shorthand_properties.rs @@ -37,7 +37,7 @@ impl<'a> ShorthandProperties<'a> { let is_proto_string = obj_prop.key.is_specific_string_literal("__proto__"); - if !is_proto_string && obj_prop.key.is_specific_id("__proto__") { + if !is_proto_string && !obj_prop.key.is_specific_id("__proto__") { return; } // We reach here, it means that the key is `__proto__` or `"__proto__"`.