perf(codegen): simplify print_space_before_identifier (#27841)

Perf optimization.

`print_space_before_identifier` is called before printing every identifier and keyword. Simplify it to reduce its size, so it's more likely to be inlined.

Previously it had a branch for when last character is non-ASCII. In that case, it collected the last character of the output (many instructions) and then called `is_identifier_part` which performs 2 table lookups and several more arithmetic/bitwise operations. This branch is extremely rarely taken, so the computation cost of these operations doesn't matter, but they do bloat the code significantly.

All this code is unnecessary.

Non-ASCII characters can appear in various places (e.g. in strings), but in such cases an ASCII character (e.g. `"`) is always printed after them. The only case in which last byte of output can be non-ASCII is when what was last printed was an identifier ending with a non-ASCII character.

Therefore, if the last byte printed is not ASCII, it must be the end of an identifier. All that's needed is a simple `byte.is_ascii()` check.
This commit is contained in:
overlookmotel committed 2026-10-16 20:05:29 +00:00
1 parent 29caafd7bc
commit eeff416a60
1 file changed
+8 -13
+8 -13
View File
@@ -16,7 +16,7 @@ use oxc_span::{GetSpan, SourceType, Span};
use oxc_str::CompactStr;
use oxc_syntax::{
class::ClassId,
identifier::{is_identifier_part, is_identifier_part_ascii},
identifier::is_identifier_part_ascii,
operator::{BinaryOperator, UnaryOperator, UpdateOperator},
precedence::Precedence,
};
@@ -684,13 +684,13 @@ impl<'a> Codegen<'a> {
let Some(byte) = self.last_byte() else { return };
if self.need_space_before_identifier != self.code.len() {
let is_identifier = if byte.is_ascii() {
// Fast path for ASCII (very common case)
is_identifier_part_ascii(byte as char)
} else {
is_identifier_part(self.last_char().unwrap())
};
if !is_identifier {
// Non-ASCII characters can appear in various places (e.g. in strings),
// but in such cases an ASCII character (e.g. `"`) is always printed after them.
// Only case in which last byte of output can be non-ASCII when printing an identifier
// or keyword (when this function is called) is when what was last printed was
// an identifier ending with a non-ASCII character.
// So non-ASCII must be an identifier, and a space is required.
if byte.is_ascii() && !is_identifier_part_ascii(byte as char) {
return;
}
}
@@ -703,11 +703,6 @@ impl<'a> Codegen<'a> {
self.code.last_byte()
}
#[inline]
fn last_char(&self) -> Option<char> {
self.code.last_char()
}
#[inline]
fn indent(&mut self) {
if !self.options.minify {