feat(ast/estree)!: replace serde with custom ESTree serializer (#9256)

Replace `serde` with a custom serializer specialized for writing JSON.

`impl Serialize` -> `impl ESTree`. I've called it `ESTree` in case we
want to add e.g. `impl Babel` later on.

The main motivation is that with control of the serializer, we can make
it stateful, so we can extend it to, for example, omit TS fields based
on a flag. This is pretty much impossible with `serde`'s interface.

But a side-effect of the simpler implementation is that it's also more
performant.

The usage of `ESTree` trait is very similar to `serde::Serialize`. Main
differences:

* `serialize` method is infallible. Does not return a `Result`.
* `state = serializer.serialize_struct();` instead of `map =
serializer.serialize_map();`.
* `state.serialize_field(...)` instead of `map.serialize_entry(...)`.
* Struct keys must be strings, and JSON-safe (not including `"` or other
characters that require escaping in JSON).

This is a breaking change, as it's possible downstream consumers are
using `serde` to serialize our AST to other formats. I'd guess unlikely,
but possible. We can always implement `serde::Serialize` as well as
`ESTree` further down the line if there's a demand.
This commit is contained in:
overlookmotel authored and GitHub committed 2025-02-21 12:20:26 +08:00
1 parent ed6bd5dc8b
commit 216b33fd2a
37 files changed
+3782 -2687

No files matched your search

Generated
+4 -6
View File
@@ -1565,6 +1565,7 @@ dependencies = [
"allocator-api2",
"bumpalo",
"hashbrown 0.15.2",
"oxc_estree",
"rustc-hash",
"serde",
"serde_json",
@@ -1584,8 +1585,6 @@ dependencies = [
"oxc_regular_expression",
"oxc_span",
"oxc_syntax",
"serde",
"serde_json",
]
[[package]]
@@ -1751,7 +1750,8 @@ dependencies = [
name = "oxc_estree"
version = "0.51.0"
dependencies = [
"serde",
"itoa",
"ryu-js",
]
[[package]]
@@ -1964,7 +1964,6 @@ dependencies = [
"pico-args",
"rustc-hash",
"seq-macro",
"serde_json",
]
[[package]]
@@ -1982,7 +1981,6 @@ dependencies = [
"oxc_sourcemap",
"rustc-hash",
"self_cell",
"serde_json",
"string_wizard",
]
@@ -2040,7 +2038,6 @@ dependencies = [
"oxc_span",
"phf",
"rustc-hash",
"serde",
"unicode-id-start",
]
@@ -2249,6 +2246,7 @@ dependencies = [
"oxc_prettier",
"serde",
"serde-wasm-bindgen",
"serde_json",
"tsify",
"wasm-bindgen",
]
+3 -1
View File
@@ -19,6 +19,8 @@ workspace = true
doctest = false
[dependencies]
oxc_estree = { workspace = true, optional = true }
allocator-api2 = { workspace = true }
bumpalo = { workspace = true, features = ["allocator-api2", "collections"] }
hashbrown = { workspace = true, default-features = false, features = ["inline-more", "allocator-api2"] }
@@ -32,4 +34,4 @@ serde = { workspace = true }
serde_json = { workspace = true }
[features]
serialize = ["dep:serde"]
serialize = ["dep:serde", "oxc_estree/serialize"]
+26 -4
View File
@@ -12,7 +12,9 @@ use std::{
};
#[cfg(any(feature = "serialize", test))]
use serde::{Serialize, Serializer};
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
#[cfg(any(feature = "serialize", test))]
use serde::{Serialize, Serializer as SerdeSerializer};
use crate::Allocator;
@@ -183,11 +185,18 @@ impl<T: ?Sized + Debug> Debug for Box<'_, T> {
#[cfg(any(feature = "serialize", test))]
impl<T: Serialize> Serialize for Box<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.deref().serialize(serializer)
}
}
#[cfg(any(feature = "serialize", test))]
impl<T: ESTree> ESTree for Box<'_, T> {
fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
self.deref().serialize(serializer);
}
}
impl<T: Hash> Hash for Box<'_, T> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
@@ -238,8 +247,21 @@ mod test {
fn box_serialize() {
let allocator = Allocator::default();
let b = Box::new_in("x", &allocator);
let b = serde_json::to_string(&b).unwrap();
assert_eq!(b, "\"x\"");
let s = serde_json::to_string(&b).unwrap();
assert_eq!(s, r#""x""#);
}
#[test]
fn box_serialize_estree() {
use oxc_estree::{CompactSerializer, ESTree};
let allocator = Allocator::default();
let b = Box::new_in("x", &allocator);
let mut serializer = CompactSerializer::new();
b.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(s, r#""x""#);
}
#[test]
+27 -4
View File
@@ -18,7 +18,9 @@ use std::{
use allocator_api2::vec::Vec as InnerVec;
use bumpalo::Bump;
#[cfg(any(feature = "serialize", test))]
use serde::{Serialize, Serializer};
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
#[cfg(any(feature = "serialize", test))]
use serde::{Serialize, Serializer as SerdeSerializer};
use crate::{Allocator, Box};
@@ -274,11 +276,18 @@ where
#[cfg(any(feature = "serialize", test))]
impl<T: Serialize> Serialize for Vec<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.as_slice().serialize(serializer)
}
}
#[cfg(any(feature = "serialize", test))]
impl<T: ESTree> ESTree for Vec<'_, T> {
fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
self.as_slice().serialize(serializer);
}
}
impl<T: Hash> Hash for Vec<'_, T> {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
@@ -319,8 +328,22 @@ mod test {
let allocator = Allocator::default();
let mut v = Vec::new_in(&allocator);
v.push("x");
let v = serde_json::to_string(&v).unwrap();
assert_eq!(v, "[\"x\"]");
let s = serde_json::to_string(&v).unwrap();
assert_eq!(s, r#"["x"]"#);
}
#[test]
fn vec_serialize_estree() {
use oxc_estree::{CompactSerializer, ESTree};
let allocator = Allocator::default();
let mut v = Vec::new_in(&allocator);
v.push("x");
let mut serializer = CompactSerializer::new();
v.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(s, r#"["x"]"#);
}
#[test]
-6
View File
@@ -30,18 +30,12 @@ bitflags = { workspace = true }
cow-utils = { workspace = true }
nonmax = { workspace = true }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, optional = true }
[features]
default = []
serialize = [
"dep:serde",
"dep:serde_json",
"oxc_allocator/serialize",
"oxc_regular_expression/serialize",
"oxc_span/serialize",
"oxc_syntax/serialize",
"oxc_syntax/to_js_string",
"oxc_estree/serialize",
]
+1 -1
View File
@@ -45,7 +45,7 @@ impl NumericLiteral<'_> {
return int32_value;
}
// NaN, Infinity if not included in our NumericLiteral, so we just serde(skip) step 2.
// NaN, Infinity if not included in our NumericLiteral, so we just skip step 2.
// step 3
let pos_int = num.signum() * num.abs().floor();
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -22,7 +22,7 @@
//! You can obtain an AST by parsing source code with a [`Parser`] from [`oxc_parser`].
//!
//! ## Cargo Features
//! * `"serde"` enables support for serde serialization
//! * `"serialize"` enables support for serialization to ESTree JSON
//!
//! [`BindingIdentifier`]: ast::BindingIdentifier
//! [`IdentifierReference`]: ast::IdentifierReference
+148 -200
View File
@@ -1,79 +1,25 @@
use std::io::Write;
use cow_utils::CowUtils;
use serde::{
ser::{SerializeMap, SerializeSeq, Serializer},
Serialize,
};
use oxc_ast_macros::ast_meta;
use oxc_estree::{
CompactSerializer, ESTree, PrettySerializer, SequenceSerializer, Serializer, StructSerializer,
};
use crate::ast::*;
impl Program<'_> {
/// Serialize AST to JSON.
//
// Should not panic if everything is working correctly.
// Serializing into a `Vec<u8>` should be infallible.
#[expect(clippy::missing_panics_doc)]
/// Serialize AST to ESTree JSON.
pub fn to_json(&self) -> String {
let buf = Vec::new();
let ser = self.to_json_into_writer(buf).unwrap();
let buf = ser.into_inner();
// SAFETY: `serde_json` outputs valid UTF-8.
// `serde_json::to_string` also uses `from_utf8_unchecked`.
// https://github.com/serde-rs/json/blob/1174c5f57db44c26460951b525c6ede50984b655/src/ser.rs#L2209-L2219
unsafe { String::from_utf8_unchecked(buf) }
let mut serializer = CompactSerializer::new();
self.serialize(&mut serializer);
serializer.into_string()
}
/// Serialize AST into a "black hole" writer.
///
/// Only useful for testing, to make sure serialization completes successfully.
/// Should be faster than [`Program::to_json`], as does not actually produce any output.
///
/// # Errors
/// Returns `Err` if serialization fails.
#[doc(hidden)]
pub fn test_to_json(&self) -> Result<(), serde_json::Error> {
struct BlackHole;
#[expect(clippy::inline_always)]
impl Write for BlackHole {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
Ok(buf.len())
}
#[inline(always)]
fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(())
}
}
self.to_json_into_writer(BlackHole).map(|_| ())
}
/// Serialize AST into the provided writer.
fn to_json_into_writer<W: Write>(
&self,
writer: W,
) -> Result<serde_json::Serializer<W, EcmaFormatter>, serde_json::Error> {
let mut ser = serde_json::Serializer::with_formatter(writer, EcmaFormatter);
self.serialize(&mut ser)?;
Ok(ser)
}
}
/// `serde_json` formatter which uses `ryu_js` to serialize `f64`.
pub struct EcmaFormatter;
impl serde_json::ser::Formatter for EcmaFormatter {
fn write_f64<W>(&mut self, writer: &mut W, value: f64) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
use oxc_syntax::number::ToJsString;
writer.write_all(value.to_js_string().as_bytes())
/// Serialize AST to ESTree JSON.
pub fn to_pretty_json(&self) -> String {
let mut serializer = PrettySerializer::new();
self.serialize(&mut serializer);
serializer.into_string()
}
}
@@ -86,9 +32,9 @@ impl serde_json::ser::Formatter for EcmaFormatter {
#[estree(ts_type = "null")]
pub struct Null<'b, T>(#[expect(dead_code)] pub &'b T);
impl<T> Serialize for Null<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
().serialize(serializer)
impl<T> ESTree for Null<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) {
().serialize(serializer);
}
}
@@ -97,9 +43,9 @@ impl<T> Serialize for Null<'_, T> {
#[estree(ts_type = "true")]
pub struct True<'b, T>(#[expect(dead_code)] pub &'b T);
impl<T> Serialize for True<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
true.serialize(serializer)
impl<T> ESTree for True<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) {
true.serialize(serializer);
}
}
@@ -108,9 +54,9 @@ impl<T> Serialize for True<'_, T> {
#[estree(ts_type = "false")]
pub struct False<'b, T>(#[expect(dead_code)] pub &'b T);
impl<T> Serialize for False<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
false.serialize(serializer)
impl<T> ESTree for False<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) {
false.serialize(serializer);
}
}
@@ -119,9 +65,9 @@ impl<T> Serialize for False<'_, T> {
#[estree(ts_type = "'in'")]
pub struct In<'b, T>(#[expect(dead_code)] pub &'b T);
impl<T> Serialize for In<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
"in".serialize(serializer)
impl<T> ESTree for In<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) {
"in".serialize(serializer);
}
}
@@ -130,9 +76,9 @@ impl<T> Serialize for In<'_, T> {
#[estree(ts_type = "'init'")]
pub struct Init<'b, T>(#[expect(dead_code)] pub &'b T);
impl<T> Serialize for Init<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
"init".serialize(serializer)
impl<T> ESTree for Init<'_, T> {
fn serialize<S: Serializer>(&self, serializer: S) {
"init".serialize(serializer);
}
}
@@ -145,8 +91,9 @@ impl<T> Serialize for Init<'_, T> {
#[estree(ts_type = "string | null")]
pub struct BooleanLiteralRaw<'b>(pub &'b BooleanLiteral);
impl Serialize for BooleanLiteralRaw<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for BooleanLiteralRaw<'_> {
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 if self.0.value {
@@ -154,7 +101,7 @@ impl Serialize for BooleanLiteralRaw<'_> {
} else {
Some("false")
};
raw.serialize(serializer)
raw.serialize(serializer);
}
}
@@ -163,10 +110,11 @@ impl Serialize for BooleanLiteralRaw<'_> {
#[estree(ts_type = "'null' | null")]
pub struct NullLiteralRaw<'b>(pub &'b NullLiteral);
impl Serialize for NullLiteralRaw<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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") };
raw.serialize(serializer)
raw.serialize(serializer);
}
}
@@ -175,10 +123,10 @@ impl Serialize for NullLiteralRaw<'_> {
#[estree(ts_type = "string")]
pub struct BigIntLiteralBigint<'a, 'b>(pub &'b BigIntLiteral<'a>);
impl Serialize for BigIntLiteralBigint<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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)
bigint.serialize(serializer);
}
}
@@ -189,9 +137,9 @@ impl Serialize for BigIntLiteralBigint<'_, '_> {
#[estree(ts_type = "BigInt")]
pub struct BigIntLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b BigIntLiteral<'a>);
impl Serialize for BigIntLiteralValue<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_none()
impl ESTree for BigIntLiteralValue<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
().serialize(serializer);
}
}
@@ -200,10 +148,10 @@ impl Serialize for BigIntLiteralValue<'_, '_> {
#[estree(ts_type = "RegExp")]
pub struct RegExpLiteralRegex<'a, 'b>(pub &'b RegExpLiteral<'a>);
impl Serialize for RegExpLiteralRegex<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("pattern", &self.0.regex.pattern)?;
impl ESTree for RegExpLiteralRegex<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("pattern", &self.0.regex.pattern);
// If `raw` field is present, flags must be in same order as in source to match Acorn.
// Count number of set bits in `flags` to get number of flags
@@ -212,11 +160,11 @@ impl Serialize 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;
map.serialize_entry("flags", &raw[flags_index..])?;
state.serialize_field("flags", &raw[flags_index..]);
} else {
map.serialize_entry("flags", &flags)?;
state.serialize_field("flags", &flags);
}
map.end()
state.end();
}
}
@@ -227,21 +175,21 @@ impl Serialize for RegExpLiteralRegex<'_, '_> {
#[estree(ts_type = "RegExp | null")]
pub struct RegExpLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b RegExpLiteral<'a>);
impl Serialize for RegExpLiteralValue<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_none()
impl ESTree for RegExpLiteralValue<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
().serialize(serializer);
}
}
impl Serialize for RegExpFlags {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
impl ESTree for RegExpFlags {
fn serialize<S: Serializer>(&self, serializer: S) {
self.to_string().as_str().serialize(serializer);
}
}
impl Serialize for RegExpPattern<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
impl ESTree for RegExpPattern<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
self.to_string().as_str().serialize(serializer);
}
}
@@ -250,42 +198,42 @@ impl Serialize for RegExpPattern<'_> {
// --------------------
/// Serialize `ArrayExpressionElement::Elision` variant as `null`.
impl Serialize for Elision {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_none()
impl ESTree for Elision {
fn serialize<S: Serializer>(&self, serializer: S) {
().serialize(serializer);
}
}
/// Serialize `FormalParameters`, to be estree compatible, with `items` and `rest` fields combined
/// and `argument` field flattened.
impl Serialize for FormalParameters<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(None)?;
impl ESTree for FormalParameters<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut seq = serializer.serialize_sequence();
for item in &self.items {
seq.serialize_element(item)?;
seq.serialize_element(item);
}
if let Some(rest) = &self.rest {
seq.serialize_element(&FormalParametersRest(rest))?;
seq.serialize_element(&FormalParametersRest(rest));
}
seq.end()
seq.end();
}
}
struct FormalParametersRest<'a, 'b>(&'b BindingRestElement<'a>);
impl Serialize for FormalParametersRest<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for FormalParametersRest<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let rest = self.0;
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "RestElement")?;
map.serialize_entry("start", &rest.span.start)?;
map.serialize_entry("end", &rest.span.end)?;
map.serialize_entry("argument", &rest.argument.kind)?;
map.serialize_entry("type_annotation", &rest.argument.type_annotation)?;
map.serialize_entry("optional", &rest.argument.optional)?;
map.end()
let mut state = serializer.serialize_struct();
state.serialize_field("type", "RestElement");
state.serialize_field("start", &rest.span.start);
state.serialize_field("end", &rest.span.end);
state.serialize_field("argument", &rest.argument.kind);
state.serialize_field("type_annotation", &rest.argument.type_annotation);
state.serialize_field("optional", &rest.argument.optional);
state.end();
}
}
@@ -296,59 +244,59 @@ impl Serialize for FormalParametersRest<'_, '_> {
#[estree(ts_type = "Array<ImportDeclarationSpecifier>")]
pub struct ImportDeclarationSpecifiers<'a, 'b>(pub &'b ImportDeclaration<'a>);
impl Serialize for ImportDeclarationSpecifiers<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for ImportDeclarationSpecifiers<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(specifiers) = &self.0.specifiers {
specifiers.serialize(serializer)
specifiers.serialize(serializer);
} else {
[(); 0].serialize(serializer)
[(); 0].serialize(serializer);
}
}
}
/// Serialize `ObjectProperty` with fields in same order as Acorn.
impl Serialize for ObjectProperty<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Property")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("method", &self.method)?;
map.serialize_entry("shorthand", &self.shorthand)?;
map.serialize_entry("computed", &self.computed)?;
map.serialize_entry("key", &self.key)?;
impl ESTree for ObjectProperty<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("type", "Property");
state.serialize_field("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("method", &self.method);
state.serialize_field("shorthand", &self.shorthand);
state.serialize_field("computed", &self.computed);
state.serialize_field("key", &self.key);
// Acorn has `kind` field before `value` for methods and shorthand properties
if self.method || self.kind != PropertyKind::Init || self.shorthand {
map.serialize_entry("kind", &self.kind)?;
map.serialize_entry("value", &self.value)?;
state.serialize_field("kind", &self.kind);
state.serialize_field("value", &self.value);
} else {
map.serialize_entry("value", &self.value)?;
map.serialize_entry("kind", &self.kind)?;
state.serialize_field("value", &self.value);
state.serialize_field("kind", &self.kind);
}
map.end()
state.end();
}
}
/// Serialize `BindingProperty` with fields in same order as Acorn.
impl Serialize for BindingProperty<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Property")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("method", &false)?;
map.serialize_entry("shorthand", &self.shorthand)?;
map.serialize_entry("computed", &self.computed)?;
map.serialize_entry("key", &self.key)?;
impl ESTree for BindingProperty<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("type", "Property");
state.serialize_field("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("method", &false);
state.serialize_field("shorthand", &self.shorthand);
state.serialize_field("computed", &self.computed);
state.serialize_field("key", &self.key);
// Acorn has `kind` field before `value` for shorthand properties
if self.shorthand {
map.serialize_entry("kind", "init")?;
map.serialize_entry("value", &self.value)?;
state.serialize_field("kind", "init");
state.serialize_field("value", &self.value);
} else {
map.serialize_entry("value", &self.value)?;
map.serialize_entry("kind", "init")?;
state.serialize_field("value", &self.value);
state.serialize_field("kind", "init");
}
map.end()
state.end();
}
}
@@ -360,12 +308,12 @@ impl Serialize for BindingProperty<'_> {
#[estree(ts_type = "FunctionBody | Expression")]
pub struct ArrowFunctionExpressionBody<'a>(pub &'a ArrowFunctionExpression<'a>);
impl Serialize for ArrowFunctionExpressionBody<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for ArrowFunctionExpressionBody<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(expression) = self.0.get_expression() {
expression.serialize(serializer)
expression.serialize(serializer);
} else {
self.0.body.serialize(serializer)
self.0.body.serialize(serializer);
}
}
}
@@ -378,18 +326,18 @@ pub struct AssignmentTargetPropertyIdentifierValue<'a>(
pub &'a AssignmentTargetPropertyIdentifier<'a>,
);
impl Serialize for AssignmentTargetPropertyIdentifierValue<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for AssignmentTargetPropertyIdentifierValue<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(init) = &self.0.init {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "AssignmentPattern")?;
map.serialize_entry("start", &self.0.span.start)?;
map.serialize_entry("end", &self.0.span.end)?;
map.serialize_entry("left", &self.0.binding)?;
map.serialize_entry("right", init)?;
map.end()
let mut state = serializer.serialize_struct();
state.serialize_field("type", "AssignmentPattern");
state.serialize_field("start", &self.0.span.start);
state.serialize_field("end", &self.0.span.end);
state.serialize_field("left", &self.0.binding);
state.serialize_field("right", init);
state.end();
} else {
self.0.binding.serialize(serializer)
self.0.binding.serialize(serializer);
}
}
}
@@ -402,12 +350,12 @@ impl Serialize for AssignmentTargetPropertyIdentifierValue<'_> {
#[estree(ts_type = "Expression | null")]
pub struct ImportExpressionArguments<'a>(pub &'a ImportExpression<'a>);
impl Serialize for ImportExpressionArguments<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for ImportExpressionArguments<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(expression) = self.0.arguments.first() {
expression.serialize(serializer)
expression.serialize(serializer);
} else {
().serialize(serializer)
().serialize(serializer);
}
}
}
@@ -424,12 +372,12 @@ impl Serialize for ImportExpressionArguments<'_> {
#[estree(ts_type = "Array<ImportAttribute>")]
pub struct ImportDeclarationWithClause<'a, 'b>(pub &'b ImportDeclaration<'a>);
impl Serialize for ImportDeclarationWithClause<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for ImportDeclarationWithClause<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(with_clause) = &self.0.with_clause {
with_clause.with_entries.serialize(serializer)
with_clause.with_entries.serialize(serializer);
} else {
[(); 0].serialize(serializer)
[(); 0].serialize(serializer);
}
}
}
@@ -438,12 +386,12 @@ impl Serialize for ImportDeclarationWithClause<'_, '_> {
#[estree(ts_type = "Array<ImportAttribute>")]
pub struct ExportNamedDeclarationWithClause<'a, 'b>(pub &'b ExportNamedDeclaration<'a>);
impl Serialize for ExportNamedDeclarationWithClause<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for ExportNamedDeclarationWithClause<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(with_clause) = &self.0.with_clause {
with_clause.with_entries.serialize(serializer)
with_clause.with_entries.serialize(serializer);
} else {
[(); 0].serialize(serializer)
[(); 0].serialize(serializer);
}
}
}
@@ -452,12 +400,12 @@ impl Serialize for ExportNamedDeclarationWithClause<'_, '_> {
#[estree(ts_type = "Array<ImportAttribute>")]
pub struct ExportAllDeclarationWithClause<'a, 'b>(pub &'b ExportAllDeclaration<'a>);
impl Serialize for ExportAllDeclarationWithClause<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for ExportAllDeclarationWithClause<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(with_clause) = &self.0.with_clause {
with_clause.with_entries.serialize(serializer)
with_clause.with_entries.serialize(serializer);
} else {
[(); 0].serialize(serializer)
[(); 0].serialize(serializer);
}
}
}
@@ -466,31 +414,31 @@ impl Serialize for ExportAllDeclarationWithClause<'_, '_> {
// JSX
// --------------------
impl Serialize for JSXElementName<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for JSXElementName<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
Self::Identifier(ident) => ident.serialize(serializer),
Self::IdentifierReference(ident) => {
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer)
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer);
}
Self::NamespacedName(name) => name.serialize(serializer),
Self::MemberExpression(expr) => expr.serialize(serializer),
Self::ThisExpression(expr) => {
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer)
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer);
}
}
}
}
impl Serialize for JSXMemberExpressionObject<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for JSXMemberExpressionObject<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
Self::IdentifierReference(ident) => {
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer)
JSXIdentifier { span: ident.span, name: ident.name }.serialize(serializer);
}
Self::MemberExpression(expr) => expr.serialize(serializer),
Self::ThisExpression(expr) => {
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer)
JSXIdentifier { span: expr.span, name: "this".into() }.serialize(serializer);
}
}
}
+8 -4
View File
@@ -12,12 +12,16 @@ repository.workspace = true
rust-version.workspace = true
description.workspace = true
[dependencies]
serde = { workspace = true, optional = true }
[lints]
workspace = true
[lib]
doctest = false
[dependencies]
itoa = { workspace = true }
ryu-js = { workspace = true, optional = true }
[features]
default = []
serialize = ["dep:serde"]
serialize = ["dep:ryu-js"]
+8 -2
View File
@@ -1,6 +1,12 @@
#[cfg(feature = "serialize")]
pub mod ser;
#[cfg(feature = "serialize")]
mod serialize;
#[cfg(feature = "serialize")]
pub use serialize::*;
/// Empty trait that will be used later for custom serialization and TypeScript
/// generation for AST nodes.
/// Placeholder for real `ESTree` trait when `serialize` feature not enabled.
///
/// Provided to support `#[generate_derive(ESTree)]`, without enabling the feature.
#[cfg(not(feature = "serialize"))]
pub trait ESTree {}
+14 -14
View File
@@ -1,4 +1,4 @@
use serde::ser::{Serialize, SerializeSeq, Serializer};
use crate::{ESTree, SequenceSerializer, Serializer};
/// A helper struct for serializing a sequence followed by an optional element.
/// This is only used by generated ESTree serialization code.
@@ -7,17 +7,17 @@ pub struct AppendTo<'a, TVec, TAfter> {
pub after: &'a Option<TAfter>,
}
impl<TVec: Serialize, TAfter: Serialize> Serialize for AppendTo<'_, TVec, TAfter> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl<TVec: ESTree, TAfter: ESTree> ESTree for AppendTo<'_, TVec, TAfter> {
fn serialize<S: Serializer>(&self, serializer: S) {
if let Some(after) = self.after {
let mut seq = serializer.serialize_seq(Some(self.array.len() + 1))?;
let mut seq = serializer.serialize_sequence();
for element in self.array {
seq.serialize_element(element)?;
seq.serialize_element(element);
}
seq.serialize_element(after)?;
seq.end()
seq.serialize_element(after);
seq.end();
} else {
self.array.serialize(serializer)
self.array.serialize(serializer);
}
}
}
@@ -27,15 +27,15 @@ pub struct AppendToConcat<'a, TVec, TAfter> {
pub after: &'a [TAfter],
}
impl<TVec: Serialize, TAfter: Serialize> Serialize for AppendToConcat<'_, TVec, TAfter> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(self.array.len() + self.after.len()))?;
impl<TVec: ESTree, TAfter: ESTree> ESTree for AppendToConcat<'_, TVec, TAfter> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut seq = serializer.serialize_sequence();
for element in self.array {
seq.serialize_element(element)?;
seq.serialize_element(element);
}
for element in self.after {
seq.serialize_element(element)?;
seq.serialize_element(element);
}
seq.end()
seq.end();
}
}
+103
View File
@@ -0,0 +1,103 @@
use std::borrow::Cow;
use super::{ESTree, Serializer};
/// [`ESTree`] implementation for a `&` reference to any type that implements `ESTree`.
impl<T> ESTree for &T
where
T: ESTree + ?Sized,
{
#[inline(always)]
fn serialize<S: Serializer>(&self, serializer: S) {
(**self).serialize(serializer);
}
}
/// [`ESTree`] implementation for a `&mut` reference to any type that implements `ESTree`.
impl<T> ESTree for &mut T
where
T: ESTree + ?Sized,
{
#[inline(always)]
fn serialize<S: Serializer>(&self, serializer: S) {
(**self).serialize(serializer);
}
}
/// [`ESTree`] implementation for `Option<T>`.
impl<T: ESTree> ESTree for Option<T> {
fn serialize<S: Serializer>(&self, mut serializer: S) {
if let Some(value) = self {
value.serialize(serializer);
} else {
serializer.buffer_mut().push_str("null");
}
}
}
/// [`ESTree`] implementation for a `Cow` wrapping any type that implements `ESTree`.
impl<T> ESTree for Cow<'_, T>
where
T: ESTree + ToOwned + ?Sized,
{
#[inline(always)]
fn serialize<S: Serializer>(&self, serializer: S) {
(**self).serialize(serializer);
}
}
#[cfg(test)]
mod tests {
use super::super::CompactSerializer;
use super::*;
#[expect(clippy::needless_borrow)]
#[test]
fn serialize_ref() {
let cases = [(&"foo", r#""foo""#), (&&"bar", r#""bar""#)];
for (input, output) in cases {
let mut serializer = CompactSerializer::new();
input.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, output);
}
}
#[test]
fn serialize_mut_ref() {
let cases = [(&mut "foo", r#""foo""#), (&mut &mut "bar", r#""bar""#)];
for (input, output) in cases {
let mut serializer = CompactSerializer::new();
input.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, output);
}
}
#[test]
fn serialize_option() {
let cases = [(None, "null"), (Some(123.0f64), "123")];
for (input, output) in cases {
let mut serializer = CompactSerializer::new();
input.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, output);
}
}
#[test]
fn serialize_cow() {
let cases =
[(Cow::Borrowed("foo"), r#""foo""#), (Cow::Owned("bar".to_string()), r#""bar""#)];
for (input, output) in cases {
let mut serializer = CompactSerializer::new();
input.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, output);
}
}
}
+51
View File
@@ -0,0 +1,51 @@
/// Buffer to store serialized JSON.
pub struct Buffer {
bytes: Vec<u8>,
}
impl Buffer {
/// Create new [`Buffer`].
pub(super) fn new() -> Self {
Self { bytes: vec![] }
}
/// Push a single ASCII byte to the buffer.
///
/// # Panics
/// Panics if `byte` is not ASCII.
#[inline(always)]
pub(super) fn push_ascii_byte(&mut self, byte: u8) {
assert!(byte.is_ascii());
// SAFETY: We just checked this byte is ASCII
unsafe { self.push_ascii_byte_unchecked(byte) };
}
/// Push a single ASCII byte to the buffer, without checking that it's ASCII.
///
/// # SAFETY
/// `byte` must be ASCII.
#[inline(always)]
pub(super) unsafe fn push_ascii_byte_unchecked(&mut self, byte: u8) {
self.bytes.push(byte);
}
/// Push a raw string to the buffer.
pub(super) fn push_str(&mut self, s: &str) {
self.bytes.extend_from_slice(s.as_bytes());
}
/// Push a slice of bytes to the buffer.
///
/// # SAFETY
/// `bytes` must comprise a valid UTF-8 string.
pub(super) unsafe fn push_bytes(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes);
}
/// Consume [`Buffer`] and convert buffer to string.
pub(super) fn into_string(self) -> String {
// SAFETY: None of `Serializer`'s safe methods allow
// adding invalid byte sequences to `self.bytes`
unsafe { String::from_utf8_unchecked(self.bytes) }
}
}
@@ -0,0 +1,104 @@
use super::Buffer;
/// Formatter trait.
pub trait Formatter {
/// Create new [`Formatter`].
fn new() -> Self;
/// Called before the first field of a struct or element of a sequence.
/// If the struct/sequence has no fields/elements, this is not called.
fn before_first_element(&mut self, buffer: &mut Buffer);
/// Called before a later field of a struct or element of a sequence
/// (i.e. not the first field/element).
fn before_later_element(&mut self, buffer: &mut Buffer);
/// Called after the key of a struct field.
fn before_field_value(&mut self, buffer: &mut Buffer);
/// Called after the last element of a sequence / last element of a struct.
/// If the struct/sequence has no fields/elements, this is not called.
fn after_last_element(&mut self, buffer: &mut Buffer);
}
/// Compact formatter.
///
/// All methods are no-ops.
/// This formatter does not insert line breaks, indentation, or whitespace.
///
/// e.g. `{"type":"Program","start":0,"end":0,"body":[]}`
pub struct CompactFormatter;
impl Formatter for CompactFormatter {
#[inline(always)]
fn new() -> Self {
Self
}
#[inline(always)]
fn before_first_element(&mut self, _buffer: &mut Buffer) {}
#[inline(always)]
fn before_later_element(&mut self, _buffer: &mut Buffer) {}
#[inline(always)]
fn before_field_value(&mut self, _buffer: &mut Buffer) {}
#[inline(always)]
fn after_last_element(&mut self, _buffer: &mut Buffer) {}
}
/// Pretty-print formatter.
///
/// Produces pretty-formatted JSON with line breaks, indentation, and whitespace.
///
/// e.g.:
///
/// ```json
/// {
/// "type": "Program",
/// "start": 0,
/// "end": 0,
/// "body": []
/// }
/// ```
///
/// Note that empty structs/sequences do not contain line breaks (`[]` not `[\n ]`, `{}` not `{\n }`)
/// because `before_first_element` and `after_last_element` are only called if the struct/sequence
/// contains at least 1 element.
pub struct PrettyFormatter {
indent: usize,
}
impl Formatter for PrettyFormatter {
#[inline(always)]
fn new() -> Self {
Self { indent: 0 }
}
fn before_first_element(&mut self, buffer: &mut Buffer) {
self.indent += 1;
self.push_new_line_and_indent(buffer);
}
fn before_later_element(&mut self, buffer: &mut Buffer) {
self.push_new_line_and_indent(buffer);
}
fn before_field_value(&mut self, buffer: &mut Buffer) {
buffer.push_ascii_byte(b' ');
}
fn after_last_element(&mut self, buffer: &mut Buffer) {
self.indent -= 1;
self.push_new_line_and_indent(buffer);
}
}
impl PrettyFormatter {
fn push_new_line_and_indent(&self, buffer: &mut Buffer) {
buffer.push_ascii_byte(b'\n');
// SAFETY: Spaces are ASCII
unsafe { buffer.push_bytes(&b" ".repeat(self.indent)) };
}
}
+116
View File
@@ -0,0 +1,116 @@
// Methods which are trivial or just delegate to other methods are marked `#[inline(always)]`
#![expect(clippy::inline_always)]
mod blanket;
mod buffer;
mod formatter;
mod primitives;
mod sequences;
mod strings;
mod structs;
use buffer::Buffer;
use formatter::{CompactFormatter, Formatter, PrettyFormatter};
use sequences::ESTreeSequenceSerializer;
use structs::ESTreeStructSerializer;
pub use sequences::SequenceSerializer;
pub use structs::{FlatStructSerializer, StructSerializer};
/// Trait for types which can be serialized to ESTree.
pub trait ESTree {
fn serialize<S: Serializer>(&self, serializer: S);
}
/// Trait for serializers.
//
// This trait contains public methods.
// Internal methods we don't want to expose outside this crate are in [`SerializerPrivate`] trait.
#[expect(private_bounds)]
pub trait Serializer: SerializerPrivate {
/// Type of struct serializer this serializer uses.
type StructSerializer: StructSerializer;
/// Type of sequence serializer this serializer uses.
type SequenceSerializer: SequenceSerializer;
/// Serialize struct.
fn serialize_struct(self) -> Self::StructSerializer;
/// Serialize sequence.
fn serialize_sequence(self) -> Self::SequenceSerializer;
}
/// Trait containing internal methods of [`Serializer`]s that we don't want to expose outside this crate.
trait SerializerPrivate: Sized {
/// Formatter type
type Formatter: Formatter;
/// Get mutable reference to buffer.
fn buffer_mut(&mut self) -> &mut Buffer;
/// Get mutable references to buffer and formatter.
fn buffer_and_formatter_mut(&mut self) -> (&mut Buffer, &mut Self::Formatter);
}
/// ESTree serializer which produces compact JSON.
pub type CompactSerializer = ESTreeSerializer<CompactFormatter>;
/// ESTree serializer which produces pretty JSON.
pub type PrettySerializer = ESTreeSerializer<PrettyFormatter>;
/// ESTree serializer.
pub struct ESTreeSerializer<F: Formatter> {
buffer: Buffer,
formatter: F,
}
impl<F: Formatter> ESTreeSerializer<F> {
/// Create new [`ESTreeSerializer`].
pub fn new() -> Self {
Self { buffer: Buffer::new(), formatter: F::new() }
}
/// Consume this [`ESTreeSerializer`] and convert buffer to string.
pub fn into_string(self) -> String {
self.buffer.into_string()
}
}
impl<F: Formatter> Default for ESTreeSerializer<F> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl<'s, F: Formatter> Serializer for &'s mut ESTreeSerializer<F> {
type StructSerializer = ESTreeStructSerializer<'s, F>;
type SequenceSerializer = ESTreeSequenceSerializer<'s, F>;
/// Serialize struct.
#[inline(always)]
fn serialize_struct(self) -> ESTreeStructSerializer<'s, F> {
ESTreeStructSerializer::new(self)
}
/// Serialize sequence.
#[inline(always)]
fn serialize_sequence(self) -> ESTreeSequenceSerializer<'s, F> {
ESTreeSequenceSerializer::new(self)
}
}
impl<F: Formatter> SerializerPrivate for &mut ESTreeSerializer<F> {
type Formatter = F;
/// Get mutable reference to buffer.
#[inline(always)]
fn buffer_mut(&mut self) -> &mut Buffer {
&mut self.buffer
}
/// Get mutable references to buffer and formatter.
#[inline(always)]
fn buffer_and_formatter_mut(&mut self) -> (&mut Buffer, &mut F) {
(&mut self.buffer, &mut self.formatter)
}
}
@@ -0,0 +1,234 @@
use itoa::Buffer as ItoaBuffer;
use ryu_js::Buffer as RyuBuffer;
use super::{ESTree, Serializer};
/// [`ESTree`] implementation for `bool`.
impl ESTree for bool {
fn serialize<S: Serializer>(&self, mut serializer: S) {
serializer.buffer_mut().push_str(if *self { "true" } else { "false" });
}
}
/// [`ESTree`] implementations for `f32` and `f64`.
macro_rules! impl_float {
($ty:ident) => {
impl ESTree for $ty {
fn serialize<S: Serializer>(&self, mut serializer: S) {
if self.is_finite() {
let mut buffer = RyuBuffer::new();
let s = buffer.format_finite(*self);
serializer.buffer_mut().push_str(s);
} else if self.is_nan() {
// Serialize `NAN` as `null`
// TODO: Throw an error? Use a sentinel value?
serializer.buffer_mut().push_str("null");
} else if *self == $ty::INFINITY {
// Serialize `INFINITY` as `1e+400. `JSON.parse` deserializes this as `Infinity`.
serializer.buffer_mut().push_str("1e+400");
} else {
// Serialize `-INFINITY` as `-1e+400`. `JSON.parse` deserializes this as `-Infinity`.
serializer.buffer_mut().push_str("-1e+400");
}
}
}
};
}
impl_float!(f32);
impl_float!(f64);
/// [`ESTree`] implementations for integer types.
macro_rules! impl_integer {
($ty:ident) => {
impl ESTree for $ty {
fn serialize<S: Serializer>(&self, mut serializer: S) {
let mut buffer = ItoaBuffer::new();
let s = buffer.format(*self);
serializer.buffer_mut().push_str(s);
}
}
};
}
impl_integer!(u8);
impl_integer!(u16);
impl_integer!(u32);
impl_integer!(u64);
impl_integer!(u128);
impl_integer!(usize);
impl_integer!(i8);
impl_integer!(i16);
impl_integer!(i32);
impl_integer!(i64);
impl_integer!(i128);
impl_integer!(isize);
/// [`ESTree`] implementation for `()`.
impl ESTree for () {
fn serialize<S: Serializer>(&self, mut serializer: S) {
serializer.buffer_mut().push_str("null");
}
}
#[cfg(test)]
mod tests {
use super::super::CompactSerializer;
use super::*;
fn run_test<T: ESTree>(cases: &[(T, &str)]) {
for (input, output) in cases {
let mut serializer = CompactSerializer::new();
input.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, output);
}
}
#[test]
fn serialize_bool() {
run_test(&[(true, "true"), (false, "false")]);
}
#[test]
fn serialize_f32() {
run_test(&[
(0.0, "0"),
(1.0, "1"),
(123_456.0, "123456"),
(0.12345, "0.12345"),
(123.45, "123.45"),
(f32::MIN, "-3.4028235e+38"),
(f32::MAX, "3.4028235e+38"),
(f32::INFINITY, "1e+400"),
(-f32::INFINITY, "-1e+400"),
]);
}
#[test]
fn serialize_f64() {
run_test(&[
(0.0, "0"),
(1.0, "1"),
(123_456.0, "123456"),
(0.12345, "0.12345"),
(123.45, "123.45"),
(f64::MIN, "-1.7976931348623157e+308"),
(f64::MAX, "1.7976931348623157e+308"),
(f64::INFINITY, "1e+400"),
(-f64::INFINITY, "-1e+400"),
]);
}
#[test]
fn serialize_u8() {
run_test(&[(0, "0"), (1, "1"), (123, "123"), (u8::MAX, "255")]);
}
#[test]
fn serialize_u16() {
run_test(&[(0, "0"), (1, "1"), (12_345, "12345"), (u16::MAX, "65535")]);
}
#[test]
fn serialize_u32() {
run_test(&[(0, "0"), (1, "1"), (123_456, "123456"), (u32::MAX, "4294967295")]);
}
#[test]
fn serialize_u64() {
run_test(&[(0, "0"), (1, "1"), (123_456, "123456"), (u64::MAX, "18446744073709551615")]);
}
#[test]
fn serialize_u128() {
run_test(&[
(0, "0"),
(1, "1"),
(123_456, "123456"),
(u128::MAX, "340282366920938463463374607431768211455"),
]);
}
#[test]
fn serialize_usize() {
run_test(&[(0, "0"), (1, "1"), (123_456, "123456"), (u32::MAX as usize, "4294967295")]);
}
#[test]
fn serialize_i8() {
run_test(&[
(0, "0"),
(1, "1"),
(-1, "-1"),
(123, "123"),
(i8::MIN, "-128"),
(i8::MAX, "127"),
]);
}
#[test]
fn serialize_i16() {
run_test(&[
(0, "0"),
(1, "1"),
(-1, "-1"),
(12_345, "12345"),
(i16::MIN, "-32768"),
(i16::MAX, "32767"),
]);
}
#[test]
fn serialize_i32() {
run_test(&[
(0, "0"),
(1, "1"),
(-1, "-1"),
(123_456, "123456"),
(i32::MIN, "-2147483648"),
(i32::MAX, "2147483647"),
]);
}
#[test]
fn serialize_i64() {
run_test(&[
(0, "0"),
(1, "1"),
(-1, "-1"),
(123_456, "123456"),
(i64::MIN, "-9223372036854775808"),
(i64::MAX, "9223372036854775807"),
]);
}
#[test]
fn serialize_i128() {
run_test(&[
(0, "0"),
(1, "1"),
(-1, "-1"),
(123_456, "123456"),
(i128::MIN, "-170141183460469231731687303715884105728"),
(i128::MAX, "170141183460469231731687303715884105727"),
]);
}
#[test]
fn serialize_isize() {
run_test(&[
(0, "0"),
(1, "1"),
(-1, "-1"),
(123_456, "123456"),
(i32::MIN as isize, "-2147483648"),
(i32::MAX as isize, "2147483647"),
]);
}
#[test]
fn serialize_unit() {
run_test(&[((), "null")]);
}
}
@@ -0,0 +1,132 @@
use super::{ESTree, ESTreeSerializer, Formatter, Serializer, SerializerPrivate};
/// Trait for sequence serializers.
pub trait SequenceSerializer {
/// Serialize sequence entry.
fn serialize_element<T: ESTree + ?Sized>(&mut self, value: &T);
/// Finish serializing sequence.
fn end(self);
}
/// Serializer for sequences.
///
/// This is returned by `ESTreeSerializer::serialize_sequence`.
pub struct ESTreeSequenceSerializer<'s, F: Formatter> {
/// Serializer
serializer: &'s mut ESTreeSerializer<F>,
/// State of sequence.
/// Starts as `SequenceState::Empty`, transitions to `SequenceState::HasEntries` on first entry.
state: SequenceState,
}
impl<'s, F: Formatter> ESTreeSequenceSerializer<'s, F> {
/// Create new [`ESTreeSequenceSerializer`].
pub(super) fn new(mut serializer: &'s mut ESTreeSerializer<F>) -> Self {
serializer.buffer_mut().push_ascii_byte(b'[');
Self { serializer, state: SequenceState::Empty }
}
}
impl<F: Formatter> SequenceSerializer for ESTreeSequenceSerializer<'_, F> {
/// Serialize sequence entry.
fn serialize_element<T: ESTree + ?Sized>(&mut self, value: &T) {
let (buffer, formatter) = self.serializer.buffer_and_formatter_mut();
if self.state == SequenceState::Empty {
self.state = SequenceState::HasEntries;
formatter.before_first_element(buffer);
} else {
buffer.push_ascii_byte(b',');
formatter.before_later_element(buffer);
}
value.serialize(&mut *self.serializer);
}
/// Finish serializing sequence.
fn end(mut self) {
let (buffer, formatter) = self.serializer.buffer_and_formatter_mut();
if self.state == SequenceState::HasEntries {
formatter.after_last_element(buffer);
}
buffer.push_ascii_byte(b']');
}
}
/// State of [`ESTreeSequenceSerializer`].
#[derive(Clone, Copy, PartialEq, Eq)]
enum SequenceState {
Empty,
HasEntries,
}
/// [`ESTree`] implementation for slices.
impl<T: ESTree> ESTree for &[T] {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut seq = serializer.serialize_sequence();
for element in *self {
seq.serialize_element(element);
}
seq.end();
}
}
/// [`ESTree`] implementation for arrays.
impl<T: ESTree, const N: usize> ESTree for [T; N] {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut seq = serializer.serialize_sequence();
for element in self {
seq.serialize_element(element);
}
seq.end();
}
}
#[cfg(test)]
mod tests {
use super::super::{CompactSerializer, PrettySerializer, StructSerializer};
use super::*;
#[test]
fn serialize_sequence() {
struct Foo<'a> {
none: &'a [&'a str],
one: &'a [&'a str],
two: [&'a str; 2],
}
impl ESTree for Foo<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("none", &self.none);
state.serialize_field("one", &self.one);
state.serialize_field("two", &self.two);
state.end();
}
}
let foo = Foo { none: &[], one: &["one"], two: ["two one", "two two"] };
let mut serializer = CompactSerializer::new();
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, r#"{"none":[],"one":["one"],"two":["two one","two two"]}"#);
let mut serializer = PrettySerializer::new();
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(
&s,
r#"{
"none": [],
"one": [
"one"
],
"two": [
"two one",
"two two"
]
}"#
);
}
}
+169
View File
@@ -0,0 +1,169 @@
use super::{Buffer, ESTree, Serializer};
/// [`ESTree`] implementation for string slice.
impl ESTree for str {
fn serialize<S: Serializer>(&self, mut serializer: S) {
write_str(self, serializer.buffer_mut());
}
}
/// [`ESTree`] implementation for `String`.
impl ESTree for String {
fn serialize<S: Serializer>(&self, mut serializer: S) {
write_str(self.as_str(), serializer.buffer_mut());
}
}
/// Escapes
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum Escape {
__ = 0,
BB = b'b', // \x08
TT = b't', // \x09
NN = b'n', // \x0A
FF = b'f', // \x0C
RR = b'r', // \x0D
QU = b'"', // \x22
BS = b'\\', // \x5C
UU = b'u', // \x00...\x1F except the ones above
}
/// Lookup table of escape sequences. A value of `b'x'` at index `i` means that byte `i`
/// is escaped as "\x" in JSON. A value of 0 means that byte `i` is not escaped.
///
/// A value of `UU` means that byte is escaped as `\u00xx`, where `xx` is the hex code of the byte.
/// e.g. `0x1F` is output as `\u001F`.
static ESCAPE: [Escape; 256] = {
#[allow(clippy::enum_glob_use, clippy::allow_attributes)]
use Escape::*;
[
// 1 2 3 4 5 6 7 8 9 A B C D E F
UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
__, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
__, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
]
};
/// Write string to buffer.
/// String is wrapped in `"`s, and with any characters which are not valid in JSON escaped.
#[inline(always)]
fn write_str(s: &str, buffer: &mut Buffer) {
buffer.push_ascii_byte(b'"');
let bytes = s.as_bytes();
let mut start = 0;
for (index, &byte) in bytes.iter().enumerate() {
let escape = ESCAPE[byte as usize];
if escape == Escape::__ {
continue;
}
if start < index {
// SAFETY: `bytes` is derived from a `&str`.
// `escape` is only non-zero for ASCII bytes.
// Therefore current `index` must mark the end of a valid UTF8 character sequence.
// `start` is either the start of string, or after an ASCII character,
// therefore always the start of a valid UTF8 character sequence.
unsafe { buffer.push_bytes(&bytes[start..index]) };
}
write_char_escape(escape, byte, buffer);
start = index + 1;
}
if start < bytes.len() {
// SAFETY: `bytes` is derived from a `&str`.
// `start` is either the start of string, or after an ASCII character,
// therefore always the start of a valid UTF8 character sequence.
unsafe { buffer.push_bytes(&bytes[start..]) };
}
buffer.push_ascii_byte(b'"');
}
fn write_char_escape(escape: Escape, byte: u8, buffer: &mut Buffer) {
#[expect(clippy::if_not_else)]
if escape != Escape::UU {
buffer.push_ascii_byte(b'\\');
// SAFETY: All values of `Escape` are ASCII
unsafe { buffer.push_ascii_byte_unchecked(escape as u8) };
} else {
static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
let bytes = [
b'\\',
b'u',
b'0',
b'0',
HEX_DIGITS[(byte >> 4) as usize],
HEX_DIGITS[(byte & 0xF) as usize],
];
// SAFETY: `bytes` contains only ASCII bytes
unsafe { buffer.push_bytes(&bytes) }
}
}
#[cfg(test)]
mod tests {
use super::super::CompactSerializer;
use super::*;
#[test]
fn serialize_string_slice() {
let cases = [
("", r#""""#),
("foobar", r#""foobar""#),
("\n", r#""\n""#),
("\nfoobar", r#""\nfoobar""#),
("foo\nbar", r#""foo\nbar""#),
("foobar\n", r#""foobar\n""#),
(
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
r#""\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f""#,
),
(
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F",
r#""\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f""#,
),
(
r#"They call me "Bob" but I prefer "Dennis", innit?"#,
r#""They call me \"Bob\" but I prefer \"Dennis\", innit?""#,
),
];
for (input, output) in cases {
let mut serializer = CompactSerializer::new();
input.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, output);
}
}
#[test]
fn serialize_string() {
let cases = [(String::new(), r#""""#), ("foobar".to_string(), r#""foobar""#)];
for (input, output) in cases {
let mut serializer = CompactSerializer::new();
input.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(&s, output);
}
}
}
+342
View File
@@ -0,0 +1,342 @@
use super::{
Buffer, ESTree, ESTreeSequenceSerializer, ESTreeSerializer, Formatter, Serializer,
SerializerPrivate,
};
/// Trait for struct serializers.
pub trait StructSerializer {
type Formatter: Formatter;
/// Serialize struct field.
///
/// `key` must not contain any characters which require escaping in JSON.
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &str, value: &T);
/// Finish serializing struct.
fn end(self);
}
/// Serializer for structs.
///
/// This is returned by `ESTreeSerializer::serialize_struct`.
pub struct ESTreeStructSerializer<'s, F: Formatter> {
/// Serializer
serializer: &'s mut ESTreeSerializer<F>,
/// State of struct.
/// Starts as `StructState::Empty`, transitions to `StructState::HasFields` on first field.
state: StructState,
}
impl<'s, F: Formatter> ESTreeStructSerializer<'s, F> {
/// Create new [`ESTreeStructSerializer`].
pub(super) fn new(mut serializer: &'s mut ESTreeSerializer<F>) -> Self {
serializer.buffer_mut().push_ascii_byte(b'{');
Self { serializer, state: StructState::Empty }
}
}
impl<F: Formatter> StructSerializer for ESTreeStructSerializer<'_, F> {
type Formatter = F;
/// Serialize struct field.
///
/// `key` must not contain any characters which require escaping in JSON.
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &str, value: &T) {
let (buffer, formatter) = self.serializer.buffer_and_formatter_mut();
if self.state == StructState::Empty {
self.state = StructState::HasFields;
formatter.before_first_element(buffer);
} else {
buffer.push_ascii_byte(b',');
formatter.before_later_element(buffer);
}
buffer.push_ascii_byte(b'"');
buffer.push_str(key);
buffer.push_str("\":");
formatter.before_field_value(buffer);
value.serialize(&mut *self.serializer);
}
/// Finish serializing struct.
fn end(self) {
let mut serializer = self.serializer;
let (buffer, formatter) = serializer.buffer_and_formatter_mut();
if self.state == StructState::HasFields {
formatter.after_last_element(buffer);
}
buffer.push_ascii_byte(b'}');
}
}
/// State of [`StructSerializer`].
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum StructState {
Empty,
HasFields,
}
/// Flattening struct serializer.
///
/// The only method it's legal to call on a `FlatStructSerializer` is `serialize_struct`.
///
/// # Example
/// ```
/// struct SomeNode {
/// // We want to flatten `Span` as if its fields were on `SomeNode`
/// span: Span,
/// whatever: u32,
/// }
///
/// struct Span {
/// start: u32,
/// end: u32,
/// }
///
/// impl ESTree for SomeNode {
/// fn serialize<S: Serializer>(&self, serializer: S) {
/// let mut state = serializer.serialize_struct();
/// self.inner.serialize(FlatStructSerializer(&mut state));
/// state.serialize_field("whatever", &self.whatever);
/// state.end();
/// }
/// }
///
/// impl ESTree for Span {
/// fn serialize<S: Serializer>(&self, serializer: S) {
/// let mut state = serializer.serialize_struct();
/// state.serialize_field("start", &self.start);
/// state.serialize_field("end", &self.end);
/// state.end();
/// }
/// }
/// ```
pub struct FlatStructSerializer<'p, P: StructSerializer>(pub &'p mut P);
impl<'p, P: StructSerializer> Serializer for FlatStructSerializer<'p, P> {
type StructSerializer = Self;
type SequenceSerializer = ESTreeSequenceSerializer<'p, P::Formatter>;
/// Serialize struct.
fn serialize_struct(self) -> Self {
self
}
fn serialize_sequence(self) -> ESTreeSequenceSerializer<'p, P::Formatter> {
const {
panic!("Cannot flatten a sequence into a struct");
}
}
}
impl<P: StructSerializer> SerializerPrivate for FlatStructSerializer<'_, P> {
type Formatter = P::Formatter;
fn buffer_mut(&mut self) -> &mut Buffer {
const {
panic!("Cannot flatten anything but a struct into another struct");
}
}
fn buffer_and_formatter_mut(&mut self) -> (&mut Buffer, &mut P::Formatter) {
const {
panic!("Cannot flatten anything but a struct into another struct");
}
}
}
impl<P: StructSerializer> StructSerializer for FlatStructSerializer<'_, P> {
type Formatter = P::Formatter;
/// Serialize struct field.
///
/// `key` must not contain any characters which require escaping in JSON.
#[inline(always)]
fn serialize_field<T: ESTree + ?Sized>(&mut self, key: &str, value: &T) {
// Delegate to parent `StructSerializer`
self.0.serialize_field(key, value);
}
/// Finish serializing struct.
fn end(self) {
// No-op - there may be more fields to be added to the struct in the parent
}
}
#[cfg(test)]
mod tests {
use super::super::{CompactSerializer, FlatStructSerializer, PrettySerializer, Serializer};
use super::*;
#[test]
fn serialize_struct() {
struct Foo<'a> {
n: f64,
u: u32,
bar: Bar<'a>,
empty: Null,
hello: Option<&'a str>,
maybe_bar: Option<Bar<'a>>,
maybe_not_bar: Option<Bar<'a>>,
}
struct Bar<'a> {
yes: &'a str,
no: &'a str,
}
struct Null;
impl ESTree for Foo<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("n", &self.n);
state.serialize_field("u", &self.u);
state.serialize_field("bar", &self.bar);
state.serialize_field("empty", &self.empty);
state.serialize_field("hello", &self.hello);
state.serialize_field("maybe_bar", &self.maybe_bar);
state.serialize_field("maybe_not_bar", &self.maybe_not_bar);
state.end();
}
}
impl ESTree for Bar<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("yes", &self.yes);
state.serialize_field("no", &self.no);
state.end();
}
}
impl ESTree for Null {
fn serialize<S: Serializer>(&self, serializer: S) {
let state = serializer.serialize_struct();
state.end();
}
}
let foo = Foo {
n: 123.0,
u: 12345,
bar: Bar { yes: "yup", no: "nope" },
empty: Null,
hello: Some("hi!"),
maybe_bar: Some(Bar { yes: "hell yeah!", no: "not a chance in a million, mate" }),
maybe_not_bar: None,
};
let mut serializer = CompactSerializer::new();
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(
&s,
r#"{"n":123,"u":12345,"bar":{"yes":"yup","no":"nope"},"empty":{},"hello":"hi!","maybe_bar":{"yes":"hell yeah!","no":"not a chance in a million, mate"},"maybe_not_bar":null}"#
);
let mut serializer = PrettySerializer::new();
foo.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(
&s,
r#"{
"n": 123,
"u": 12345,
"bar": {
"yes": "yup",
"no": "nope"
},
"empty": {},
"hello": "hi!",
"maybe_bar": {
"yes": "hell yeah!",
"no": "not a chance in a million, mate"
},
"maybe_not_bar": null
}"#
);
}
#[test]
fn serialize_flattened_struct() {
struct Outer {
outer1: &'static str,
inner: Inner,
outer2: &'static str,
}
struct Inner {
inner1: &'static str,
innermost: Innermost,
inner2: &'static str,
}
struct Innermost {
innermost1: &'static str,
innermost2: &'static str,
}
impl ESTree for Outer {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("outer1", &self.outer1);
self.inner.serialize(FlatStructSerializer(&mut state));
state.serialize_field("outer2", &self.outer2);
state.end();
}
}
impl ESTree for Inner {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("inner1", &self.inner1);
self.innermost.serialize(FlatStructSerializer(&mut state));
state.serialize_field("inner2", &self.inner2);
state.end();
}
}
impl ESTree for Innermost {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("innermost1", &self.innermost1);
state.serialize_field("innermost2", &self.innermost2);
state.end();
}
}
let outer = Outer {
outer1: "out1",
inner: Inner {
inner1: "in1",
innermost: Innermost { innermost1: "inin1", innermost2: "inin2" },
inner2: "in2",
},
outer2: "out2",
};
let mut serializer = CompactSerializer::new();
outer.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(
&s,
r#"{"outer1":"out1","inner1":"in1","innermost1":"inin1","innermost2":"inin2","inner2":"in2","outer2":"out2"}"#
);
let mut serializer = PrettySerializer::new();
outer.serialize(&mut serializer);
let s = serializer.into_string();
assert_eq!(
&s,
r#"{
"outer1": "out1",
"inner1": "in1",
"innermost1": "inin1",
"innermost2": "inin2",
"inner2": "in2",
"outer2": "out2"
}"#
);
}
}
-1
View File
@@ -40,7 +40,6 @@ memchr = { workspace = true }
[dev-dependencies]
oxc_ast = { workspace = true, features = ["serialize"] }
pico-args = { workspace = true }
serde_json = { workspace = true }
[features]
# Expose Lexer for benchmarks
+1 -1
View File
@@ -43,7 +43,7 @@ fn main() -> Result<(), String> {
if show_estree {
Utf8ToUtf16::new().convert(&mut program);
}
println!("{}", serde_json::to_string_pretty(&program).unwrap());
println!("{}", program.to_pretty_json());
}
if ret.errors.is_empty() {
-3
View File
@@ -30,12 +30,9 @@ phf = { workspace = true, features = ["macros"] }
rustc-hash = { workspace = true }
unicode-id-start = { workspace = true }
serde = { workspace = true, optional = true }
[features]
default = []
serialize = [
"dep:serde",
"oxc_allocator/serialize",
"oxc_span/serialize",
"oxc_estree/serialize",
@@ -1,49 +1,50 @@
// Auto-generated code, DO NOT EDIT DIRECTLY!
// To edit this generated file you have to edit `tasks/ast_tools/src/derives/estree.rs`
#![allow(unused_imports, clippy::match_same_arms)]
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
use serde::{__private::ser::FlatMapSerializer, ser::SerializeMap, Serialize, Serializer};
use oxc_estree::ser::{AppendTo, AppendToConcat};
use oxc_estree::{
ser::{AppendTo, AppendToConcat},
ESTree, FlatStructSerializer, Serializer, StructSerializer,
};
use crate::ast::*;
impl Serialize for Pattern<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Pattern")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for Disjunction<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Disjunction")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for Alternative<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Alternative")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for Term<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for Term<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
Term::BoundaryAssertion(it) => it.serialize(serializer),
Term::LookAroundAssertion(it) => it.serialize(serializer),
@@ -61,222 +62,170 @@ impl Serialize for Term<'_> {
}
}
impl Serialize for BoundaryAssertion {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "BoundaryAssertion")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("kind", &self.kind)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("kind", &self.kind);
state.end();
}
}
impl Serialize for BoundaryAssertionKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for BoundaryAssertionKind {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
BoundaryAssertionKind::Start => {
serializer.serialize_unit_variant("BoundaryAssertionKind", 0, "start")
}
BoundaryAssertionKind::End => {
serializer.serialize_unit_variant("BoundaryAssertionKind", 1, "end")
}
BoundaryAssertionKind::Boundary => {
serializer.serialize_unit_variant("BoundaryAssertionKind", 2, "boundary")
}
BoundaryAssertionKind::NegativeBoundary => {
serializer.serialize_unit_variant("BoundaryAssertionKind", 3, "negativeBoundary")
BoundaryAssertionKind::Start => "start".serialize(serializer),
BoundaryAssertionKind::End => "end".serialize(serializer),
BoundaryAssertionKind::Boundary => "boundary".serialize(serializer),
BoundaryAssertionKind::NegativeBoundary => "negativeBoundary".serialize(serializer),
}
}
}
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("kind", &self.kind);
state.serialize_field("body", &self.body);
state.end();
}
}
impl ESTree for LookAroundAssertionKind {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
LookAroundAssertionKind::Lookahead => "lookahead".serialize(serializer),
LookAroundAssertionKind::NegativeLookahead => "negativeLookahead".serialize(serializer),
LookAroundAssertionKind::Lookbehind => "lookbehind".serialize(serializer),
LookAroundAssertionKind::NegativeLookbehind => {
"negativeLookbehind".serialize(serializer)
}
}
}
}
impl Serialize for LookAroundAssertion<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "LookAroundAssertion")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("kind", &self.kind)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("min", &self.min);
state.serialize_field("max", &self.max);
state.serialize_field("greedy", &self.greedy);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for LookAroundAssertionKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("kind", &self.kind);
state.serialize_field("value", &self.value);
state.end();
}
}
impl ESTree for CharacterKind {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
LookAroundAssertionKind::Lookahead => {
serializer.serialize_unit_variant("LookAroundAssertionKind", 0, "lookahead")
}
LookAroundAssertionKind::NegativeLookahead => {
serializer.serialize_unit_variant("LookAroundAssertionKind", 1, "negativeLookahead")
}
LookAroundAssertionKind::Lookbehind => {
serializer.serialize_unit_variant("LookAroundAssertionKind", 2, "lookbehind")
}
LookAroundAssertionKind::NegativeLookbehind => serializer.serialize_unit_variant(
"LookAroundAssertionKind",
3,
"negativeLookbehind",
),
CharacterKind::ControlLetter => "controlLetter".serialize(serializer),
CharacterKind::HexadecimalEscape => "hexadecimalEscape".serialize(serializer),
CharacterKind::Identifier => "identifier".serialize(serializer),
CharacterKind::Null => "null".serialize(serializer),
CharacterKind::Octal1 => "octal1".serialize(serializer),
CharacterKind::Octal2 => "octal2".serialize(serializer),
CharacterKind::Octal3 => "octal3".serialize(serializer),
CharacterKind::SingleEscape => "singleEscape".serialize(serializer),
CharacterKind::Symbol => "symbol".serialize(serializer),
CharacterKind::UnicodeEscape => "unicodeEscape".serialize(serializer),
}
}
}
impl Serialize for Quantifier<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Quantifier")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("min", &self.min)?;
map.serialize_entry("max", &self.max)?;
map.serialize_entry("greedy", &self.greedy)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("kind", &self.kind);
state.end();
}
}
impl Serialize for Character {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Character")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("kind", &self.kind)?;
map.serialize_entry("value", &self.value)?;
map.end()
}
}
impl Serialize for CharacterKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for CharacterClassEscapeKind {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
CharacterKind::ControlLetter => {
serializer.serialize_unit_variant("CharacterKind", 0, "controlLetter")
}
CharacterKind::HexadecimalEscape => {
serializer.serialize_unit_variant("CharacterKind", 1, "hexadecimalEscape")
}
CharacterKind::Identifier => {
serializer.serialize_unit_variant("CharacterKind", 2, "identifier")
}
CharacterKind::Null => serializer.serialize_unit_variant("CharacterKind", 3, "null"),
CharacterKind::Octal1 => {
serializer.serialize_unit_variant("CharacterKind", 4, "octal1")
}
CharacterKind::Octal2 => {
serializer.serialize_unit_variant("CharacterKind", 5, "octal2")
}
CharacterKind::Octal3 => {
serializer.serialize_unit_variant("CharacterKind", 6, "octal3")
}
CharacterKind::SingleEscape => {
serializer.serialize_unit_variant("CharacterKind", 7, "singleEscape")
}
CharacterKind::Symbol => {
serializer.serialize_unit_variant("CharacterKind", 8, "symbol")
}
CharacterKind::UnicodeEscape => {
serializer.serialize_unit_variant("CharacterKind", 9, "unicodeEscape")
}
CharacterClassEscapeKind::D => "d".serialize(serializer),
CharacterClassEscapeKind::NegativeD => "negativeD".serialize(serializer),
CharacterClassEscapeKind::S => "s".serialize(serializer),
CharacterClassEscapeKind::NegativeS => "negativeS".serialize(serializer),
CharacterClassEscapeKind::W => "w".serialize(serializer),
CharacterClassEscapeKind::NegativeW => "negativeW".serialize(serializer),
}
}
}
impl Serialize for CharacterClassEscape {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "CharacterClassEscape")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("kind", &self.kind)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("negative", &self.negative);
state.serialize_field("strings", &self.strings);
state.serialize_field("name", &self.name);
state.serialize_field("value", &self.value);
state.end();
}
}
impl Serialize for CharacterClassEscapeKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.end();
}
}
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("negative", &self.negative);
state.serialize_field("strings", &self.strings);
state.serialize_field("kind", &self.kind);
state.serialize_field("body", &self.body);
state.end();
}
}
impl ESTree for CharacterClassContentsKind {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
CharacterClassEscapeKind::D => {
serializer.serialize_unit_variant("CharacterClassEscapeKind", 0, "d")
}
CharacterClassEscapeKind::NegativeD => {
serializer.serialize_unit_variant("CharacterClassEscapeKind", 1, "negativeD")
}
CharacterClassEscapeKind::S => {
serializer.serialize_unit_variant("CharacterClassEscapeKind", 2, "s")
}
CharacterClassEscapeKind::NegativeS => {
serializer.serialize_unit_variant("CharacterClassEscapeKind", 3, "negativeS")
}
CharacterClassEscapeKind::W => {
serializer.serialize_unit_variant("CharacterClassEscapeKind", 4, "w")
}
CharacterClassEscapeKind::NegativeW => {
serializer.serialize_unit_variant("CharacterClassEscapeKind", 5, "negativeW")
}
CharacterClassContentsKind::Union => "union".serialize(serializer),
CharacterClassContentsKind::Intersection => "intersection".serialize(serializer),
CharacterClassContentsKind::Subtraction => "subtraction".serialize(serializer),
}
}
}
impl Serialize for UnicodePropertyEscape<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "UnicodePropertyEscape")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("negative", &self.negative)?;
map.serialize_entry("strings", &self.strings)?;
map.serialize_entry("name", &self.name)?;
map.serialize_entry("value", &self.value)?;
map.end()
}
}
impl Serialize for Dot {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Dot")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.end()
}
}
impl Serialize for CharacterClass<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "CharacterClass")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("negative", &self.negative)?;
map.serialize_entry("strings", &self.strings)?;
map.serialize_entry("kind", &self.kind)?;
map.serialize_entry("body", &self.body)?;
map.end()
}
}
impl Serialize for CharacterClassContentsKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
CharacterClassContentsKind::Union => {
serializer.serialize_unit_variant("CharacterClassContentsKind", 0, "union")
}
CharacterClassContentsKind::Intersection => {
serializer.serialize_unit_variant("CharacterClassContentsKind", 1, "intersection")
}
CharacterClassContentsKind::Subtraction => {
serializer.serialize_unit_variant("CharacterClassContentsKind", 2, "subtraction")
}
}
}
}
impl Serialize for CharacterClassContents<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for CharacterClassContents<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
CharacterClassContents::CharacterClassRange(it) => it.serialize(serializer),
CharacterClassContents::CharacterClassEscape(it) => it.serialize(serializer),
@@ -288,107 +237,107 @@ impl Serialize for CharacterClassContents<'_> {
}
}
impl Serialize for CharacterClassRange {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "CharacterClassRange")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("min", &self.min)?;
map.serialize_entry("max", &self.max)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("min", &self.min);
state.serialize_field("max", &self.max);
state.end();
}
}
impl Serialize for ClassStringDisjunction<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "ClassStringDisjunction")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("strings", &self.strings)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("strings", &self.strings);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for ClassString<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "ClassString")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("strings", &self.strings)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("strings", &self.strings);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for CapturingGroup<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "CapturingGroup")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("name", &self.name)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("name", &self.name);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for IgnoreGroup<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "IgnoreGroup")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("modifiers", &self.modifiers)?;
map.serialize_entry("body", &self.body)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("modifiers", &self.modifiers);
state.serialize_field("body", &self.body);
state.end();
}
}
impl Serialize for Modifiers {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Modifiers")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("enabling", &self.enabling)?;
map.serialize_entry("disabling", &self.disabling)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("enabling", &self.enabling);
state.serialize_field("disabling", &self.disabling);
state.end();
}
}
impl Serialize for Modifier {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "Modifier")?;
map.serialize_entry("ignoreCase", &self.ignore_case)?;
map.serialize_entry("multiline", &self.multiline)?;
map.serialize_entry("sticky", &self.sticky)?;
map.end()
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("ignoreCase", &self.ignore_case);
state.serialize_field("multiline", &self.multiline);
state.serialize_field("sticky", &self.sticky);
state.end();
}
}
impl Serialize for IndexedReference {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "IndexedReference")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("index", &self.index)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("index", &self.index);
state.end();
}
}
impl Serialize for NamedReference<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("type", "NamedReference")?;
map.serialize_entry("start", &self.span.start)?;
map.serialize_entry("end", &self.span.end)?;
map.serialize_entry("name", &self.name)?;
map.end()
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("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("name", &self.name);
state.end();
}
}
+12 -3
View File
@@ -6,7 +6,9 @@ use std::{
use oxc_allocator::{Allocator, CloneIn, FromIn};
#[cfg(feature = "serialize")]
use serde::{Serialize, Serializer};
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
#[cfg(feature = "serialize")]
use serde::{Serialize, Serializer as SerdeSerializer};
use crate::{CompactStr, ContentEq};
@@ -208,7 +210,14 @@ impl fmt::Display for Atom<'_> {
#[cfg(feature = "serialize")]
impl Serialize for Atom<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
Serialize::serialize(self.as_str(), serializer)
}
}
#[cfg(feature = "serialize")]
impl ESTree for Atom<'_> {
fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
ESTree::serialize(self.as_str(), serializer);
}
}
+21 -22
View File
@@ -1,40 +1,39 @@
// Auto-generated code, DO NOT EDIT DIRECTLY!
// To edit this generated file you have to edit `tasks/ast_tools/src/derives/estree.rs`
#![allow(unused_imports, clippy::match_same_arms)]
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
use serde::{__private::ser::FlatMapSerializer, ser::SerializeMap, Serialize, Serializer};
use oxc_estree::ser::{AppendTo, AppendToConcat};
use oxc_estree::{
ser::{AppendTo, AppendToConcat},
ESTree, FlatStructSerializer, Serializer, StructSerializer,
};
use crate::source_type::*;
use crate::span::*;
impl Serialize for Span {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("start", &self.start)?;
map.serialize_entry("end", &self.end)?;
map.end()
impl ESTree for Span {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("start", &self.start);
state.serialize_field("end", &self.end);
state.end();
}
}
impl Serialize for SourceType {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("sourceType", &self.module_kind)?;
map.end()
impl ESTree for SourceType {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("sourceType", &self.module_kind);
state.end();
}
}
impl Serialize for ModuleKind {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for ModuleKind {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
ModuleKind::Script => serializer.serialize_unit_variant("ModuleKind", 0, "script"),
ModuleKind::Module => serializer.serialize_unit_variant("ModuleKind", 1, "module"),
ModuleKind::Unambiguous => {
serializer.serialize_unit_variant("ModuleKind", 2, "unambiguous")
}
ModuleKind::Script => "script".serialize(serializer),
ModuleKind::Module => "module".serialize(serializer),
ModuleKind::Unambiguous => "unambiguous".serialize(serializer),
}
}
}
+12
View File
@@ -5,6 +5,8 @@ use std::{
};
use miette::{LabeledSpan, SourceOffset, SourceSpan};
#[cfg(feature = "serialize")]
use serde::{ser::SerializeMap, Serialize, Serializer as SerdeSerializer};
use oxc_allocator::{Allocator, CloneIn};
use oxc_ast_macros::ast;
@@ -543,6 +545,16 @@ impl<'a> CloneIn<'a> for Span {
}
}
#[cfg(feature = "serialize")]
impl Serialize for Span {
fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("start", &self.start)?;
map.serialize_entry("end", &self.end)?;
map.end()
}
}
/// Zero-sized type which has pointer alignment (8 on 64-bit, 4 on 32-bit).
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
+65 -146
View File
@@ -1,177 +1,96 @@
// Auto-generated code, DO NOT EDIT DIRECTLY!
// To edit this generated file you have to edit `tasks/ast_tools/src/derives/estree.rs`
#![allow(unused_imports, clippy::match_same_arms)]
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
use serde::{__private::ser::FlatMapSerializer, ser::SerializeMap, Serialize, Serializer};
use oxc_estree::ser::{AppendTo, AppendToConcat};
use oxc_estree::{
ser::{AppendTo, AppendToConcat},
ESTree, FlatStructSerializer, Serializer, StructSerializer,
};
use crate::operator::*;
impl Serialize for AssignmentOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for AssignmentOperator {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
AssignmentOperator::Assign => {
serializer.serialize_unit_variant("AssignmentOperator", 0, "=")
}
AssignmentOperator::Addition => {
serializer.serialize_unit_variant("AssignmentOperator", 1, "+=")
}
AssignmentOperator::Subtraction => {
serializer.serialize_unit_variant("AssignmentOperator", 2, "-=")
}
AssignmentOperator::Multiplication => {
serializer.serialize_unit_variant("AssignmentOperator", 3, "*=")
}
AssignmentOperator::Division => {
serializer.serialize_unit_variant("AssignmentOperator", 4, "/=")
}
AssignmentOperator::Remainder => {
serializer.serialize_unit_variant("AssignmentOperator", 5, "%=")
}
AssignmentOperator::Exponential => {
serializer.serialize_unit_variant("AssignmentOperator", 6, "**=")
}
AssignmentOperator::ShiftLeft => {
serializer.serialize_unit_variant("AssignmentOperator", 7, "<<=")
}
AssignmentOperator::ShiftRight => {
serializer.serialize_unit_variant("AssignmentOperator", 8, ">>=")
}
AssignmentOperator::ShiftRightZeroFill => {
serializer.serialize_unit_variant("AssignmentOperator", 9, ">>>=")
}
AssignmentOperator::BitwiseOR => {
serializer.serialize_unit_variant("AssignmentOperator", 10, "|=")
}
AssignmentOperator::BitwiseXOR => {
serializer.serialize_unit_variant("AssignmentOperator", 11, "^=")
}
AssignmentOperator::BitwiseAnd => {
serializer.serialize_unit_variant("AssignmentOperator", 12, "&=")
}
AssignmentOperator::LogicalOr => {
serializer.serialize_unit_variant("AssignmentOperator", 13, "||=")
}
AssignmentOperator::LogicalAnd => {
serializer.serialize_unit_variant("AssignmentOperator", 14, "&&=")
}
AssignmentOperator::LogicalNullish => {
serializer.serialize_unit_variant("AssignmentOperator", 15, "??=")
}
AssignmentOperator::Assign => "=".serialize(serializer),
AssignmentOperator::Addition => "+=".serialize(serializer),
AssignmentOperator::Subtraction => "-=".serialize(serializer),
AssignmentOperator::Multiplication => "*=".serialize(serializer),
AssignmentOperator::Division => "/=".serialize(serializer),
AssignmentOperator::Remainder => "%=".serialize(serializer),
AssignmentOperator::Exponential => "**=".serialize(serializer),
AssignmentOperator::ShiftLeft => "<<=".serialize(serializer),
AssignmentOperator::ShiftRight => ">>=".serialize(serializer),
AssignmentOperator::ShiftRightZeroFill => ">>>=".serialize(serializer),
AssignmentOperator::BitwiseOR => "|=".serialize(serializer),
AssignmentOperator::BitwiseXOR => "^=".serialize(serializer),
AssignmentOperator::BitwiseAnd => "&=".serialize(serializer),
AssignmentOperator::LogicalOr => "||=".serialize(serializer),
AssignmentOperator::LogicalAnd => "&&=".serialize(serializer),
AssignmentOperator::LogicalNullish => "??=".serialize(serializer),
}
}
}
impl Serialize for BinaryOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for BinaryOperator {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
BinaryOperator::Equality => {
serializer.serialize_unit_variant("BinaryOperator", 0, "==")
}
BinaryOperator::Inequality => {
serializer.serialize_unit_variant("BinaryOperator", 1, "!=")
}
BinaryOperator::StrictEquality => {
serializer.serialize_unit_variant("BinaryOperator", 2, "===")
}
BinaryOperator::StrictInequality => {
serializer.serialize_unit_variant("BinaryOperator", 3, "!==")
}
BinaryOperator::LessThan => serializer.serialize_unit_variant("BinaryOperator", 4, "<"),
BinaryOperator::LessEqualThan => {
serializer.serialize_unit_variant("BinaryOperator", 5, "<=")
}
BinaryOperator::GreaterThan => {
serializer.serialize_unit_variant("BinaryOperator", 6, ">")
}
BinaryOperator::GreaterEqualThan => {
serializer.serialize_unit_variant("BinaryOperator", 7, ">=")
}
BinaryOperator::Addition => serializer.serialize_unit_variant("BinaryOperator", 8, "+"),
BinaryOperator::Subtraction => {
serializer.serialize_unit_variant("BinaryOperator", 9, "-")
}
BinaryOperator::Multiplication => {
serializer.serialize_unit_variant("BinaryOperator", 10, "*")
}
BinaryOperator::Division => {
serializer.serialize_unit_variant("BinaryOperator", 11, "/")
}
BinaryOperator::Remainder => {
serializer.serialize_unit_variant("BinaryOperator", 12, "%")
}
BinaryOperator::Exponential => {
serializer.serialize_unit_variant("BinaryOperator", 13, "**")
}
BinaryOperator::ShiftLeft => {
serializer.serialize_unit_variant("BinaryOperator", 14, "<<")
}
BinaryOperator::ShiftRight => {
serializer.serialize_unit_variant("BinaryOperator", 15, ">>")
}
BinaryOperator::ShiftRightZeroFill => {
serializer.serialize_unit_variant("BinaryOperator", 16, ">>>")
}
BinaryOperator::BitwiseOR => {
serializer.serialize_unit_variant("BinaryOperator", 17, "|")
}
BinaryOperator::BitwiseXOR => {
serializer.serialize_unit_variant("BinaryOperator", 18, "^")
}
BinaryOperator::BitwiseAnd => {
serializer.serialize_unit_variant("BinaryOperator", 19, "&")
}
BinaryOperator::In => serializer.serialize_unit_variant("BinaryOperator", 20, "in"),
BinaryOperator::Instanceof => {
serializer.serialize_unit_variant("BinaryOperator", 21, "instanceof")
}
BinaryOperator::Equality => "==".serialize(serializer),
BinaryOperator::Inequality => "!=".serialize(serializer),
BinaryOperator::StrictEquality => "===".serialize(serializer),
BinaryOperator::StrictInequality => "!==".serialize(serializer),
BinaryOperator::LessThan => "<".serialize(serializer),
BinaryOperator::LessEqualThan => "<=".serialize(serializer),
BinaryOperator::GreaterThan => ">".serialize(serializer),
BinaryOperator::GreaterEqualThan => ">=".serialize(serializer),
BinaryOperator::Addition => "+".serialize(serializer),
BinaryOperator::Subtraction => "-".serialize(serializer),
BinaryOperator::Multiplication => "*".serialize(serializer),
BinaryOperator::Division => "/".serialize(serializer),
BinaryOperator::Remainder => "%".serialize(serializer),
BinaryOperator::Exponential => "**".serialize(serializer),
BinaryOperator::ShiftLeft => "<<".serialize(serializer),
BinaryOperator::ShiftRight => ">>".serialize(serializer),
BinaryOperator::ShiftRightZeroFill => ">>>".serialize(serializer),
BinaryOperator::BitwiseOR => "|".serialize(serializer),
BinaryOperator::BitwiseXOR => "^".serialize(serializer),
BinaryOperator::BitwiseAnd => "&".serialize(serializer),
BinaryOperator::In => "in".serialize(serializer),
BinaryOperator::Instanceof => "instanceof".serialize(serializer),
}
}
}
impl Serialize for LogicalOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for LogicalOperator {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
LogicalOperator::Or => serializer.serialize_unit_variant("LogicalOperator", 0, "||"),
LogicalOperator::And => serializer.serialize_unit_variant("LogicalOperator", 1, "&&"),
LogicalOperator::Coalesce => {
serializer.serialize_unit_variant("LogicalOperator", 2, "??")
}
LogicalOperator::Or => "||".serialize(serializer),
LogicalOperator::And => "&&".serialize(serializer),
LogicalOperator::Coalesce => "??".serialize(serializer),
}
}
}
impl Serialize for UnaryOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for UnaryOperator {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
UnaryOperator::UnaryPlus => serializer.serialize_unit_variant("UnaryOperator", 0, "+"),
UnaryOperator::UnaryNegation => {
serializer.serialize_unit_variant("UnaryOperator", 1, "-")
}
UnaryOperator::LogicalNot => serializer.serialize_unit_variant("UnaryOperator", 2, "!"),
UnaryOperator::BitwiseNot => serializer.serialize_unit_variant("UnaryOperator", 3, "~"),
UnaryOperator::Typeof => {
serializer.serialize_unit_variant("UnaryOperator", 4, "typeof")
}
UnaryOperator::Void => serializer.serialize_unit_variant("UnaryOperator", 5, "void"),
UnaryOperator::Delete => {
serializer.serialize_unit_variant("UnaryOperator", 6, "delete")
}
UnaryOperator::UnaryPlus => "+".serialize(serializer),
UnaryOperator::UnaryNegation => "-".serialize(serializer),
UnaryOperator::LogicalNot => "!".serialize(serializer),
UnaryOperator::BitwiseNot => "~".serialize(serializer),
UnaryOperator::Typeof => "typeof".serialize(serializer),
UnaryOperator::Void => "void".serialize(serializer),
UnaryOperator::Delete => "delete".serialize(serializer),
}
}
}
impl Serialize for UpdateOperator {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for UpdateOperator {
fn serialize<S: Serializer>(&self, serializer: S) {
match self {
UpdateOperator::Increment => {
serializer.serialize_unit_variant("UpdateOperator", 0, "++")
}
UpdateOperator::Decrement => {
serializer.serialize_unit_variant("UpdateOperator", 1, "--")
}
UpdateOperator::Increment => "++".serialize(serializer),
UpdateOperator::Decrement => "--".serialize(serializer),
}
}
}
+1
View File
@@ -28,5 +28,6 @@ oxc_prettier = { workspace = true }
console_error_panic_hook = { workspace = true }
serde = { workspace = true }
serde-wasm-bindgen = { workspace = true }
serde_json = { workspace = true }
tsify = { workspace = true }
wasm-bindgen = { workspace = true }
+15 -1
View File
@@ -466,8 +466,22 @@ impl Oxc {
}
fn convert_ast(&mut self, program: &mut Program) {
use serde::Deserialize;
Utf8ToUtf16::new().convert(program);
self.ast = program.serialize(&self.serializer).unwrap();
// Convert:
// 1. `Program` to JSON string using `ESTree`.
// 2. JSON string to `serde_json::Value`.
// 3. `serde_json::Value` to `wasm_bindgen::JsValue`.
// TODO: There has to be a better way!
let json = program.to_json();
let s = serde_json::de::StrRead::new(&json);
let mut deserializer = serde_json::Deserializer::new(s);
let value = serde_json::Value::deserialize(&mut deserializer).unwrap();
deserializer.end().unwrap();
self.ast = value.serialize(&self.serializer).unwrap();
self.comments = Self::map_comments(program.source_text, &program.comments);
}
-1
View File
@@ -29,7 +29,6 @@ oxc_sourcemap = { workspace = true, features = ["napi"] }
rustc-hash = { workspace = true }
self_cell = { workspace = true }
serde_json = { workspace = true }
string_wizard = { workspace = true, features = ["sourcemap", "serde"] }
ctor = { workspace = true }
+1 -1
View File
@@ -106,7 +106,7 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption
}
}
}
let program = serde_json::to_string(&ret.program).unwrap();
let program = ret.program.to_json();
let module = EcmaScriptModule::from(&ret.module_record);
ParseResult { source_text, program, module, comments, errors }
}
+18
View File
@@ -27,6 +27,24 @@ describe('parse', () => {
expect(code.substring(comment.start, comment.end)).toBe('/*' + comment.value + '*/');
});
it('`Infinity` is represented as `Infinity` number', () => {
const ret = parseSync('test.js', '1e+350');
expect(ret.errors.length).toBe(0);
expect(ret.program.body.length).toBe(1);
expect(ret.program.body[0]).toEqual({
type: 'ExpressionStatement',
start: 0,
end: 6,
expression: {
type: 'Literal',
start: 0,
end: 6,
value: Infinity,
raw: '1e+350',
},
});
});
it('`BigIntLiteral` has `value` as `BigInt`', () => {
const ret = parseSync('test.js', '123_456n');
expect(ret.errors.length).toBe(0);
+19 -25
View File
@@ -1,4 +1,4 @@
//! Derive for `Serialize` impls, which serialize AST to ESTree format in JSON.
//! Derive for `ESTree` impls, which serialize AST to ESTree format in JSON.
use std::borrow::Cow;
@@ -8,7 +8,7 @@ use syn::{parse_str, Expr};
use crate::{
schema::{Def, EnumDef, FieldDef, Schema, StructDef, TypeDef, VariantDef, Visibility},
utils::{create_safe_ident, number_lit},
utils::create_safe_ident,
Result,
};
@@ -16,7 +16,7 @@ use super::{
attr_positions, define_derive, AttrLocation, AttrPart, AttrPositions, Derive, StructOrEnum,
};
/// Derive for `Serialize` impls, which serialize AST to ESTree format in JSON.
/// Derive for `ESTree` impls, which serialize AST to ESTree format in JSON.
pub struct DeriveESTree;
define_derive!(DeriveESTree);
@@ -61,21 +61,17 @@ impl Derive for DeriveESTree {
fn prelude(&self) -> TokenStream {
quote! {
#![allow(unused_imports, clippy::match_same_arms)]
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
///@@line_break
use serde::{
__private::ser::FlatMapSerializer,
ser::SerializeMap,
Serialize, Serializer
use oxc_estree::{
ser::{AppendTo, AppendToConcat},
ESTree, FlatStructSerializer, Serializer, StructSerializer,
};
///@@line_break
use oxc_estree::ser::{AppendTo, AppendToConcat};
}
}
/// Generate implementation of `Serialize` for a struct or enum.
/// Generate implementation of `ESTree` for a struct or enum.
fn derive(&self, type_def: StructOrEnum, schema: &Schema) -> TokenStream {
let body = match type_def {
StructOrEnum::Struct(struct_def) => {
@@ -95,8 +91,8 @@ impl Derive for DeriveESTree {
let ty = type_def.ty_anon(schema);
quote! {
impl Serialize for #ty {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
impl ESTree for #ty {
fn serialize<S: Serializer>(&self, serializer: S) {
#body
}
}
@@ -255,7 +251,7 @@ fn generate_body_for_struct(struct_def: &StructDef, schema: &Schema) -> TokenStr
let type_field = if gen.add_type_field {
let type_name = struct_def.estree.rename.as_deref().unwrap_or_else(|| struct_def.name());
quote! {
map.serialize_entry("type", #type_name)?;
state.serialize_field("type", #type_name);
}
} else {
quote!()
@@ -263,10 +259,10 @@ fn generate_body_for_struct(struct_def: &StructDef, schema: &Schema) -> TokenStr
let stmts = gen.stmts;
quote! {
let mut map = serializer.serialize_map(None)?;
let mut state = serializer.serialize_struct();
#type_field
#stmts
map.end()
state.end();
}
}
@@ -282,7 +278,7 @@ struct StructSerializerGenerator<'s> {
/// `true` if a `type` field should be added.
/// `false` one already exists (or if `#[estree(no_type)]` attr on struct).
add_type_field: bool,
/// Crate in which the `Serialize` impl for the type will be generated
/// Crate in which the `ESTree` impl for the type will be generated
krate: &'s str,
/// Schema
schema: &'s Schema,
@@ -352,7 +348,7 @@ impl<'s> StructSerializerGenerator<'s> {
);
self.stmts.extend(quote! {
#self_path.#field_name_ident.serialize(FlatMapSerializer(&mut map))?;
#self_path.#field_name_ident.serialize(FlatStructSerializer(&mut state));
});
return;
}
@@ -384,7 +380,7 @@ impl<'s> StructSerializerGenerator<'s> {
};
self.stmts.extend(quote! {
map.serialize_entry(#field_camel_name, &#value)?;
state.serialize_field(#field_camel_name, &#value);
});
}
@@ -397,7 +393,7 @@ impl<'s> StructSerializerGenerator<'s> {
let converter = self.schema.meta_by_name(converter_name);
let converter_path = converter.import_path_from_crate(self.krate, self.schema);
self.stmts.extend(quote! {
map.serialize_entry(#field_name, &#converter_path(#self_path))?;
state.serialize_field(#field_name, &#converter_path(#self_path));
});
}
}
@@ -409,11 +405,9 @@ fn generate_body_for_enum(enum_def: &EnumDef, schema: &Schema) -> TokenStream {
let match_branches = enum_def.all_variants(schema).map(|variant| {
let variant_ident = variant.ident();
if variant.is_fieldless() {
let enum_name = enum_def.name();
let discriminant = number_lit(variant.discriminant);
let value = get_fieldless_variant_value(enum_def, variant);
quote! {
#enum_ident::#variant_ident => serializer.serialize_unit_variant(#enum_name, #discriminant, #value),
#enum_ident::#variant_ident => #value.serialize(serializer),
}
} else {
quote! {
@@ -469,7 +463,7 @@ pub fn should_flatten_field(field: &FieldDef, schema: &Schema) -> bool {
///
/// If the field's type is a struct, then usually it can.
/// But it can't in the case where that type is defined in a different crate from where
/// the `Serialize` impl will be generated, and one of the flattened fields is not public.
/// the `ESTree` impl will be generated, and one of the flattened fields is not public.
pub fn can_flatten_field_inline(field: &FieldDef, krate: &str, schema: &Schema) -> bool {
let field_type = field.type_def(schema);
let TypeDef::Struct(struct_def) = field_type else { return false };
+16 -1
View File
@@ -2,4 +2,19 @@ commit: bc5c1417
estree_test262 Summary:
AST Parsed : 48767/48767 (100.00%)
Positive Passed: 48767/48767 (100.00%)
Positive Passed: 48762/48767 (99.99%)
tasks/coverage/test262/test/built-ins/Array/prototype/indexOf/15.4.4.14-10-1.js
serde_json error: number out of range at line 1 column 3184
tasks/coverage/test262/test/built-ins/Array/prototype/lastIndexOf/15.4.4.15-9-1.js
serde_json error: number out of range at line 1 column 3184
tasks/coverage/test262/test/built-ins/Number/S9.3.1_A6_T1.js
serde_json error: number out of range at line 1 column 1792
tasks/coverage/test262/test/built-ins/Number/S9.3.1_A6_T2.js
serde_json error: number out of range at line 1 column 3552
tasks/coverage/test262/test/language/types/number/8.5.1.js
serde_json error: number out of range at line 1 column 9723
+1 -1
View File
@@ -76,7 +76,7 @@ impl CompilerInterface for Driver {
self.errors.push(OxcDiagnostic::error("SourceType must not be unambiguous."));
}
// Make sure serialization doesn't crash; also for code coverage.
program.test_to_json().unwrap();
program.to_json();
ControlFlow::Continue(())
}
+6 -3
View File
@@ -1,7 +1,7 @@
import assert from 'assert';
import { parseSync } from '../../npm/parser-wasm/node/oxc_parser_wasm.js';
const code = '/abc/gu; 123n;';
const code = '/abc/gu; 123n; 1e+350;';
const result = parseSync(code, { sourceFilename: 'test.ts' });
assert(result.errors.length === 0);
@@ -12,8 +12,11 @@ assert(result.program === program);
// Check output is correct
assert(program.type === 'Program');
assert(program.body.length === 2);
assert(program.body.length === 3);
// Check `RegExp`s and `BigInt`s are deserialized correctly
// Check `RegExp`s, `BigInt`s and `Infinity` are deserialized correctly
assert(program.body[0].expression.value instanceof RegExp);
assert(typeof program.body[1].expression.value === 'bigint');
const inf = program.body[2].expression.value;
assert(typeof inf === 'number');
assert(inf === Infinity);