refactor(napi/oxlint, napi/parser): remove source length from RawTransferMetadata (#12483)

Refactor to oxlint JS plugins and raw transfer. Remove source text length field from `RawTransferMetadata`. It's not necessary - source text is part of `Program`, so the length can be obtained from there, it doesn't need to be duplicated.
This commit is contained in:
overlookmotel committed 2025-07-24 08:54:16 +00:00
1 parent 7c75dba281
commit f0b1f0d485
12 files changed
+68 -72

No files matched your search

@@ -96,7 +96,7 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map {
("DebuggerStatement", StructDetails { field_order: None }),
("CommentNodeId", StructDetails { field_order: None }),
("Character", StructDetails { field_order: Some(&[0, 2, 1]) }),
("RawTransferMetadata2", StructDetails { field_order: Some(&[0, 3, 1, 2]) }),
("RawTransferMetadata2", StructDetails { field_order: Some(&[1, 2, 0]) }),
("StringLiteral", StructDetails { field_order: None }),
("TSInferType", StructDetails { field_order: None }),
("TSThisType", StructDetails { field_order: None }),
@@ -243,7 +243,7 @@ pub static STRUCTS: phf::Map<&'static str, StructDetails> = ::phf::Map {
("SpreadElement", StructDetails { field_order: None }),
("MetaProperty", StructDetails { field_order: None }),
("TSIntersectionType", StructDetails { field_order: None }),
("RawTransferMetadata", StructDetails { field_order: Some(&[0, 3, 1, 2]) }),
("RawTransferMetadata", StructDetails { field_order: Some(&[1, 2, 0]) }),
("ConditionalExpression", StructDetails { field_order: None }),
("PrivateInExpression", StructDetails { field_order: None }),
("TSStringKeyword", StructDetails { field_order: None }),
@@ -11,22 +11,20 @@ use crate::*;
const _: () = {
// Padding: 3 bytes
assert!(size_of::<RawTransferMetadata2>() == 16);
assert!(align_of::<RawTransferMetadata2>() == 4);
assert!(offset_of!(RawTransferMetadata2, data_offset) == 0);
assert!(align_of::<RawTransferMetadata2>() == 8);
assert!(offset_of!(RawTransferMetadata2, data_offset) == 8);
assert!(offset_of!(RawTransferMetadata2, is_ts) == 12);
assert!(offset_of!(RawTransferMetadata2, source_len) == 4);
assert!(offset_of!(RawTransferMetadata2, _padding) == 8);
assert!(offset_of!(RawTransferMetadata2, _padding) == 0);
};
#[cfg(target_pointer_width = "32")]
const _: () = {
// Padding: 3 bytes
assert!(size_of::<RawTransferMetadata2>() == 16);
assert!(align_of::<RawTransferMetadata2>() == 4);
assert!(offset_of!(RawTransferMetadata2, data_offset) == 0);
assert!(align_of::<RawTransferMetadata2>() == 8);
assert!(offset_of!(RawTransferMetadata2, data_offset) == 8);
assert!(offset_of!(RawTransferMetadata2, is_ts) == 12);
assert!(offset_of!(RawTransferMetadata2, source_len) == 4);
assert!(offset_of!(RawTransferMetadata2, _padding) == 8);
assert!(offset_of!(RawTransferMetadata2, _padding) == 0);
};
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
+5 -9
View File
@@ -247,13 +247,11 @@ impl Linter {
// `external_linter` always exists when `oxlint2` feature is enabled
let external_linter = self.external_linter.as_ref().unwrap();
// Write offset of `Program` and source text length in metadata at end of buffer
// Write offset of `Program` in metadata at end of buffer
let program = semantic.nodes().program().unwrap();
let program_offset = ptr::from_ref(program) as u32;
#[expect(clippy::cast_possible_truncation)]
let source_len = program.source_text.len() as u32;
let metadata = RawTransferMetadata::new(program_offset, source_len);
let metadata = RawTransferMetadata::new(program_offset);
let metadata_ptr = allocator.end_ptr().cast::<RawTransferMetadata>();
// SAFETY: `Allocator` was created by `FixedSizeAllocator` which reserved space after `end_ptr`
// for a `RawTransferMetadata`. `end_ptr` is aligned for `FixedSizeAllocator`.
@@ -318,10 +316,8 @@ struct RawTransferMetadata2 {
pub data_offset: u32,
/// `true` if AST is TypeScript.
pub is_ts: bool,
/// Offset of `u32` containing source text length within buffer.
pub(crate) source_len: u32,
/// Padding to pad struct to size 16.
pub(crate) _padding: u32,
pub(crate) _padding: u64,
}
#[cfg(all(feature = "oxlint2", not(feature = "disable_oxlint2")))]
@@ -329,8 +325,8 @@ use RawTransferMetadata2 as RawTransferMetadata;
#[cfg(all(feature = "oxlint2", not(feature = "disable_oxlint2")))]
impl RawTransferMetadata {
pub fn new(data_offset: u32, source_len: u32) -> Self {
Self { data_offset, is_ts: false, source_len, _padding: 0 }
pub fn new(data_offset: u32) -> Self {
Self { data_offset, is_ts: false, _padding: 0 }
}
}
+4 -4
View File
@@ -3,16 +3,16 @@
const BUFFER_SIZE = 2147483616,
BUFFER_ALIGN = 4294967296,
DATA_POINTER_POS_32 = 536870900,
DATA_POINTER_POS_32 = 536870902,
IS_TS_FLAG_POS = 2147483612,
SOURCE_LEN_POS_32 = 536870901,
PROGRAM_OFFSET = 0;
PROGRAM_OFFSET = 0,
SOURCE_LEN_OFFSET = 16;
module.exports = {
BUFFER_SIZE,
BUFFER_ALIGN,
DATA_POINTER_POS_32,
IS_TS_FLAG_POS,
SOURCE_LEN_POS_32,
PROGRAM_OFFSET,
SOURCE_LEN_OFFSET,
};
+4 -3
View File
@@ -1,6 +1,6 @@
import { createRequire } from 'node:module';
import { lint } from './bindings.js';
import { DATA_POINTER_POS_32, SOURCE_LEN_POS_32 } from './generated/constants.cjs';
import { DATA_POINTER_POS_32, SOURCE_LEN_OFFSET } from './generated/constants.cjs';
import { getErrorMessage } from './utils.js';
import { addVisitorToCompiled, compiledVisitor, finalizeCompiledVisitor, initCompiledVisitor } from './visitor.js';
@@ -167,8 +167,9 @@ function lintFile([filePath, bufferId, buffer, ruleIds]) {
// Some rules seen in the wild return an empty visitor object from `create` if some initial check fails
// e.g. file extension is not one the rule acts on.
if (needsVisit) {
const programPos = buffer.uint32[DATA_POINTER_POS_32],
sourceByteLen = buffer.uint32[SOURCE_LEN_POS_32];
const { uint32 } = buffer,
programPos = uint32[DATA_POINTER_POS_32],
sourceByteLen = uint32[(programPos + SOURCE_LEN_OFFSET) >> 2];
const sourceText = textDecoder.decode(buffer.subarray(0, sourceByteLen));
const sourceIsAscii = sourceText.length === sourceByteLen;
+4 -4
View File
@@ -3,16 +3,16 @@
const BUFFER_SIZE = 2147483616,
BUFFER_ALIGN = 4294967296,
DATA_POINTER_POS_32 = 536870900,
DATA_POINTER_POS_32 = 536870902,
IS_TS_FLAG_POS = 2147483612,
SOURCE_LEN_POS_32 = 536870901,
PROGRAM_OFFSET = 0;
PROGRAM_OFFSET = 0,
SOURCE_LEN_OFFSET = 16;
module.exports = {
BUFFER_SIZE,
BUFFER_ALIGN,
DATA_POINTER_POS_32,
IS_TS_FLAG_POS,
SOURCE_LEN_POS_32,
PROGRAM_OFFSET,
SOURCE_LEN_OFFSET,
};
+6 -6
View File
@@ -20,7 +20,7 @@ function deserialize(buffer, sourceTextInput, sourceByteLenInput) {
sourceByteLen = sourceByteLenInput;
sourceIsAscii = sourceText.length === sourceByteLen;
const data = deserializeRawTransferData(uint32[536870900]);
const data = deserializeRawTransferData(uint32[536870902]);
uint8 =
uint32 =
@@ -5238,16 +5238,16 @@ function deserializeBoxTSExternalModuleReference(pos) {
return deserializeTSExternalModuleReference(uint32[pos >> 2]);
}
function deserializeOptionNameSpan(pos) {
if (uint32[(pos + 8) >> 2] === 0 && uint32[(pos + 12) >> 2] === 0) return null;
return deserializeNameSpan(pos);
}
function deserializeU64(pos) {
const pos32 = pos >> 2;
return uint32[pos32] + uint32[pos32 + 1] * 4294967296;
}
function deserializeOptionNameSpan(pos) {
if (uint32[(pos + 8) >> 2] === 0 && uint32[(pos + 12) >> 2] === 0) return null;
return deserializeNameSpan(pos);
}
function deserializeOptionU64(pos) {
if (uint8[pos] === 0) return null;
return deserializeU64(pos + 8);
+6 -6
View File
@@ -20,7 +20,7 @@ function deserialize(buffer, sourceTextInput, sourceByteLenInput) {
sourceByteLen = sourceByteLenInput;
sourceIsAscii = sourceText.length === sourceByteLen;
const data = deserializeRawTransferData(uint32[536870900]);
const data = deserializeRawTransferData(uint32[536870902]);
uint8 =
uint32 =
@@ -5369,16 +5369,16 @@ function deserializeBoxTSExternalModuleReference(pos) {
return deserializeTSExternalModuleReference(uint32[pos >> 2]);
}
function deserializeOptionNameSpan(pos) {
if (uint32[(pos + 8) >> 2] === 0 && uint32[(pos + 12) >> 2] === 0) return null;
return deserializeNameSpan(pos);
}
function deserializeU64(pos) {
const pos32 = pos >> 2;
return uint32[pos32] + uint32[pos32 + 1] * 4294967296;
}
function deserializeOptionNameSpan(pos) {
if (uint32[(pos + 8) >> 2] === 0 && uint32[(pos + 12) >> 2] === 0) return null;
return deserializeNameSpan(pos);
}
function deserializeOptionU64(pos) {
if (uint8[pos] === 0) return null;
return deserializeU64(pos + 8);
+5 -5
View File
@@ -13752,17 +13752,17 @@ function constructBoxTSExternalModuleReference(pos, ast) {
return new TSExternalModuleReference(ast.buffer.uint32[pos >> 2], ast);
}
function constructOptionNameSpan(pos, ast) {
if (ast.buffer.uint32[(pos + 8) >> 2] === 0 && ast.buffer.uint32[(pos + 12) >> 2] === 0) return null;
return new NameSpan(pos, ast);
}
function constructU64(pos, ast) {
const { uint32 } = ast.buffer,
pos32 = pos >> 2;
return uint32[pos32] + uint32[pos32 + 1] * 4294967296;
}
function constructOptionNameSpan(pos, ast) {
if (ast.buffer.uint32[(pos + 8) >> 2] === 0 && ast.buffer.uint32[(pos + 12) >> 2] === 0) return null;
return new NameSpan(pos, ast);
}
function constructOptionU64(pos, ast) {
if (ast.buffer[pos] === 0) return null;
return constructU64(pos + 8, ast);
+6 -8
View File
@@ -19,11 +19,10 @@ const _: () = {
// Padding: 3 bytes
assert!(size_of::<RawTransferMetadata>() == 16);
assert!(align_of::<RawTransferMetadata>() == 4);
assert!(offset_of!(RawTransferMetadata, data_offset) == 0);
assert!(align_of::<RawTransferMetadata>() == 8);
assert!(offset_of!(RawTransferMetadata, data_offset) == 8);
assert!(offset_of!(RawTransferMetadata, is_ts) == 12);
assert!(offset_of!(RawTransferMetadata, source_len) == 4);
assert!(offset_of!(RawTransferMetadata, _padding) == 8);
assert!(offset_of!(RawTransferMetadata, _padding) == 0);
// Padding: 7 bytes
assert!(size_of::<Error>() == 80);
@@ -78,11 +77,10 @@ const _: () = {
// Padding: 3 bytes
assert!(size_of::<RawTransferMetadata>() == 16);
assert!(align_of::<RawTransferMetadata>() == 4);
assert!(offset_of!(RawTransferMetadata, data_offset) == 0);
assert!(align_of::<RawTransferMetadata>() == 8);
assert!(offset_of!(RawTransferMetadata, data_offset) == 8);
assert!(offset_of!(RawTransferMetadata, is_ts) == 12);
assert!(offset_of!(RawTransferMetadata, source_len) == 4);
assert!(offset_of!(RawTransferMetadata, _padding) == 8);
assert!(offset_of!(RawTransferMetadata, _padding) == 0);
// Padding: 3 bytes
assert!(size_of::<Error>() == 44);
+2 -5
View File
@@ -36,16 +36,13 @@ pub struct RawTransferMetadata {
pub data_offset: u32,
/// `true` if AST is TypeScript.
pub is_ts: bool,
/// Offset of `u32` containing source text length within buffer.
/// Not used in this implementation. Used by `napi/oxlint2`.
pub(crate) source_len: u32,
/// Padding to pad struct to size 16.
pub(crate) _padding: u32,
pub(crate) _padding: u64,
}
impl RawTransferMetadata {
pub fn new(data_offset: u32, is_ts: bool) -> Self {
Self { data_offset, is_ts, source_len: 0, _padding: 0 }
Self { data_offset, is_ts, _padding: 0 }
}
}
+18 -12
View File
@@ -26,6 +26,9 @@ use crate::{
use super::define_generator;
/// Offset of length field in `&str`
const STR_LEN_OFFSET: u32 = 8;
/// Bytes reserved for `malloc`'s metadata
const MALLOC_RESERVED_SIZE: u32 = 16;
@@ -1015,10 +1018,10 @@ struct Constants {
data_pointer_pos: u32,
/// Offset within buffer of `bool` indicating if AST is TS or JS
is_ts_pos: u32,
/// Offset within buffer of `u32` containing position of `u32` containing source text length
source_len_pos: u32,
/// Offset of `Program` in buffer, relative to position of `RawTransferData`
program_offset: u32,
/// Offset of `u32` source text length, relative to position of `Program`
source_len_offset: u32,
/// Size of `RawTransferData` in bytes
raw_metadata_size: u32,
}
@@ -1029,13 +1032,12 @@ fn generate_constants(consts: Constants) -> (String, TokenStream) {
buffer_size,
data_pointer_pos,
is_ts_pos,
source_len_pos,
program_offset,
source_len_offset,
raw_metadata_size,
} = consts;
let data_pointer_pos_32 = data_pointer_pos / 4;
let source_len_pos_32 = source_len_pos / 4;
#[rustfmt::skip]
let js_output = format!("
@@ -1043,16 +1045,16 @@ fn generate_constants(consts: Constants) -> (String, TokenStream) {
BUFFER_ALIGN = {BLOCK_ALIGN},
DATA_POINTER_POS_32 = {data_pointer_pos_32},
IS_TS_FLAG_POS = {is_ts_pos},
SOURCE_LEN_POS_32 = {source_len_pos_32},
PROGRAM_OFFSET = {program_offset};
PROGRAM_OFFSET = {program_offset},
SOURCE_LEN_OFFSET = {source_len_offset};
module.exports = {{
BUFFER_SIZE,
BUFFER_ALIGN,
DATA_POINTER_POS_32,
IS_TS_FLAG_POS,
SOURCE_LEN_POS_32,
PROGRAM_OFFSET,
SOURCE_LEN_OFFSET,
}};
");
@@ -1085,7 +1087,6 @@ fn get_constants(schema: &Schema) -> Constants {
let mut data_offset_field = None;
let mut is_ts_field = None;
let mut source_len_field = None;
for (field1, field2) in raw_metadata_struct.fields.iter().zip(&raw_metadata2_struct.fields) {
assert_eq!(field1.name(), field2.name());
assert_eq!(field1.type_id, field2.type_id);
@@ -1093,13 +1094,11 @@ fn get_constants(schema: &Schema) -> Constants {
match field1.name() {
"data_offset" => data_offset_field = Some(field1),
"is_ts" => is_ts_field = Some(field1),
"source_len" => source_len_field = Some(field1),
_ => {}
}
}
let data_offset_field = data_offset_field.unwrap();
let is_ts_field = is_ts_field.unwrap();
let source_len_field = source_len_field.unwrap();
let raw_metadata_size = raw_metadata_struct.layout_64().size;
@@ -1115,7 +1114,6 @@ fn get_constants(schema: &Schema) -> Constants {
let raw_metadata_pos = buffer_size - raw_metadata_size;
let data_pointer_pos = raw_metadata_pos + data_offset_field.offset_64();
let is_ts_pos = raw_metadata_pos + is_ts_field.offset_64();
let source_len_pos = raw_metadata_pos + source_len_field.offset_64();
let program_offset = schema
.type_by_name("RawTransferData")
@@ -1124,12 +1122,20 @@ fn get_constants(schema: &Schema) -> Constants {
.field_by_name("program")
.offset_64();
let source_len_offset = schema
.type_by_name("Program")
.as_struct()
.unwrap()
.field_by_name("source_text")
.offset_64()
+ STR_LEN_OFFSET;
Constants {
buffer_size,
data_pointer_pos,
is_ts_pos,
source_len_pos,
program_offset,
source_len_offset,
raw_metadata_size,
}
}