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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1755,7 +1755,7 @@ pub struct FormalParameters<'a> {
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)]
// Pluralize as `FormalParameterList` to avoid naming clash with `FormalParameters`.
#[plural(FormalParameterList)]
#[estree(no_type, field_order(pattern, decorators, accessibility, readonly, r#override))]
#[estree(no_type)]
pub struct FormalParameter<'a> {
#[estree(skip)]
pub span: Span,
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_ast/src/generated/derive_estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,9 @@ impl ESTree for CallExpression<'_> {
state.serialize_field("start", &self.span.start);
state.serialize_field("end", &self.span.end);
state.serialize_field("callee", &self.callee);
state.serialize_ts_field("typeParameters", &self.type_parameters);
state.serialize_field("arguments", &self.arguments);
state.serialize_field("optional", &self.optional);
state.serialize_ts_field("typeParameters", &self.type_parameters);
state.end();
}
}
Expand Down Expand Up @@ -1749,11 +1749,11 @@ impl ESTree for ExportNamedDeclaration<'_> {
state.serialize_field("declaration", &self.declaration);
state.serialize_field("specifiers", &self.specifiers);
state.serialize_field("source", &self.source);
state.serialize_ts_field("exportKind", &self.export_kind);
state.serialize_field(
"attributes",
&crate::serialize::ExportNamedDeclarationWithClause(self),
);
state.serialize_ts_field("exportKind", &self.export_kind);
state.end();
}
}
Expand Down
4 changes: 2 additions & 2 deletions napi/parser/deserialize-ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ function deserializeCallExpression(pos) {
start: deserializeU32(pos),
end: deserializeU32(pos + 4),
callee: deserializeExpression(pos + 8),
typeParameters: deserializeOptionBoxTSTypeParameterInstantiation(pos + 24),
arguments: deserializeVecArgument(pos + 32),
optional: deserializeBool(pos + 64),
typeParameters: deserializeOptionBoxTSTypeParameterInstantiation(pos + 24),
};
}

Expand Down Expand Up @@ -1024,8 +1024,8 @@ function deserializeExportNamedDeclaration(pos) {
declaration: deserializeOptionDeclaration(pos + 8),
specifiers: deserializeVecExportSpecifier(pos + 24),
source: deserializeOptionStringLiteral(pos + 56),
exportKind: deserializeImportOrExportKind(pos + 96),
attributes: withClause === null ? [] : withClause.withEntries,
exportKind: deserializeImportOrExportKind(pos + 96),
};
}

