mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(parser): build simple arrow bindings directly (#28493)
Build simple arrow parameters directly as bindings, avoiding a temporary identifier-reference allocation. Based on #28491. Validated parser conformance, annotations, bindings, and allocations. AI-assisted.
This commit is contained in:
1 parent
e1c9baf001
commit
b3b5444712
8 files changed
+99
-26
No files matched your search
@@ -38,14 +38,17 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let start = self.cur_start();
|
||||
self.bump_any(); // bump `async`
|
||||
// Lookahead established a single binding identifier followed by `=>`.
|
||||
// An ordinary identifier cannot extend into a member, call, or binary expression here.
|
||||
let expr = if self.at(Kind::Ident) {
|
||||
self.parse_plain_identifier_expression()
|
||||
// Build the binding directly instead of allocating a temporary identifier reference.
|
||||
let ident = if self.at(Kind::Ident) {
|
||||
self.parse_plain_binding_identifier()
|
||||
} else if self.cur_kind().is_identifier() {
|
||||
self.parse_known_binding_identifier()
|
||||
} else {
|
||||
self.parse_binary_expression_or_higher(Precedence::Comma)
|
||||
};
|
||||
let Expression::Identifier(ident) = &expr else {
|
||||
return self.unexpected();
|
||||
let expr = self.parse_binary_expression_or_higher(Precedence::Comma);
|
||||
let Expression::Identifier(ident) = &expr else {
|
||||
return self.unexpected();
|
||||
};
|
||||
BindingIdentifier::new(ident.span, ident.name, self)
|
||||
};
|
||||
// It is a Syntax Error if ArrowParameters Contains AwaitExpression is true.
|
||||
if ident.name == "await" {
|
||||
@@ -256,14 +259,15 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
pub(crate) fn parse_simple_arrow_function_expression(
|
||||
&mut self,
|
||||
start: u32,
|
||||
ident: &IdentifierReference<'a>,
|
||||
ident: BindingIdentifier<'a>,
|
||||
r#async: bool,
|
||||
allow_return_type_in_arrow_function: bool,
|
||||
) -> Expression<'a> {
|
||||
let pattern = BindingPattern::new_binding_identifier(ident.span, ident.name, self);
|
||||
let formal_parameter = FormalParameter::new_plain(ident.span, pattern, self);
|
||||
let span = ident.span;
|
||||
let pattern = BindingPattern::BindingIdentifier(self.alloc(ident));
|
||||
let formal_parameter = FormalParameter::new_plain(span, pattern, self);
|
||||
let params = FormalParameters::boxed(
|
||||
ident.span,
|
||||
span,
|
||||
FormalParameterKind::ArrowFormalParameters,
|
||||
[formal_parameter],
|
||||
None,
|
||||
|
||||
@@ -1642,7 +1642,30 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
let lhs = if matches!(kind, Kind::Ident | Kind::This) || kind.is_literal() {
|
||||
let pure_comments = self.lexer.trivia_builder.previous_token_pure_comments();
|
||||
let primary = match kind {
|
||||
Kind::Ident => self.parse_plain_identifier_expression(),
|
||||
Kind::Ident => {
|
||||
let (span, name) = self.parse_identifier_kind(Kind::Ident);
|
||||
// Only allocate an identifier reference once the following token
|
||||
// establishes that this is an expression rather than an arrow head.
|
||||
if self.at(Kind::Arrow) {
|
||||
let binding = BindingIdentifier::new(span, name, self);
|
||||
let mut arrow_expr = self.parse_simple_arrow_function_expression(
|
||||
start,
|
||||
binding,
|
||||
/* async */ false,
|
||||
allow_return_type_in_arrow_function,
|
||||
);
|
||||
if let Some(comments) = no_side_effects_comments
|
||||
&& let Expression::ArrowFunctionExpression(func) = &mut arrow_expr
|
||||
{
|
||||
func.pure = true;
|
||||
self.lexer
|
||||
.trivia_builder
|
||||
.mark_no_side_effects_comments_applied(comments);
|
||||
}
|
||||
return arrow_expr;
|
||||
}
|
||||
Expression::new_identifier(span, name, self)
|
||||
}
|
||||
Kind::This => self.parse_this_expression(),
|
||||
_ => self.parse_literal_expression(),
|
||||
};
|
||||
@@ -1708,9 +1731,10 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
if kind == Kind::Arrow
|
||||
&& let Expression::Identifier(ident) = &lhs
|
||||
{
|
||||
let binding = BindingIdentifier::new(ident.span, ident.name, self);
|
||||
let mut arrow_expr = self.parse_simple_arrow_function_expression(
|
||||
start,
|
||||
ident,
|
||||
binding,
|
||||
/* async */ false,
|
||||
allow_return_type_in_arrow_function,
|
||||
);
|
||||
|
||||
@@ -1076,6 +1076,51 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_arrow_no_side_effects_annotations() {
|
||||
for source in [
|
||||
"/*#__NO_SIDE_EFFECTS__*/ value => value",
|
||||
"/*#__NO_SIDE_EFFECTS__*/ async value => value",
|
||||
"/*#__NO_SIDE_EFFECTS__*/ async of => of",
|
||||
] {
|
||||
let allocator = Allocator::default();
|
||||
let expr =
|
||||
Parser::new(&allocator, source, SourceType::default()).parse_expression().unwrap();
|
||||
let Expression::ArrowFunctionExpression(arrow) = expr else { panic!("{source}") };
|
||||
assert!(arrow.pure, "{source}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn identifier_continuation_pure_annotations() {
|
||||
for source in [
|
||||
"/*#__PURE__*/ callee()",
|
||||
"/*#__PURE__*/ callee<T>()",
|
||||
"/*#__PURE__*/ callee!.method()",
|
||||
] {
|
||||
let allocator = Allocator::default();
|
||||
let expr =
|
||||
Parser::new(&allocator, source, SourceType::ts()).parse_expression().unwrap();
|
||||
let Expression::CallExpression(call) = expr else { panic!("{source}") };
|
||||
assert!(call.pure, "{source}");
|
||||
}
|
||||
|
||||
// Check both the assignment-expression entry and a binary RHS continuation.
|
||||
for (source, annotated_left) in [
|
||||
("/*#__PURE__*/ callee() + other()", true),
|
||||
("other() + /*#__PURE__*/ callee()", false),
|
||||
] {
|
||||
let allocator = Allocator::default();
|
||||
let expr =
|
||||
Parser::new(&allocator, source, SourceType::default()).parse_expression().unwrap();
|
||||
let Expression::BinaryExpression(binary) = expr else { panic!("{source}") };
|
||||
let Expression::CallExpression(left) = &binary.left else { panic!("{source}") };
|
||||
let Expression::CallExpression(right) = &binary.right else { panic!("{source}") };
|
||||
assert_eq!(left.pure, annotated_left, "{source}");
|
||||
assert_eq!(right.pure, !annotated_left, "{source}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flow_error() {
|
||||
let allocator = Allocator::default();
|
||||
|
||||
@@ -7,7 +7,7 @@ checker.ts:
|
||||
sys peak growth: 2973842 # 2.97 MB
|
||||
arena allocs: 36968
|
||||
arena reallocs: 927
|
||||
arena size: 38503224 # 38.50 MB
|
||||
arena size: 38485784 # 38.49 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
@@ -40,7 +40,7 @@ pdf.mjs:
|
||||
sys peak growth: 1156488 # 1.16 MB
|
||||
arena allocs: 21652
|
||||
arena reallocs: 402
|
||||
arena size: 10987960 # 10.99 MB
|
||||
arena size: 10983512 # 10.98 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
|
||||
@@ -7,7 +7,7 @@ checker.ts:
|
||||
sys peak growth: 10169560 # 10.17 MB
|
||||
arena allocs: 56228
|
||||
arena reallocs: 5084
|
||||
arena size: 16339184 # 16.34 MB
|
||||
arena size: 16321744 # 16.32 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
@@ -40,7 +40,7 @@ pdf.mjs:
|
||||
sys peak growth: 3693355 # 3.69 MB
|
||||
arena allocs: 19582
|
||||
arena reallocs: 2574
|
||||
arena size: 5666384 # 5.67 MB
|
||||
arena size: 5661936 # 5.66 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
|
||||
@@ -5,9 +5,9 @@ checker.ts:
|
||||
sys deallocs: 19
|
||||
sys alloc bytes: 4820 # 4.82 kB
|
||||
sys peak growth: 2432 # 2.43 kB
|
||||
arena allocs: 259857
|
||||
arena allocs: 259312
|
||||
arena reallocs: 15233
|
||||
arena size: 12892944 # 12.89 MB
|
||||
arena size: 12875504 # 12.88 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
@@ -38,9 +38,9 @@ pdf.mjs:
|
||||
sys deallocs: 61
|
||||
sys alloc bytes: 24020 # 24.02 kB
|
||||
sys peak growth: 12140 # 12.14 kB
|
||||
arena allocs: 89079
|
||||
arena allocs: 88940
|
||||
arena reallocs: 5904
|
||||
arena size: 4492488 # 4.49 MB
|
||||
arena size: 4488040 # 4.49 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
@@ -60,7 +60,7 @@ binder.ts:
|
||||
sys deallocs: 0
|
||||
sys alloc bytes: 3016 # 3.02 kB
|
||||
sys peak growth: 251 # 251 B
|
||||
arena allocs: 16465
|
||||
arena allocs: 16454
|
||||
arena reallocs: 1093
|
||||
arena size: 915968 # 915.97 kB
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ checker.ts:
|
||||
sys peak growth: 3858362 # 3.86 MB
|
||||
arena allocs: 0
|
||||
arena reallocs: 0
|
||||
arena size: 12892944 # 12.89 MB
|
||||
arena size: 12875504 # 12.88 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
@@ -40,7 +40,7 @@ pdf.mjs:
|
||||
sys peak growth: 832762 # 832.76 kB
|
||||
arena allocs: 0
|
||||
arena reallocs: 0
|
||||
arena size: 4492488 # 4.49 MB
|
||||
arena size: 4488040 # 4.49 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
|
||||
@@ -7,7 +7,7 @@ checker.ts:
|
||||
sys peak growth: 2243 # 2.24 kB
|
||||
arena allocs: 1959
|
||||
arena reallocs: 5
|
||||
arena size: 12981400 # 12.98 MB
|
||||
arena size: 12963960 # 12.96 MB
|
||||
|
||||
App.tsx:
|
||||
file size: 415342 # 415.34 kB
|
||||
@@ -40,7 +40,7 @@ pdf.mjs:
|
||||
sys peak growth: 1610 # 1.61 kB
|
||||
arena allocs: 2
|
||||
arena reallocs: 0
|
||||
arena size: 4492512 # 4.49 MB
|
||||
arena size: 4488064 # 4.49 MB
|
||||
|
||||
antd.js:
|
||||
file size: 6686316 # 6.69 MB
|
||||
|
||||
Reference in new issue
Block a user