Skip to content

Commit

Permalink
feat(transformer): add object-spread
Browse files Browse the repository at this point in the history
  • Loading branch information
magic-akari committed May 3, 2024
1 parent e7fa6a8 commit 3e6e05f
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 24 deletions.
4 changes: 4 additions & 0 deletions crates/oxc_ast/src/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ impl<'a> AstBuilder<'a> {
mem::replace(stmts, self.new_vec())
}

pub fn move_vec<T>(&self, vec: &mut Vec<'a, T>) -> Vec<'a, T> {
mem::replace(vec, self.new_vec())
}

pub fn move_assignment_target(
&self,
target: &mut AssignmentTarget<'a>,
Expand Down
17 changes: 11 additions & 6 deletions crates/oxc_transformer/src/es2018/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
mod object_rest_spread;
mod object_spread;
mod options;

pub use object_rest_spread::{ObjectRestSpread, ObjectRestSpreadOptions};
pub use options::ES2018Options;

use oxc_ast::ast::*;
use std::rc::Rc;

use crate::context::Ctx;
use crate::{context::Ctx, CompilerAssumptions};

#[allow(dead_code)]
pub struct ES2018<'a> {
Expand All @@ -18,14 +20,17 @@ pub struct ES2018<'a> {
}

impl<'a> ES2018<'a> {
pub fn new(options: ES2018Options, ctx: &Ctx<'a>) -> Self {
pub fn new(options: ES2018Options, assumptions: CompilerAssumptions, ctx: &Ctx<'a>) -> Self {
Self {
object_rest_spread: ObjectRestSpread::new(
options.object_rest_spread.clone().unwrap_or_default(),
ctx,
),
ctx: Rc::clone(ctx),
options,
object_rest_spread: ObjectRestSpread::new(assumptions, ctx),
}
}

pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
if self.options.object_rest_spread.is_some() {
self.object_rest_spread.transform_expression(expr);
}
}
}
32 changes: 23 additions & 9 deletions crates/oxc_transformer/src/es2018/object_rest_spread.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::rc::Rc;

use oxc_ast::ast::*;
use serde::Deserialize;

use crate::context::Ctx;
use crate::{context::Ctx, CompilerAssumptions};

#[derive(Debug, Default, Clone, Deserialize)]
use super::object_spread::{ObjectSpread, ObjectSpreadOptions};

#[derive(Debug, Default, Clone, Copy, Deserialize)]
pub struct ObjectRestSpreadOptions {
/// When using object spread, assume that
/// spreaded properties don't trigger getters on the target object
/// and thus it's safe to assign them rather than defining them using `Object.defineProperty`.
#[serde(rename = "loose")]
pub set_spread_properties: bool,
pub _loose: bool,

#[serde(rename = "useBuiltIns")]
pub _use_built_ins: bool,
Expand All @@ -28,11 +28,25 @@ pub struct ObjectRestSpreadOptions {
/// * <https://github.com/babel/babel/tree/main/packages/babel-plugin-transform-object-rest-spread>
pub struct ObjectRestSpread<'a> {
ctx: Ctx<'a>,
_options: ObjectRestSpreadOptions,

object_spread: ObjectSpread<'a>,
}

impl<'a> ObjectRestSpread<'a> {
pub fn new(options: ObjectRestSpreadOptions, ctx: &Ctx<'a>) -> Self {
Self { ctx: Rc::clone(ctx), _options: options }
pub fn new(assumptions: CompilerAssumptions, ctx: &Ctx<'a>) -> Self {
Self {
ctx: Rc::clone(ctx),
object_spread: ObjectSpread::new(
ObjectSpreadOptions {
set_spread_properties: assumptions.set_spread_properties,
pure_getters: assumptions.pure_getters,
},
ctx,
),
}
}

pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
self.object_spread.transform_expression(expr);
}
}
101 changes: 101 additions & 0 deletions crates/oxc_transformer/src/es2018/object_spread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::rc::Rc;

use oxc_ast::{ast::*, AstBuilder};
use oxc_span::SPAN;
use serde::Deserialize;

use crate::context::Ctx;

#[derive(Debug, Default, Clone, Copy, Deserialize)]
pub struct ObjectSpreadOptions {
pub set_spread_properties: bool,
pub pure_getters: bool,
}

pub struct ObjectSpread<'a> {
ctx: Ctx<'a>,
options: ObjectSpreadOptions,
}

impl<'a> ObjectSpread<'a> {
pub fn new(options: ObjectSpreadOptions, ctx: &Ctx<'a>) -> Self {
Self { ctx: Rc::clone(ctx), options }
}

fn ast(&self) -> &AstBuilder<'a> {
&self.ctx.ast
}

fn get_static_member_expression(
&self,
object_ident_name: &str,
property_name: &str,
) -> Expression<'a> {
let property = IdentifierName::new(SPAN, self.ast().new_atom(property_name));
let ident = IdentifierReference::new(SPAN, self.ast().new_atom(object_ident_name));
let object = self.ast().identifier_reference_expression(ident);
self.ast().static_member_expression(SPAN, object, property, false)
}

