mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(ast/estree): reduce overhead serializing static strings (#9396)
By default, strings have to be processed to ensure they contain no characters which need to be escaped in JSON, and escape those characters when they do. This is quite slow. But many strings the codegen produces are statically known not to require escaping e.g. `"IdentifierReference"` in `{"type":"IdentifierReference",...}`.
Introduce `JsonSafeString` type. Wrapping a string in this type asserts that the string definitely doesn't need to be escaped, and serializer can skip checks.
In codegen for `ESTree` impls, check if static strings need escaping, and wrap them in `JsonSafeString` if they don't.
This commit is contained in:
1 parent
9b7017c2ae
commit
b0a0a82df1
8 files changed
+394
-347
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -2,8 +2,8 @@ use cow_utils::CowUtils;
|
||||
|
||||
use oxc_ast_macros::ast_meta;
|
||||
use oxc_estree::{
|
||||
CompactJSSerializer, CompactTSSerializer, ESTree, PrettyJSSerializer, PrettyTSSerializer,
|
||||
SequenceSerializer, Serializer, StructSerializer,
|
||||
CompactJSSerializer, CompactTSSerializer, ESTree, JsonSafeString, PrettyJSSerializer,
|
||||
PrettyTSSerializer, SequenceSerializer, Serializer, StructSerializer,
|
||||
};
|
||||
|
||||
use crate::ast::*;
|
||||
@@ -113,7 +113,7 @@ pub struct In<'b, T>(#[expect(dead_code)] pub &'b T);
|
||||
|
||||
impl<T> ESTree for In<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
"in".serialize(serializer);
|
||||
JsonSafeString("in").serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ pub struct Init<'b, T>(#[expect(dead_code)] pub &'b T);
|
||||
|
||||
impl<T> ESTree for Init<'_, T> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
"init".serialize(serializer);
|
||||
JsonSafeString("init").serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,9 +143,9 @@ impl ESTree for BooleanLiteralRaw<'_> {
|
||||
let raw = if self.0.span.is_unspanned() {
|
||||
None
|
||||
} else if self.0.value {
|
||||
Some("true")
|
||||
Some(JsonSafeString("true"))
|
||||
} else {
|
||||
Some("false")
|
||||
Some(JsonSafeString("false"))
|
||||
};
|
||||
raw.serialize(serializer);
|
||||
}
|
||||
@@ -159,7 +159,7 @@ pub struct NullLiteralRaw<'b>(pub &'b NullLiteral);
|
||||
impl ESTree for NullLiteralRaw<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
#[expect(clippy::collection_is_never_read)] // Clippy is wrong!
|
||||
let raw = if self.0.span.is_unspanned() { None } else { Some("null") };
|
||||
let raw = if self.0.span.is_unspanned() { None } else { Some(JsonSafeString("null")) };
|
||||
raw.serialize(serializer);
|
||||
}
|
||||
}
|
||||
@@ -171,8 +171,8 @@ pub struct BigIntLiteralBigint<'a, 'b>(pub &'b BigIntLiteral<'a>);
|
||||
|
||||
impl ESTree for BigIntLiteralBigint<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let bigint = self.0.raw.strip_suffix('n').unwrap().cow_replace('_', "");
|
||||
bigint.serialize(serializer);
|
||||
let bigint = self.0.raw[..self.0.raw.len() - 1].cow_replace('_', "");
|
||||
JsonSafeString(bigint.as_ref()).serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ impl ESTree for RegExpLiteralRegex<'_, '_> {
|
||||
if let Some(raw) = &self.0.raw {
|
||||
let flags_count = flags.bits().count_ones() as usize;
|
||||
let flags_index = raw.len() - flags_count;
|
||||
state.serialize_field("flags", &raw[flags_index..]);
|
||||
state.serialize_field("flags", &JsonSafeString(&raw[flags_index..]));
|
||||
} else {
|
||||
state.serialize_field("flags", &flags);
|
||||
}
|
||||
@@ -243,7 +243,7 @@ pub struct RegExpFlagsConverter<'b>(pub &'b RegExpFlags);
|
||||
|
||||
impl ESTree for RegExpFlagsConverter<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
self.0.to_string().serialize(serializer);
|
||||
JsonSafeString(self.0.to_string().as_str()).serialize(serializer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ impl ESTree for FormalParametersRest<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let rest = self.0;
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "RestElement");
|
||||
state.serialize_field("type", &JsonSafeString("RestElement"));
|
||||
state.serialize_field("start", &rest.span.start);
|
||||
state.serialize_field("end", &rest.span.end);
|
||||
state.serialize_field("argument", &rest.argument.kind);
|
||||
@@ -324,7 +324,7 @@ impl ESTree for ObjectPropertyConverter<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let prop = self.0;
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Property");
|
||||
state.serialize_field("type", &JsonSafeString("Property"));
|
||||
state.serialize_field("start", &prop.span.start);
|
||||
state.serialize_field("end", &prop.span.end);
|
||||
state.serialize_field("method", &prop.method);
|
||||
@@ -351,7 +351,7 @@ impl ESTree for BindingPropertyConverter<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let prop = self.0;
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Property");
|
||||
state.serialize_field("type", &JsonSafeString("Property"));
|
||||
state.serialize_field("start", &prop.span.start);
|
||||
state.serialize_field("end", &prop.span.end);
|
||||
state.serialize_field("method", &false);
|
||||
@@ -360,11 +360,11 @@ impl ESTree for BindingPropertyConverter<'_, '_> {
|
||||
state.serialize_field("key", &prop.key);
|
||||
// Acorn has `kind` field before `value` for shorthand properties
|
||||
if prop.shorthand {
|
||||
state.serialize_field("kind", "init");
|
||||
state.serialize_field("kind", &JsonSafeString("init"));
|
||||
state.serialize_field("value", &prop.value);
|
||||
} else {
|
||||
state.serialize_field("value", &prop.value);
|
||||
state.serialize_field("kind", "init");
|
||||
state.serialize_field("kind", &JsonSafeString("init"));
|
||||
}
|
||||
state.end();
|
||||
}
|
||||
@@ -400,7 +400,7 @@ impl ESTree for AssignmentTargetPropertyIdentifierValue<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
if let Some(init) = &self.0.init {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "AssignmentPattern");
|
||||
state.serialize_field("type", &JsonSafeString("AssignmentPattern"));
|
||||
state.serialize_field("start", &self.0.span.start);
|
||||
state.serialize_field("end", &self.0.span.end);
|
||||
state.serialize_field("left", &self.0.binding);
|
||||
@@ -441,7 +441,7 @@ impl ESTree for ExportNamedDeclarationConverter<'_, '_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let decl = self.0;
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "ExportNamedDeclaration");
|
||||
state.serialize_field("type", &JsonSafeString("ExportNamedDeclaration"));
|
||||
state.serialize_field("start", &decl.span.start);
|
||||
state.serialize_field("end", &decl.span.end);
|
||||
state.serialize_field("declaration", &decl.declaration);
|
||||
|
||||
@@ -16,6 +16,7 @@ use sequences::ESTreeSequenceSerializer;
|
||||
use structs::ESTreeStructSerializer;
|
||||
|
||||
pub use sequences::SequenceSerializer;
|
||||
pub use strings::JsonSafeString;
|
||||
pub use structs::{FlatStructSerializer, StructSerializer};
|
||||
|
||||
/// Trait for types which can be serialized to ESTree.
|
||||
|
||||
@@ -2,6 +2,24 @@ use oxc_data_structures::code_buffer::CodeBuffer;
|
||||
|
||||
use super::{ESTree, Serializer};
|
||||
|
||||
/// A string which does not need any escaping in JSON.
|
||||
///
|
||||
/// This provides better performance when you know that the string definitely contains no characters
|
||||
/// that require escaping, as it avoids the cost of checking that.
|
||||
///
|
||||
/// If the string does in fact contain characters that did need escaping, it will result in invalid JSON.
|
||||
pub struct JsonSafeString<'s>(pub &'s str);
|
||||
|
||||
impl ESTree for JsonSafeString<'_> {
|
||||
#[inline(always)]
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
let buffer = serializer.buffer_mut();
|
||||
buffer.print_ascii_byte(b'"');
|
||||
buffer.print_str(self.0);
|
||||
buffer.print_ascii_byte(b'"');
|
||||
}
|
||||
}
|
||||
|
||||
/// [`ESTree`] implementation for string slice.
|
||||
impl ESTree for str {
|
||||
fn serialize<S: Serializer>(&self, mut serializer: S) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
use oxc_estree::{
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
ESTree, FlatStructSerializer, JsonSafeString, Serializer, StructSerializer,
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::ast::*;
|
||||
impl ESTree for Pattern<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Pattern");
|
||||
state.serialize_field("type", &JsonSafeString("Pattern"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("body", &self.body);
|
||||
@@ -24,7 +24,7 @@ impl ESTree for Pattern<'_> {
|
||||
impl ESTree for Disjunction<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Disjunction");
|
||||
state.serialize_field("type", &JsonSafeString("Disjunction"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("body", &self.body);
|
||||
@@ -35,7 +35,7 @@ impl ESTree for Disjunction<'_> {
|
||||
impl ESTree for Alternative<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Alternative");
|
||||
state.serialize_field("type", &JsonSafeString("Alternative"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("body", &self.body);
|
||||
@@ -65,7 +65,7 @@ impl ESTree for Term<'_> {
|
||||
impl ESTree for BoundaryAssertion {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "BoundaryAssertion");
|
||||
state.serialize_field("type", &JsonSafeString("BoundaryAssertion"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
@@ -76,10 +76,10 @@ impl ESTree for BoundaryAssertion {
|
||||
impl ESTree for BoundaryAssertionKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Start => "start".serialize(serializer),
|
||||
Self::End => "end".serialize(serializer),
|
||||
Self::Boundary => "boundary".serialize(serializer),
|
||||
Self::NegativeBoundary => "negativeBoundary".serialize(serializer),
|
||||
Self::Start => JsonSafeString("start").serialize(serializer),
|
||||
Self::End => JsonSafeString("end").serialize(serializer),
|
||||
Self::Boundary => JsonSafeString("boundary").serialize(serializer),
|
||||
Self::NegativeBoundary => JsonSafeString("negativeBoundary").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ impl ESTree for BoundaryAssertionKind {
|
||||
impl ESTree for LookAroundAssertion<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "LookAroundAssertion");
|
||||
state.serialize_field("type", &JsonSafeString("LookAroundAssertion"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
@@ -99,10 +99,10 @@ impl ESTree for LookAroundAssertion<'_> {
|
||||
impl ESTree for LookAroundAssertionKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Lookahead => "lookahead".serialize(serializer),
|
||||
Self::NegativeLookahead => "negativeLookahead".serialize(serializer),
|
||||
Self::Lookbehind => "lookbehind".serialize(serializer),
|
||||
Self::NegativeLookbehind => "negativeLookbehind".serialize(serializer),
|
||||
Self::Lookahead => JsonSafeString("lookahead").serialize(serializer),
|
||||
Self::NegativeLookahead => JsonSafeString("negativeLookahead").serialize(serializer),
|
||||
Self::Lookbehind => JsonSafeString("lookbehind").serialize(serializer),
|
||||
Self::NegativeLookbehind => JsonSafeString("negativeLookbehind").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ impl ESTree for LookAroundAssertionKind {
|
||||
impl ESTree for Quantifier<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Quantifier");
|
||||
state.serialize_field("type", &JsonSafeString("Quantifier"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("min", &self.min);
|
||||
@@ -124,7 +124,7 @@ impl ESTree for Quantifier<'_> {
|
||||
impl ESTree for Character {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Character");
|
||||
state.serialize_field("type", &JsonSafeString("Character"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
@@ -136,16 +136,16 @@ impl ESTree for Character {
|
||||
impl ESTree for CharacterKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::ControlLetter => "controlLetter".serialize(serializer),
|
||||
Self::HexadecimalEscape => "hexadecimalEscape".serialize(serializer),
|
||||
Self::Identifier => "identifier".serialize(serializer),
|
||||
Self::Null => "null".serialize(serializer),
|
||||
Self::Octal1 => "octal1".serialize(serializer),
|
||||
Self::Octal2 => "octal2".serialize(serializer),
|
||||
Self::Octal3 => "octal3".serialize(serializer),
|
||||
Self::SingleEscape => "singleEscape".serialize(serializer),
|
||||
Self::Symbol => "symbol".serialize(serializer),
|
||||
Self::UnicodeEscape => "unicodeEscape".serialize(serializer),
|
||||
Self::ControlLetter => JsonSafeString("controlLetter").serialize(serializer),
|
||||
Self::HexadecimalEscape => JsonSafeString("hexadecimalEscape").serialize(serializer),
|
||||
Self::Identifier => JsonSafeString("identifier").serialize(serializer),
|
||||
Self::Null => JsonSafeString("null").serialize(serializer),
|
||||
Self::Octal1 => JsonSafeString("octal1").serialize(serializer),
|
||||
Self::Octal2 => JsonSafeString("octal2").serialize(serializer),
|
||||
Self::Octal3 => JsonSafeString("octal3").serialize(serializer),
|
||||
Self::SingleEscape => JsonSafeString("singleEscape").serialize(serializer),
|
||||
Self::Symbol => JsonSafeString("symbol").serialize(serializer),
|
||||
Self::UnicodeEscape => JsonSafeString("unicodeEscape").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ impl ESTree for CharacterKind {
|
||||
impl ESTree for CharacterClassEscape {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CharacterClassEscape");
|
||||
state.serialize_field("type", &JsonSafeString("CharacterClassEscape"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("kind", &self.kind);
|
||||
@@ -164,12 +164,12 @@ impl ESTree for CharacterClassEscape {
|
||||
impl ESTree for CharacterClassEscapeKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::D => "d".serialize(serializer),
|
||||
Self::NegativeD => "negativeD".serialize(serializer),
|
||||
Self::S => "s".serialize(serializer),
|
||||
Self::NegativeS => "negativeS".serialize(serializer),
|
||||
Self::W => "w".serialize(serializer),
|
||||
Self::NegativeW => "negativeW".serialize(serializer),
|
||||
Self::D => JsonSafeString("d").serialize(serializer),
|
||||
Self::NegativeD => JsonSafeString("negativeD").serialize(serializer),
|
||||
Self::S => JsonSafeString("s").serialize(serializer),
|
||||
Self::NegativeS => JsonSafeString("negativeS").serialize(serializer),
|
||||
Self::W => JsonSafeString("w").serialize(serializer),
|
||||
Self::NegativeW => JsonSafeString("negativeW").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ impl ESTree for CharacterClassEscapeKind {
|
||||
impl ESTree for UnicodePropertyEscape<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "UnicodePropertyEscape");
|
||||
state.serialize_field("type", &JsonSafeString("UnicodePropertyEscape"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("negative", &self.negative);
|
||||
@@ -191,7 +191,7 @@ impl ESTree for UnicodePropertyEscape<'_> {
|
||||
impl ESTree for Dot {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Dot");
|
||||
state.serialize_field("type", &JsonSafeString("Dot"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.end();
|
||||
@@ -201,7 +201,7 @@ impl ESTree for Dot {
|
||||
impl ESTree for CharacterClass<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CharacterClass");
|
||||
state.serialize_field("type", &JsonSafeString("CharacterClass"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("negative", &self.negative);
|
||||
@@ -215,9 +215,9 @@ impl ESTree for CharacterClass<'_> {
|
||||
impl ESTree for CharacterClassContentsKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Union => "union".serialize(serializer),
|
||||
Self::Intersection => "intersection".serialize(serializer),
|
||||
Self::Subtraction => "subtraction".serialize(serializer),
|
||||
Self::Union => JsonSafeString("union").serialize(serializer),
|
||||
Self::Intersection => JsonSafeString("intersection").serialize(serializer),
|
||||
Self::Subtraction => JsonSafeString("subtraction").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +238,7 @@ impl ESTree for CharacterClassContents<'_> {
|
||||
impl ESTree for CharacterClassRange {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CharacterClassRange");
|
||||
state.serialize_field("type", &JsonSafeString("CharacterClassRange"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("min", &self.min);
|
||||
@@ -250,7 +250,7 @@ impl ESTree for CharacterClassRange {
|
||||
impl ESTree for ClassStringDisjunction<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "ClassStringDisjunction");
|
||||
state.serialize_field("type", &JsonSafeString("ClassStringDisjunction"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("strings", &self.strings);
|
||||
@@ -262,7 +262,7 @@ impl ESTree for ClassStringDisjunction<'_> {
|
||||
impl ESTree for ClassString<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "ClassString");
|
||||
state.serialize_field("type", &JsonSafeString("ClassString"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("strings", &self.strings);
|
||||
@@ -274,7 +274,7 @@ impl ESTree for ClassString<'_> {
|
||||
impl ESTree for CapturingGroup<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "CapturingGroup");
|
||||
state.serialize_field("type", &JsonSafeString("CapturingGroup"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("name", &self.name);
|
||||
@@ -286,7 +286,7 @@ impl ESTree for CapturingGroup<'_> {
|
||||
impl ESTree for IgnoreGroup<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "IgnoreGroup");
|
||||
state.serialize_field("type", &JsonSafeString("IgnoreGroup"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("modifiers", &self.modifiers);
|
||||
@@ -298,7 +298,7 @@ impl ESTree for IgnoreGroup<'_> {
|
||||
impl ESTree for Modifiers {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Modifiers");
|
||||
state.serialize_field("type", &JsonSafeString("Modifiers"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("enabling", &self.enabling);
|
||||
@@ -310,7 +310,7 @@ impl ESTree for Modifiers {
|
||||
impl ESTree for Modifier {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "Modifier");
|
||||
state.serialize_field("type", &JsonSafeString("Modifier"));
|
||||
state.serialize_field("ignoreCase", &self.ignore_case);
|
||||
state.serialize_field("multiline", &self.multiline);
|
||||
state.serialize_field("sticky", &self.sticky);
|
||||
@@ -321,7 +321,7 @@ impl ESTree for Modifier {
|
||||
impl ESTree for IndexedReference {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "IndexedReference");
|
||||
state.serialize_field("type", &JsonSafeString("IndexedReference"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("index", &self.index);
|
||||
@@ -332,7 +332,7 @@ impl ESTree for IndexedReference {
|
||||
impl ESTree for NamedReference<'_> {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
let mut state = serializer.serialize_struct();
|
||||
state.serialize_field("type", "NamedReference");
|
||||
state.serialize_field("type", &JsonSafeString("NamedReference"));
|
||||
state.serialize_field("start", &self.span.start);
|
||||
state.serialize_field("end", &self.span.end);
|
||||
state.serialize_field("name", &self.name);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
use oxc_estree::{
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
ESTree, FlatStructSerializer, JsonSafeString, Serializer, StructSerializer,
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
};
|
||||
|
||||
@@ -31,9 +31,9 @@ impl ESTree for SourceType {
|
||||
impl ESTree for ModuleKind {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Script => "script".serialize(serializer),
|
||||
Self::Module => "module".serialize(serializer),
|
||||
Self::Unambiguous => "unambiguous".serialize(serializer),
|
||||
Self::Script => JsonSafeString("script").serialize(serializer),
|
||||
Self::Module => JsonSafeString("module").serialize(serializer),
|
||||
Self::Unambiguous => JsonSafeString("unambiguous").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
use oxc_estree::{
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
ESTree, FlatStructSerializer, JsonSafeString, Serializer, StructSerializer,
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
};
|
||||
|
||||
@@ -13,22 +13,22 @@ use crate::operator::*;
|
||||
impl ESTree for AssignmentOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Assign => "=".serialize(serializer),
|
||||
Self::Addition => "+=".serialize(serializer),
|
||||
Self::Subtraction => "-=".serialize(serializer),
|
||||
Self::Multiplication => "*=".serialize(serializer),
|
||||
Self::Division => "/=".serialize(serializer),
|
||||
Self::Remainder => "%=".serialize(serializer),
|
||||
Self::Exponential => "**=".serialize(serializer),
|
||||
Self::ShiftLeft => "<<=".serialize(serializer),
|
||||
Self::ShiftRight => ">>=".serialize(serializer),
|
||||
Self::ShiftRightZeroFill => ">>>=".serialize(serializer),
|
||||
Self::BitwiseOR => "|=".serialize(serializer),
|
||||
Self::BitwiseXOR => "^=".serialize(serializer),
|
||||
Self::BitwiseAnd => "&=".serialize(serializer),
|
||||
Self::LogicalOr => "||=".serialize(serializer),
|
||||
Self::LogicalAnd => "&&=".serialize(serializer),
|
||||
Self::LogicalNullish => "??=".serialize(serializer),
|
||||
Self::Assign => JsonSafeString("=").serialize(serializer),
|
||||
Self::Addition => JsonSafeString("+=").serialize(serializer),
|
||||
Self::Subtraction => JsonSafeString("-=").serialize(serializer),
|
||||
Self::Multiplication => JsonSafeString("*=").serialize(serializer),
|
||||
Self::Division => JsonSafeString("/=").serialize(serializer),
|
||||
Self::Remainder => JsonSafeString("%=").serialize(serializer),
|
||||
Self::Exponential => JsonSafeString("**=").serialize(serializer),
|
||||
Self::ShiftLeft => JsonSafeString("<<=").serialize(serializer),
|
||||
Self::ShiftRight => JsonSafeString(">>=").serialize(serializer),
|
||||
Self::ShiftRightZeroFill => JsonSafeString(">>>=").serialize(serializer),
|
||||
Self::BitwiseOR => JsonSafeString("|=").serialize(serializer),
|
||||
Self::BitwiseXOR => JsonSafeString("^=").serialize(serializer),
|
||||
Self::BitwiseAnd => JsonSafeString("&=").serialize(serializer),
|
||||
Self::LogicalOr => JsonSafeString("||=").serialize(serializer),
|
||||
Self::LogicalAnd => JsonSafeString("&&=").serialize(serializer),
|
||||
Self::LogicalNullish => JsonSafeString("??=").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,28 +36,28 @@ impl ESTree for AssignmentOperator {
|
||||
impl ESTree for BinaryOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Equality => "==".serialize(serializer),
|
||||
Self::Inequality => "!=".serialize(serializer),
|
||||
Self::StrictEquality => "===".serialize(serializer),
|
||||
Self::StrictInequality => "!==".serialize(serializer),
|
||||
Self::LessThan => "<".serialize(serializer),
|
||||
Self::LessEqualThan => "<=".serialize(serializer),
|
||||
Self::GreaterThan => ">".serialize(serializer),
|
||||
Self::GreaterEqualThan => ">=".serialize(serializer),
|
||||
Self::Addition => "+".serialize(serializer),
|
||||
Self::Subtraction => "-".serialize(serializer),
|
||||
Self::Multiplication => "*".serialize(serializer),
|
||||
Self::Division => "/".serialize(serializer),
|
||||
Self::Remainder => "%".serialize(serializer),
|
||||
Self::Exponential => "**".serialize(serializer),
|
||||
Self::ShiftLeft => "<<".serialize(serializer),
|
||||
Self::ShiftRight => ">>".serialize(serializer),
|
||||
Self::ShiftRightZeroFill => ">>>".serialize(serializer),
|
||||
Self::BitwiseOR => "|".serialize(serializer),
|
||||
Self::BitwiseXOR => "^".serialize(serializer),
|
||||
Self::BitwiseAnd => "&".serialize(serializer),
|
||||
Self::In => "in".serialize(serializer),
|
||||
Self::Instanceof => "instanceof".serialize(serializer),
|
||||
Self::Equality => JsonSafeString("==").serialize(serializer),
|
||||
Self::Inequality => JsonSafeString("!=").serialize(serializer),
|
||||
Self::StrictEquality => JsonSafeString("===").serialize(serializer),
|
||||
Self::StrictInequality => JsonSafeString("!==").serialize(serializer),
|
||||
Self::LessThan => JsonSafeString("<").serialize(serializer),
|
||||
Self::LessEqualThan => JsonSafeString("<=").serialize(serializer),
|
||||
Self::GreaterThan => JsonSafeString(">").serialize(serializer),
|
||||
Self::GreaterEqualThan => JsonSafeString(">=").serialize(serializer),
|
||||
Self::Addition => JsonSafeString("+").serialize(serializer),
|
||||
Self::Subtraction => JsonSafeString("-").serialize(serializer),
|
||||
Self::Multiplication => JsonSafeString("*").serialize(serializer),
|
||||
Self::Division => JsonSafeString("/").serialize(serializer),
|
||||
Self::Remainder => JsonSafeString("%").serialize(serializer),
|
||||
Self::Exponential => JsonSafeString("**").serialize(serializer),
|
||||
Self::ShiftLeft => JsonSafeString("<<").serialize(serializer),
|
||||
Self::ShiftRight => JsonSafeString(">>").serialize(serializer),
|
||||
Self::ShiftRightZeroFill => JsonSafeString(">>>").serialize(serializer),
|
||||
Self::BitwiseOR => JsonSafeString("|").serialize(serializer),
|
||||
Self::BitwiseXOR => JsonSafeString("^").serialize(serializer),
|
||||
Self::BitwiseAnd => JsonSafeString("&").serialize(serializer),
|
||||
Self::In => JsonSafeString("in").serialize(serializer),
|
||||
Self::Instanceof => JsonSafeString("instanceof").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,9 +65,9 @@ impl ESTree for BinaryOperator {
|
||||
impl ESTree for LogicalOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Or => "||".serialize(serializer),
|
||||
Self::And => "&&".serialize(serializer),
|
||||
Self::Coalesce => "??".serialize(serializer),
|
||||
Self::Or => JsonSafeString("||").serialize(serializer),
|
||||
Self::And => JsonSafeString("&&").serialize(serializer),
|
||||
Self::Coalesce => JsonSafeString("??").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,13 +75,13 @@ impl ESTree for LogicalOperator {
|
||||
impl ESTree for UnaryOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::UnaryPlus => "+".serialize(serializer),
|
||||
Self::UnaryNegation => "-".serialize(serializer),
|
||||
Self::LogicalNot => "!".serialize(serializer),
|
||||
Self::BitwiseNot => "~".serialize(serializer),
|
||||
Self::Typeof => "typeof".serialize(serializer),
|
||||
Self::Void => "void".serialize(serializer),
|
||||
Self::Delete => "delete".serialize(serializer),
|
||||
Self::UnaryPlus => JsonSafeString("+").serialize(serializer),
|
||||
Self::UnaryNegation => JsonSafeString("-").serialize(serializer),
|
||||
Self::LogicalNot => JsonSafeString("!").serialize(serializer),
|
||||
Self::BitwiseNot => JsonSafeString("~").serialize(serializer),
|
||||
Self::Typeof => JsonSafeString("typeof").serialize(serializer),
|
||||
Self::Void => JsonSafeString("void").serialize(serializer),
|
||||
Self::Delete => JsonSafeString("delete").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,8 +89,8 @@ impl ESTree for UnaryOperator {
|
||||
impl ESTree for UpdateOperator {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) {
|
||||
match self {
|
||||
Self::Increment => "++".serialize(serializer),
|
||||
Self::Decrement => "--".serialize(serializer),
|
||||
Self::Increment => JsonSafeString("++").serialize(serializer),
|
||||
Self::Decrement => JsonSafeString("--").serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ impl Derive for DeriveESTree {
|
||||
///@@line_break
|
||||
use oxc_estree::{
|
||||
ser::{AppendTo, AppendToConcat},
|
||||
ESTree, FlatStructSerializer, Serializer, StructSerializer,
|
||||
ESTree, FlatStructSerializer, JsonSafeString, Serializer, StructSerializer,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -248,9 +248,8 @@ fn generate_body_for_struct(struct_def: &StructDef, schema: &Schema) -> TokenStr
|
||||
|
||||
let type_field = if g.add_type_field {
|
||||
let type_name = struct_def.estree.rename.as_deref().unwrap_or_else(|| struct_def.name());
|
||||
quote! {
|
||||
state.serialize_field("type", #type_name);
|
||||
}
|
||||
let type_name = string_to_tokens(type_name, true);
|
||||
quote!( state.serialize_field("type", #type_name); )
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
@@ -408,6 +407,7 @@ fn generate_body_for_enum(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
|
||||
let variant_ident = variant.ident();
|
||||
if variant.is_fieldless() {
|
||||
let value = get_fieldless_variant_value(enum_def, variant);
|
||||
let value = string_to_tokens(value.as_ref(), false);
|
||||
quote! {
|
||||
Self::#variant_ident => #value.serialize(serializer),
|
||||
}
|
||||
@@ -524,3 +524,21 @@ pub fn get_struct_field_name(field: &FieldDef) -> Cow<'_, str> {
|
||||
Cow::Owned(field.camel_name())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert string to [`TokenStream`] representing string literal.
|
||||
///
|
||||
/// If the string contains no characters which need escaping in JSON,
|
||||
/// returns tokens for `JsonSafeString("string")`, which is faster to serialize.
|
||||
///
|
||||
/// If `as_ref` is `true`, and string is JSON-safe, returns tokens for `&JsonSafeString("string")`.
|
||||
fn string_to_tokens(str: &str, as_ref: bool) -> TokenStream {
|
||||
let contains_chars_needing_escaping =
|
||||
str.as_bytes().iter().any(|&b| b < 32 || b == b'"' || b == b'\\');
|
||||
if contains_chars_needing_escaping {
|
||||
quote!(#str)
|
||||
} else if as_ref {
|
||||
quote!( &JsonSafeString(#str) )
|
||||
} else {
|
||||
quote!( JsonSafeString(#str) )
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user