Expand Down
4 changes: 2 additions & 2 deletions npm/oxc-types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ export interface PrivateFieldExpression extends Span {
export interface CallExpression extends Span {
type: 'CallExpression';
callee: Expression;
typeParameters: TSTypeParameterInstantiation | null;
arguments: Array<Argument>;
optional: boolean;
typeParameters: TSTypeParameterInstantiation | null;
}

export interface NewExpression extends Span {
Expand Down Expand Up @@ -731,8 +731,8 @@ export interface ExportNamedDeclaration extends Span {
declaration: Declaration | null;
specifiers: Array<ExportSpecifier>;
source: StringLiteral | null;
exportKind: ImportOrExportKind;
attributes: Array<ImportAttribute>;
exportKind: ImportOrExportKind;
}

export interface ExportDefaultDeclaration extends Span {
Expand Down
108 changes: 89 additions & 19 deletions tasks/ast_tools/src/derives/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use quote::quote;

use crate::{
Result,
codegen::{Codegen, DeriveId},
schema::{Def, EnumDef, FieldDef, File, Schema, StructDef, TypeDef, VariantDef, Visibility},
utils::create_safe_ident,
};
Expand Down Expand Up @@ -58,6 +59,12 @@ impl Derive for DeriveESTree {
}
}

/// Initialize `estree.field_order` on structs.
fn prepare(&self, schema: &mut Schema, codegen: &Codegen) {
let derive_id = codegen.get_derive_id_by_name(self.trait_name());
prepare_field_orders(schema, derive_id);
}

fn prelude(&self) -> TokenStream {
quote! {
#![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)]
Expand Down Expand Up @@ -115,7 +122,7 @@ fn parse_estree_attr(location: AttrLocation, part: AttrPart) -> Result<()> {
}
field_indices.push(field_index);
}
struct_def.estree.field_indices = Some(field_indices);
struct_def.estree.field_indices = field_indices;
}
AttrPart::String("ts_alias", value) => struct_def.estree.ts_alias = Some(value),
AttrPart::String("add_ts_def", value) => struct_def.estree.add_ts_def = Some(value),
Expand Down Expand Up @@ -215,6 +222,81 @@ fn parse_ts_attr(location: AttrLocation, part: &AttrPart) -> Result<()> {
Ok(())
}

/// Initialize `estree.field_order` on all structs.
fn prepare_field_orders(schema: &mut Schema, estree_derive_id: DeriveId) {
// Note: Outside the loop to avoid allocating temporary `Vec`s on each turn of the loop.
// Instead, reuse this `Vec` over and over.
let mut field_indices_temp = vec![];

for type_id in schema.types.indices() {
let Some(struct_def) = schema.types[type_id].as_struct() else { continue };
if !struct_def.generates_derive(estree_derive_id) {
continue;
}

if struct_def.estree.field_indices.is_empty() {
// No field order specified with `#[estree(field_order(...))]`.
// Set default order:
// 1. Fields without `#[ts]` attr, in definition order.
// 2. Fields with `#[ts]` attr, in definition order.
// 3. Added fields `#[estree(add_fields(...)]`, in definition order.
let mut field_indices = vec![];
let ts_field_indices = &mut field_indices_temp;
for (field_index, field) in struct_def.fields.iter().enumerate() {
if !should_skip_field(field, schema) {
let field_index = u8::try_from(field_index).unwrap();
if field.estree.is_ts {
ts_field_indices.push(field_index);
} else {
field_indices.push(field_index);
}
}
}
field_indices.append(ts_field_indices);

let fields_len = struct_def.fields.len();
for (index, _) in struct_def.estree.add_fields.iter().enumerate() {
let field_index = u8::try_from(fields_len + index).unwrap();
field_indices.push(field_index);
}

let struct_def = schema.struct_def_mut(type_id);
struct_def.estree.field_indices = field_indices;
} else {
// Custom field order specified with `#[estree(field_order(...))]`.
// Verify does not miss any fields, no fields marked `#[estree(skip)]` are included.
let unskipped_field_indices = &mut field_indices_temp;
for (field_index, field) in struct_def.fields.iter().enumerate() {
if !should_skip_field(field, schema) {
let field_index = u8::try_from(field_index).unwrap();
unskipped_field_indices.push(field_index);
}
}

let fields_len = struct_def.fields.len();
for &field_index in &struct_def.estree.field_indices {
if (field_index as usize) < fields_len {
assert!(
unskipped_field_indices.contains(&field_index),
"Skipped field `{}` included in `#[estree(field_order)]`: `{}`",
struct_def.fields[field_index as usize].name(),
struct_def.name()
);
}
}

assert!(
struct_def.estree.field_indices.len()
== unskipped_field_indices.len() + struct_def.estree.add_fields.len(),
"`#[estree(field_order)]` misses fields: `{}`",
struct_def.name()
);

unskipped_field_indices.clear();
}
}
}

/// Generate implementation of `ESTree` for a struct or enum.
fn generate_impl_for_type(type_def: StructOrEnum, schema: &Schema) -> TokenStream {
let body = match type_def {
Expand Down Expand Up @@ -294,25 +376,13 @@ impl<'s> StructSerializerGenerator<'s> {

/// Generate code to serialize all fields in a struct.
fn generate_stmts_for_struct(&mut self, struct_def: &StructDef, self_path: &TokenStream) {
if let Some(field_indices) = &struct_def.estree.field_indices {
// Specified field order - serialize in this order
for &field_index in field_indices {
let field_index = field_index as usize;
if let Some(field) = struct_def.fields.get(field_index) {
self.generate_stmts_for_field(field, struct_def, self_path);
} else {
let (field_name, converter_name) =
&struct_def.estree.add_fields[field_index - struct_def.fields.len()];
self.generate_stmt_for_added_field(field_name, converter_name, self_path);
}
}
} else {
// No specified field order - serialize in original order
for field in &struct_def.fields {
for &field_index in &struct_def.estree.field_indices {
let field_index = field_index as usize;
if let Some(field) = struct_def.fields.get(field_index) {
self.generate_stmts_for_field(field, struct_def, self_path);
}

for (field_name, converter_name) in &struct_def.estree.add_fields {
} else {
let (field_name, converter_name) =
&struct_def.estree.add_fields[field_index - struct_def.fields.len()];
self.generate_stmt_for_added_field(field_name, converter_name, self_path);
}
}
Expand Down
29 changes: 6 additions & 23 deletions tasks/ast_tools/src/generators/raw_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,30 +236,13 @@ impl<'s> StructDeserializerGenerator<'s> {
}

fn generate_struct_fields(&mut self, struct_def: &StructDef, struct_offset: u32) {
if let Some(field_indices) = &struct_def.estree.field_indices {
// Specified field order - serialize in this order
for &field_index in field_indices {
let field_index = field_index as usize;
if let Some(field) = struct_def.fields.get(field_index) {
self.generate_struct_field_owned(field, struct_def, struct_offset);
} else {
let (field_name, converter_name) =
&struct_def.estree.add_fields[field_index - struct_def.fields.len()];
self.generate_struct_field_added(
struct_def,
field_name,
converter_name,
struct_offset,
);
}
}
} else {
// No specified field order - serialize in original order
for field in &struct_def.fields {
for &field_index in &struct_def.estree.field_indices {
let field_index = field_index as usize;
if let Some(field) = struct_def.fields.get(field_index) {
self.generate_struct_field_owned(field, struct_def, struct_offset);
}

for (field_name, converter_name) in &struct_def.estree.add_fields {
} else {
let (field_name, converter_name) =
&struct_def.estree.add_fields[field_index - struct_def.fields.len()];
self.generate_struct_field_added(
struct_def,
field_name,
Expand Down
36 changes: 6 additions & 30 deletions tasks/ast_tools/src/generators/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,33 +125,9 @@ fn generate_ts_type_def_for_struct(

let mut output_as_type = false;

if let Some(field_indices) = &struct_def.estree.field_indices {
// Specified field order - output in this order
for &field_index in field_indices {
let field_index = field_index as usize;
if let Some(field) = struct_def.fields.get(field_index) {
generate_ts_type_def_for_struct_field(
struct_def,
field,
&mut fields_str,
&mut extends,
&mut output_as_type,
schema,
);
} else {
let (field_name, converter_name) =
&struct_def.estree.add_fields[field_index - struct_def.fields.len()];
generate_ts_type_def_for_added_struct_field(
field_name,
converter_name,
&mut fields_str,
schema,
);
}
}
} else {
// No specified field order - output in original order
for field in &struct_def.fields {
for &field_index in &struct_def.estree.field_indices {
let field_index = field_index as usize;
if let Some(field) = struct_def.fields.get(field_index) {
generate_ts_type_def_for_struct_field(
struct_def,
field,
Expand All @@ -160,9 +136,9 @@ fn generate_ts_type_def_for_struct(
&mut output_as_type,
schema,
);
}

for (field_name, converter_name) in &struct_def.estree.add_fields {
} else {
let (field_name, converter_name) =
&struct_def.estree.add_fields[field_index - struct_def.fields.len()];
generate_ts_type_def_for_added_struct_field(
field_name,
converter_name,
Expand Down
2 changes: 1 addition & 1 deletion tasks/ast_tools/src/schema/extensions/estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct ESTreeStruct {
/// * Actual struct field: index of the field.
/// * Added field: `struct_def.fields.len() + added_field_index`.
/// Does not include `type` field, if it's automatically added.
pub field_indices: Option<Vec<u8>>,
pub field_indices: Vec<u8>,
/// TS alias.
/// e.g. `#[estree(ts_alias = "null")]` means this type won't have a type def generated,
/// and any struct / enum referencing it will substitute `null` as the type.
Expand Down
Loading