Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,14 @@ pub struct ArrowFunctionExpression<'a> {
#[builder(default)]
#[estree(skip)]
pub pure: bool,
#[builder(default)]
#[estree(skip)]
/// `true` if the function should be marked as "Possibly-Invoked Function Expression" (PIFE).
///
/// References:
/// - v8 blog post about PIFEs: <https://v8.dev/blog/preparser#pife>
/// - introduced PR: <https://github.com/oxc-project/oxc/pull/12353>
pub pife: bool,
}

/// Generator Function Definitions
Expand Down
6 changes: 4 additions & 2 deletions crates/oxc_ast/src/generated/assert_layouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ const _: () = {
assert!(offset_of!(FunctionBody, directives) == 8);
assert!(offset_of!(FunctionBody, statements) == 32);

// Padding: 1 bytes
// Padding: 0 bytes
assert!(size_of::<ArrowFunctionExpression>() == 48);
assert!(align_of::<ArrowFunctionExpression>() == 8);
assert!(offset_of!(ArrowFunctionExpression, span) == 0);
Expand All @@ -629,6 +629,7 @@ const _: () = {
assert!(offset_of!(ArrowFunctionExpression, body) == 32);
assert!(offset_of!(ArrowFunctionExpression, scope_id) == 40);
assert!(offset_of!(ArrowFunctionExpression, pure) == 46);
assert!(offset_of!(ArrowFunctionExpression, pife) == 47);

// Padding: 7 bytes
assert!(size_of::<YieldExpression>() == 32);
Expand Down Expand Up @@ -2207,7 +2208,7 @@ const _: () = {
assert!(offset_of!(FunctionBody, directives) == 8);
assert!(offset_of!(FunctionBody, statements) == 24);

// Padding: 1 bytes
// Padding: 0 bytes
assert!(size_of::<ArrowFunctionExpression>() == 32);
assert!(align_of::<ArrowFunctionExpression>() == 4);
assert!(offset_of!(ArrowFunctionExpression, span) == 0);
Expand All @@ -2219,6 +2220,7 @@ const _: () = {
assert!(offset_of!(ArrowFunctionExpression, body) == 20);
assert!(offset_of!(ArrowFunctionExpression, scope_id) == 24);
assert!(offset_of!(ArrowFunctionExpression, pure) == 30);
assert!(offset_of!(ArrowFunctionExpression, pife) == 31);

// Padding: 3 bytes
assert!(size_of::<YieldExpression>() == 20);
Expand Down
30 changes: 20 additions & 10 deletions crates/oxc_ast/src/generated/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ impl<'a> AstBuilder<'a> {
))
}

/// Build an [`Expression::ArrowFunctionExpression`] with `scope_id` and `pure`.
/// Build an [`Expression::ArrowFunctionExpression`] with `scope_id` and `pure` and `pife`.
///
/// This node contains an [`ArrowFunctionExpression`] that will be stored in the memory arena.
///
Expand All @@ -378,8 +378,9 @@ impl<'a> AstBuilder<'a> {
/// * `body`: See `expression` for whether this arrow expression returns an expression.
/// * `scope_id`
/// * `pure`: `true` if the function is marked with a `/*#__NO_SIDE_EFFECTS__*/` comment
/// * `pife`: `true` if the function should be marked as "Possibly-Invoked Function Expression" (PIFE).
#[inline]
pub fn expression_arrow_function_with_scope_id_and_pure<T1, T2, T3, T4>(
pub fn expression_arrow_function_with_scope_id_and_pure_and_pife<T1, T2, T3, T4>(
self,
span: Span,
expression: bool,
Expand All @@ -390,6 +391,7 @@ impl<'a> AstBuilder<'a> {
body: T4,
scope_id: ScopeId,
pure: bool,
pife: bool,
) -> Expression<'a>
where
T1: IntoIn<'a, Option<Box<'a, TSTypeParameterDeclaration<'a>>>>,
Expand All @@ -398,7 +400,7 @@ impl<'a> AstBuilder<'a> {
T4: IntoIn<'a, Box<'a, FunctionBody<'a>>>,
{
Expression::ArrowFunctionExpression(
self.alloc_arrow_function_expression_with_scope_id_and_pure(
self.alloc_arrow_function_expression_with_scope_id_and_pure_and_pife(
span,
expression,
r#async,
Expand All @@ -408,6 +410,7 @@ impl<'a> AstBuilder<'a> {
body,
scope_id,
pure,
pife,
),
)
}
Expand Down Expand Up @@ -6067,6 +6070,7 @@ impl<'a> AstBuilder<'a> {
body: body.into_in(self.allocator),
scope_id: Default::default(),
pure: Default::default(),
pife: Default::default(),
}
}

Expand Down Expand Up @@ -6114,10 +6118,10 @@ impl<'a> AstBuilder<'a> {
)
}

/// Build an [`ArrowFunctionExpression`] with `scope_id` and `pure`.
/// Build an [`ArrowFunctionExpression`] with `scope_id` and `pure` and `pife`.
///
/// If you want the built node to be allocated in the memory arena,
/// use [`AstBuilder::alloc_arrow_function_expression_with_scope_id_and_pure`] instead.
/// use [`AstBuilder::alloc_arrow_function_expression_with_scope_id_and_pure_and_pife`] instead.
///
/// ## Parameters
/// * `span`: The [`Span`] covering this node
Expand All @@ -6129,8 +6133,9 @@ impl<'a> AstBuilder<'a> {
/// * `body`: See `expression` for whether this arrow expression returns an expression.
/// * `scope_id`
/// * `pure`: `true` if the function is marked with a `/*#__NO_SIDE_EFFECTS__*/` comment
/// * `pife`: `true` if the function should be marked as "Possibly-Invoked Function Expression" (PIFE).
#[inline]
pub fn arrow_function_expression_with_scope_id_and_pure<T1, T2, T3, T4>(
pub fn arrow_function_expression_with_scope_id_and_pure_and_pife<T1, T2, T3, T4>(
self,
span: Span,
expression: bool,
Expand All @@ -6141,6 +6146,7 @@ impl<'a> AstBuilder<'a> {
body: T4,
scope_id: ScopeId,
pure: bool,
pife: bool,
) -> ArrowFunctionExpression<'a>
where
T1: IntoIn<'a, Option<Box<'a, TSTypeParameterDeclaration<'a>>>>,
Expand All @@ -6158,13 +6164,14 @@ impl<'a> AstBuilder<'a> {
body: body.into_in(self.allocator),
scope_id: Cell::new(Some(scope_id)),
pure,
pife,
}
}

/// Build an [`ArrowFunctionExpression`] with `scope_id` and `pure`, and store it in the memory arena.
/// Build an [`ArrowFunctionExpression`] with `scope_id` and `pure` and `pife`, and store it in the memory arena.
///
/// Returns a [`Box`] containing the newly-allocated node.
/// If you want a stack-allocated node, use [`AstBuilder::arrow_function_expression_with_scope_id_and_pure`] instead.
/// If you want a stack-allocated node, use [`AstBuilder::arrow_function_expression_with_scope_id_and_pure_and_pife`] instead.
///
/// ## Parameters
/// * `span`: The [`Span`] covering this node
Expand All @@ -6176,8 +6183,9 @@ impl<'a> AstBuilder<'a> {
/// * `body`: See `expression` for whether this arrow expression returns an expression.
/// * `scope_id`
/// * `pure`: `true` if the function is marked with a `/*#__NO_SIDE_EFFECTS__*/` comment
/// * `pife`: `true` if the function should be marked as "Possibly-Invoked Function Expression" (PIFE).
#[inline]
pub fn alloc_arrow_function_expression_with_scope_id_and_pure<T1, T2, T3, T4>(
pub fn alloc_arrow_function_expression_with_scope_id_and_pure_and_pife<T1, T2, T3, T4>(
self,
span: Span,
expression: bool,
Expand All @@ -6188,6 +6196,7 @@ impl<'a> AstBuilder<'a> {
body: T4,
scope_id: ScopeId,
pure: bool,
pife: bool,
) -> Box<'a, ArrowFunctionExpression<'a>>
where
T1: IntoIn<'a, Option<Box<'a, TSTypeParameterDeclaration<'a>>>>,
Expand All @@ -6196,7 +6205,7 @@ impl<'a> AstBuilder<'a> {
T4: IntoIn<'a, Box<'a, FunctionBody<'a>>>,
{
Box::new_in(
self.arrow_function_expression_with_scope_id_and_pure(
self.arrow_function_expression_with_scope_id_and_pure_and_pife(
span,
expression,
r#async,
Expand All @@ -6206,6 +6215,7 @@ impl<'a> AstBuilder<'a> {
body,
scope_id,
pure,
pife,
),
self.allocator,
)
Expand Down
2 changes: 2 additions & 0 deletions crates/oxc_ast/src/generated/derive_clone_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3677,6 +3677,7 @@ impl<'new_alloc> CloneIn<'new_alloc> for ArrowFunctionExpression<'_> {
body: CloneIn::clone_in(&self.body, allocator),
scope_id: Default::default(),
pure: CloneIn::clone_in(&self.pure, allocator),
pife: CloneIn::clone_in(&self.pife, allocator),
}
}

Expand All @@ -3691,6 +3692,7 @@ impl<'new_alloc> CloneIn<'new_alloc> for ArrowFunctionExpression<'_> {
body: CloneIn::clone_in_with_semantic_ids(&self.body, allocator),
scope_id: CloneIn::clone_in_with_semantic_ids(&self.scope_id, allocator),
pure: CloneIn::clone_in_with_semantic_ids(&self.pure, allocator),
pife: CloneIn::clone_in_with_semantic_ids(&self.pife, allocator),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_ast/src/generated/derive_content_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,7 @@ impl ContentEq for ArrowFunctionExpression<'_> {
&& ContentEq::content_eq(&self.return_type, &other.return_type)
&& ContentEq::content_eq(&self.body, &other.body)
&& ContentEq::content_eq(&self.pure, &other.pure)
&& ContentEq::content_eq(&self.pife, &other.pife)
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/oxc_ast/src/generated/derive_dummy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,7 @@ impl<'a> Dummy<'a> for ArrowFunctionExpression<'a> {
body: Dummy::dummy(allocator),
scope_id: Dummy::dummy(allocator),
pure: Dummy::dummy(allocator),
pife: Dummy::dummy(allocator),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_ast_macros/src/generated/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map {
("TemplateElement", StructDetails { field_order: None }),
(
"ArrowFunctionExpression",
StructDetails { field_order: Some(&[0, 6, 7, 1, 2, 3, 4, 5, 8]) },
StructDetails { field_order: Some(&[0, 6, 7, 1, 2, 3, 4, 5, 8, 9]) },
),
("NumericLiteral", StructDetails { field_order: None }),
("TSTupleType", StructDetails { field_order: None }),
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1633,7 +1633,7 @@ impl Gen for PropertyKey<'_> {

impl GenExpr for ArrowFunctionExpression<'_> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
p.wrap(precedence >= Precedence::Assign, |p| {
p.wrap(precedence >= Precedence::Assign || self.pife, |p| {
if self.r#async {
p.print_space_before_identifier();
p.add_source_mapping(self.span);
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_codegen/tests/integration/esbuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ fn test_for() {
test("for (x(a in b);;);", "for (x(a in b);;);\n");
test("for (x[a in b];;);", "for (x[a in b];;);\n");
test("for (x?.[a in b];;);", "for (x?.[a in b];;);\n");
test("for ((x => a in b);;);", "for ((x) => (a in b);;);\n");
test("for ((x => a in b);;);", "for (((x) => (a in b));;);\n");

// Make sure for-of loops with commas are wrapped in parentheses
test("for (let a in b, c);", "for (let a in b, c);\n");
Expand Down
10 changes: 9 additions & 1 deletion crates/oxc_codegen/tests/integration/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ fn r#yield() {
fn arrow() {
test_minify("x => a, b", "x=>a,b;");
test_minify("x => (a, b)", "x=>(a,b);");
test_minify("x => (a => b)", "x=>a=>b;");
test_minify("x => (a => b)", "x=>(a=>b);");
test_minify("x => y => a, b", "x=>y=>a,b;");
test_minify("x => y => (a = b)", "x=>y=>a=b;");
test_minify("x => y => z => a = b, c", "x=>y=>z=>a=b,c;");
Expand Down Expand Up @@ -415,6 +415,14 @@ fn pure_comment() {
test_same("false || /* @__PURE__ */ noEffect();\n");
}

#[test]
fn pife() {
test_same("foo((() => 0));\n");
test_minify_same("foo((()=>0));");
test_same("(() => 0)();\n");
test_minify_same("(()=>0)();");
}

// followup from https://github.com/oxc-project/oxc/pull/6422
#[test]
fn in_expr_in_sequence_in_for_loop_init() {
Expand Down
5 changes: 5 additions & 0 deletions crates/oxc_formatter/src/generated/ast_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7551,6 +7551,11 @@ impl<'a> AstNode<'a, ArrowFunctionExpression<'a>> {
self.inner.pure
}

#[inline]
pub fn pife(&self) -> bool {
self.inner.pife
}

pub fn format_leading_comments(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> {
format_leading_comments(self.span()).fmt(f)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_minifier/tests/peephole/esbuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ fn test_remove_dead_expr_nullish_related() {
test("x('' ?? 1)", "x('');");
test("x(/./ ?? 1)", "x(/./);");
test("x({} ?? 1)", "x({});");
test("x((() => {}) ?? 1)", "x(() => {});");
test("x((() => {}) ?? 1)", "x((() => {}));");
test("x(class {} ?? 1)", "x(class {});");
test("x(function() {} ?? 1)", "x(function() {});");
test("x(null ?? 1)", "x(1);");
Expand Down
6 changes: 5 additions & 1 deletion crates/oxc_parser/src/js/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,16 @@ impl<'a> ParserImpl<'a> {
self.expect(Kind::RParen);

// ParenthesizedExpression is from acorn --preserveParens
let expression = if expressions.len() == 1 {
let mut expression = if expressions.len() == 1 {
expressions.remove(0)
} else {
self.ast.expression_sequence(expr_span, expressions)
};

if let Expression::ArrowFunctionExpression(arrow_expr) = &mut expression {
arrow_expr.pife = true;
}

if self.options.preserve_parens {
self.ast.expression_parenthesized(self.end_span(span), expression)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -954,8 +954,8 @@ impl<'a> ArrowFunctionConverter<'a> {
);
let statements = ctx.ast.vec1(ctx.ast.statement_expression(SPAN, init));
let body = ctx.ast.function_body(SPAN, ctx.ast.vec(), statements);
let init = ctx.ast.expression_arrow_function_with_scope_id_and_pure(
SPAN, true, false, NONE, params, NONE, body, scope_id, false,
let init = ctx.ast.expression_arrow_function_with_scope_id_and_pure_and_pife(
SPAN, true, false, NONE, params, NONE, body, scope_id, false, false,
);
ctx.ast.variable_declarator(
SPAN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ impl<'a> NullishCoalescingOperator<'a, '_> {
ctx.ast.vec(),
ctx.ast.vec1(ctx.ast.statement_expression(SPAN, new_expr)),
);
let arrow_function = ctx.ast.expression_arrow_function_with_scope_id_and_pure(
let arrow_function = ctx.ast.expression_arrow_function_with_scope_id_and_pure_and_pife(
SPAN,
true,
false,
Expand All @@ -170,6 +170,7 @@ impl<'a> NullishCoalescingOperator<'a, '_> {
body,
current_scope_id,
false,
false,
);
// `(x) => x;` -> `((x) => x)();`
new_expr = ctx.ast.expression_call(SPAN, arrow_function, NONE, ctx.ast.vec(), false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ impl<'a> ClassProperties<'a, '_> {
let body = ctx.ast.vec1(ctx.ast.statement_expression(SPAN, body_exprs));

// `(..._args) => (super(..._args), <inits>, this)`
let super_func = ctx.ast.expression_arrow_function_with_scope_id_and_pure(
let super_func = ctx.ast.expression_arrow_function_with_scope_id_and_pure_and_pife(
SPAN,
true,
false,
Expand All @@ -321,6 +321,7 @@ impl<'a> ClassProperties<'a, '_> {
ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), body),
super_func_scope_id,
false,
false,
);

// `var _super = (..._args) => ( ... );`
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_transformer/src/utils/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ pub fn wrap_statements_in_arrow_function_iife<'a>(
let kind = FormalParameterKind::ArrowFormalParameters;
let params = ctx.ast.alloc_formal_parameters(SPAN, kind, ctx.ast.vec(), NONE);
let body = ctx.ast.alloc_function_body(SPAN, ctx.ast.vec(), stmts);
let arrow = ctx.ast.expression_arrow_function_with_scope_id_and_pure(
SPAN, false, false, NONE, params, NONE, body, scope_id, false,
let arrow = ctx.ast.expression_arrow_function_with_scope_id_and_pure_and_pife(
SPAN, false, false, NONE, params, NONE, body, scope_id, false, false,
);
ctx.ast.expression_call(span, arrow, NONE, ctx.ast.vec(), false)
}
Expand Down
Loading
Loading