fn get_extend_object_callee(&self) -> Expression<'a> {
if self.options.set_spread_properties {
self.get_static_member_expression("Object", "assign")
} else {
let ident = IdentifierReference::new(SPAN, self.ast().new_atom("_objectSpread"));
self.ast().identifier_reference_expression(ident)
}
}

pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
let Expression::ObjectExpression(obj_expr) = expr else {
return;
};

if obj_expr
.properties
.iter()
.all(|prop| matches!(prop, ObjectPropertyKind::ObjectProperty(..)))
{
return;
}

let mut obj_prop_list = self.ast().new_vec();
while obj_expr
.properties
.last()
.map_or(false, |prop| matches!(prop, ObjectPropertyKind::ObjectProperty(..)))
{
let prop = obj_expr.properties.pop().unwrap();
obj_prop_list.push(prop);
}

let ObjectPropertyKind::SpreadProperty(mut spread_prop) =
obj_expr.properties.pop().unwrap()
else {
unreachable!();
};

self.transform_expression(expr);
let mut args = self.ast().new_vec();
args.push(Argument::from(self.ast().move_expression(expr)));
args.push(Argument::from(self.ast().move_expression(&mut spread_prop.argument)));

*expr =
self.ast().call_expression(SPAN, self.get_extend_object_callee(), args, false, None);

if !obj_prop_list.is_empty() {
obj_prop_list.reverse();
let mut args = self.ast().new_vec();
args.push(Argument::from(self.ast().move_expression(expr)));
args.push(Argument::from(self.ast().object_expression(SPAN, obj_prop_list, None)));

*expr = self.ast().call_expression(
SPAN,
self.get_extend_object_callee(),
args,
false,
None,
);
}
}
}
10 changes: 2 additions & 8 deletions crates/oxc_transformer/src/es2018/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@ use super::ObjectRestSpreadOptions;
pub struct ES2018Options {
#[serde(skip)]
pub object_rest_spread: Option<ObjectRestSpreadOptions>,
}

impl From<CompilerAssumptions> for ObjectRestSpreadOptions {
fn from(value: CompilerAssumptions) -> Self {
Self {
set_spread_properties: value.set_spread_properties,
_use_built_ins: false,
}
}
#[serde(skip)]
pub assumptions: CompilerAssumptions,
}
3 changes: 2 additions & 1 deletion crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl<'a> Transformer<'a> {
x0_typescript: TypeScript::new(options.typescript, &ctx),
x1_react: React::new(options.react, &ctx),
x3_es2015: ES2015::new(options.es2015, &ctx),
x3_es2018: ES2018::new(options.es2018, &ctx),
x3_es2018: ES2018::new(options.es2018, options.assumptions, &ctx),
}
}

Expand Down Expand Up @@ -148,6 +148,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
self.x3_es2015.transform_expression(expr);

walk_mut::walk_expression_mut(self, expr);
self.x3_es2018.transform_expression(expr);

self.x3_es2015.transform_expression_on_exit(expr);
}
Expand Down

0 comments on commit 3e6e05f

Please sign in to comment.