Skip to content

Commit 6ac09e2

Browse files
committed
feat(linter/plugins): support source text not being at start of buffer (#18375)
Previous raw transfer required that the source text start exactly at the start of the buffer. In linter, relax this restriction and handle when source text is stored elsewhere in the buffer. This is necessary for stripping BOM from source (#18376), as then the source text used on JS side starts 3 bytes after the start of the buffer.
1 parent b95a89f commit 6ac09e2

14 files changed

Lines changed: 85 additions & 48 deletions

File tree

apps/oxlint/src-js/generated/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export const DATA_POINTER_POS_32 = 536870902;
77
export const IS_TS_FLAG_POS = 2147483612;
88
export const IS_JSX_FLAG_POS = 2147483613;
99
export const PROGRAM_OFFSET = 0;
10+
export const SOURCE_START_OFFSET = 8;
1011
export const SOURCE_LEN_OFFSET = 16;

apps/oxlint/src-js/generated/deserialize.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type GetLoc = (node: { range: [number, number] }) => SourceLocation;
1010
export declare function deserializeProgramOnly(
1111
buffer: BufferWithArrays,
1212
sourceText: string,
13+
sourceStartPosInput: number,
1314
sourceByteLen: number,
1415
getLoc: GetLoc,
1516
): Program;

apps/oxlint/src-js/generated/deserialize.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ let uint8,
88
float64,
99
sourceText,
1010
sourceIsAscii,
11-
sourceByteLen,
11+
sourceStartPos,
12+
sourceEndPos,
1213
astId = 0,
1314
parent = null,
1415
getLoc;
@@ -25,16 +26,23 @@ const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
2526
},
2627
});
2728

28-
export function deserializeProgramOnly(buffer, sourceText, sourceByteLen, getLoc) {
29+
export function deserializeProgramOnly(
30+
buffer,
31+
sourceText,
32+
sourceStartPosInput,
33+
sourceByteLen,
34+
getLoc,
35+
) {
36+
sourceStartPos = sourceStartPosInput;
37+
sourceEndPos = sourceStartPosInput + sourceByteLen;
2938
return deserializeWith(buffer, sourceText, sourceByteLen, getLoc, deserializeProgram);
3039
}
3140

32-
function deserializeWith(buffer, sourceTextInput, sourceByteLenInput, getLocInput, deserialize) {
41+
function deserializeWith(buffer, sourceTextInput, sourceByteLen, getLocInput, deserialize) {
3342
uint8 = buffer;
3443
uint32 = buffer.uint32;
3544
float64 = buffer.float64;
3645
sourceText = sourceTextInput;
37-
sourceByteLen = sourceByteLenInput;
3846
sourceIsAscii = sourceText.length === sourceByteLen;
3947
getLoc = getLocInput;
4048
return deserialize(uint32[536870902]);
@@ -5921,7 +5929,9 @@ function deserializeStr(pos) {
59215929
len = uint32[pos32 + 2];
59225930
if (len === 0) return "";
59235931
pos = uint32[pos32];
5924-
if (sourceIsAscii && pos < sourceByteLen) return sourceText.substr(pos, len);
5932+
if (sourceIsAscii && pos < sourceEndPos)
5933+
// Source text may not start at position 0, so `pos - sourceStartPos` is the index into `sourceText` string
5934+
return sourceText.substr(pos - sourceStartPos, len);
59255935
// Longer strings use `TextDecoder`
59265936
// TODO: Find best switch-over point
59275937
let end = pos + len;

apps/oxlint/src-js/plugins/source_code.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { DATA_POINTER_POS_32, SOURCE_LEN_OFFSET } from "../generated/constants.ts";
1+
import {
2+
DATA_POINTER_POS_32,
3+
SOURCE_START_OFFSET,
4+
SOURCE_LEN_OFFSET,
5+
} from "../generated/constants.ts";
26

37
// We use the deserializer which removes `ParenthesizedExpression`s from AST,
48
// and with `range`, `loc`, and `parent` properties on AST nodes, to match ESLint
@@ -33,6 +37,7 @@ let hasBOM = false;
3337
// Lazily populated when `SOURCE_CODE.text` or `SOURCE_CODE.ast` is accessed,
3438
// or `initAst()` is called before the AST is walked.
3539
export let sourceText: string | null = null;
40+
let sourceStartPos: number = 0;
3641
let sourceByteLen: number = 0;
3742
export let ast: Program | null = null;
3843

@@ -62,8 +67,9 @@ export function initSourceText(): void {
6267
debugAssertIsNonNull(buffer);
6368
const { uint32 } = buffer,
6469
programPos = uint32[DATA_POINTER_POS_32];
70+
sourceStartPos = uint32[(programPos + SOURCE_START_OFFSET) >> 2];
6571
sourceByteLen = uint32[(programPos + SOURCE_LEN_OFFSET) >> 2];
66-
sourceText = textDecoder.decode(buffer.subarray(0, sourceByteLen));
72+
sourceText = textDecoder.decode(buffer.subarray(sourceStartPos, sourceStartPos + sourceByteLen));
6773
}
6874

6975
/**
@@ -74,7 +80,7 @@ export function initAst(): void {
7480
debugAssertIsNonNull(sourceText);
7581
debugAssertIsNonNull(buffer);
7682

77-
ast = deserializeProgramOnly(buffer, sourceText, sourceByteLen, getNodeLoc);
83+
ast = deserializeProgramOnly(buffer, sourceText, sourceStartPos, sourceByteLen, getNodeLoc);
7884
debugAssertIsNonNull(ast);
7985
}
8086

napi/parser/src-js/generated/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export const DATA_POINTER_POS_32 = 536870902;
77
export const IS_TS_FLAG_POS = 2147483612;
88
export const IS_JSX_FLAG_POS = 2147483613;
99
export const PROGRAM_OFFSET = 0;
10+
export const SOURCE_START_OFFSET = 8;
1011
export const SOURCE_LEN_OFFSET = 16;

napi/parser/src-js/generated/deserialize/js.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
// Auto-generated code, DO NOT EDIT DIRECTLY!
22
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
33

4-
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen;
4+
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceEndPos;
55

66
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
77
decodeStr = textDecoder.decode.bind(textDecoder),
88
{ fromCodePoint } = String;
99

1010
export function deserialize(buffer, sourceText, sourceByteLen) {
11+
sourceEndPos = sourceByteLen;
1112
let data = deserializeWith(buffer, sourceText, sourceByteLen, null, deserializeRawTransferData);
1213
resetBuffer();
1314
return data;
1415
}
1516

16-
function deserializeWith(buffer, sourceTextInput, sourceByteLenInput, getLocInput, deserialize) {
17+
function deserializeWith(buffer, sourceTextInput, sourceByteLen, getLocInput, deserialize) {
1718
uint8 = buffer;
1819
uint32 = buffer.uint32;
1920
float64 = buffer.float64;
2021
sourceText = sourceTextInput;
21-
sourceByteLen = sourceByteLenInput;
2222
sourceIsAscii = sourceText.length === sourceByteLen;
2323
return deserialize(uint32[536870902]);
2424
}
@@ -4523,7 +4523,7 @@ function deserializeStr(pos) {
45234523
len = uint32[pos32 + 2];
45244524
if (len === 0) return "";
45254525
pos = uint32[pos32];
4526-
if (sourceIsAscii && pos < sourceByteLen) return sourceText.substr(pos, len);
4526+
if (sourceIsAscii && pos < sourceEndPos) return sourceText.substr(pos, len);
45274527
// Longer strings use `TextDecoder`
45284528
// TODO: Find best switch-over point
45294529
let end = pos + len;

napi/parser/src-js/generated/deserialize/js_parent.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ let uint8,
66
float64,
77
sourceText,
88
sourceIsAscii,
9-
sourceByteLen,
9+
sourceEndPos,
1010
parent = null;
1111

1212
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
1313
decodeStr = textDecoder.decode.bind(textDecoder),
1414
{ fromCodePoint } = String;
1515

1616
export function deserialize(buffer, sourceText, sourceByteLen) {
17+
sourceEndPos = sourceByteLen;
1718
let data = deserializeWith(buffer, sourceText, sourceByteLen, null, deserializeRawTransferData);
1819
resetBuffer();
1920
return data;
2021
}
2122

22-
function deserializeWith(buffer, sourceTextInput, sourceByteLenInput, getLocInput, deserialize) {
23+
function deserializeWith(buffer, sourceTextInput, sourceByteLen, getLocInput, deserialize) {
2324
uint8 = buffer;
2425
uint32 = buffer.uint32;
2526
float64 = buffer.float64;
2627
sourceText = sourceTextInput;
27-
sourceByteLen = sourceByteLenInput;
2828
sourceIsAscii = sourceText.length === sourceByteLen;
2929
return deserialize(uint32[536870902]);
3030
}
@@ -5059,7 +5059,7 @@ function deserializeStr(pos) {
50595059
len = uint32[pos32 + 2];
50605060
if (len === 0) return "";
50615061
pos = uint32[pos32];
5062-
if (sourceIsAscii && pos < sourceByteLen) return sourceText.substr(pos, len);
5062+
if (sourceIsAscii && pos < sourceEndPos) return sourceText.substr(pos, len);
50635063
// Longer strings use `TextDecoder`
50645064
// TODO: Find best switch-over point
50655065
let end = pos + len;

napi/parser/src-js/generated/deserialize/js_range.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
// Auto-generated code, DO NOT EDIT DIRECTLY!
22
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
33

4-
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen;
4+
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceEndPos;
55

66
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
77
decodeStr = textDecoder.decode.bind(textDecoder),
88
{ fromCodePoint } = String;
99

1010
export function deserialize(buffer, sourceText, sourceByteLen) {
11+
sourceEndPos = sourceByteLen;
1112
let data = deserializeWith(buffer, sourceText, sourceByteLen, null, deserializeRawTransferData);
1213
resetBuffer();
1314
return data;
1415
}
1516

16-
function deserializeWith(buffer, sourceTextInput, sourceByteLenInput, getLocInput, deserialize) {
17+
function deserializeWith(buffer, sourceTextInput, sourceByteLen, getLocInput, deserialize) {
1718
uint8 = buffer;
1819
uint32 = buffer.uint32;
1920
float64 = buffer.float64;
2021
sourceText = sourceTextInput;
21-
sourceByteLen = sourceByteLenInput;
2222
sourceIsAscii = sourceText.length === sourceByteLen;
2323
return deserialize(uint32[536870902]);
2424
}
@@ -5071,7 +5071,7 @@ function deserializeStr(pos) {
50715071
len = uint32[pos32 + 2];
50725072
if (len === 0) return "";
50735073
pos = uint32[pos32];
5074-
if (sourceIsAscii && pos < sourceByteLen) return sourceText.substr(pos, len);
5074+
if (sourceIsAscii && pos < sourceEndPos) return sourceText.substr(pos, len);
50755075
// Longer strings use `TextDecoder`
50765076
// TODO: Find best switch-over point
50775077
let end = pos + len;

napi/parser/src-js/generated/deserialize/js_range_parent.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ let uint8,
66
float64,
77
sourceText,
88
sourceIsAscii,
9-
sourceByteLen,
9+
sourceEndPos,
1010
parent = null;
1111

1212
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
1313
decodeStr = textDecoder.decode.bind(textDecoder),
1414
{ fromCodePoint } = String;
1515

1616
export function deserialize(buffer, sourceText, sourceByteLen) {
17+
sourceEndPos = sourceByteLen;
1718
let data = deserializeWith(buffer, sourceText, sourceByteLen, null, deserializeRawTransferData);
1819
resetBuffer();
1920
return data;
2021
}
2122

22-
function deserializeWith(buffer, sourceTextInput, sourceByteLenInput, getLocInput, deserialize) {
23+
function deserializeWith(buffer, sourceTextInput, sourceByteLen, getLocInput, deserialize) {
2324
uint8 = buffer;
2425
uint32 = buffer.uint32;
2526
float64 = buffer.float64;
2627
sourceText = sourceTextInput;
27-
sourceByteLen = sourceByteLenInput;
2828
sourceIsAscii = sourceText.length === sourceByteLen;
2929
return deserialize(uint32[536870902]);
3030
}
@@ -5610,7 +5610,7 @@ function deserializeStr(pos) {
56105610
len = uint32[pos32 + 2];
56115611
if (len === 0) return "";
56125612
pos = uint32[pos32];
5613-
if (sourceIsAscii && pos < sourceByteLen) return sourceText.substr(pos, len);
5613+
if (sourceIsAscii && pos < sourceEndPos) return sourceText.substr(pos, len);
56145614
// Longer strings use `TextDecoder`
56155615
// TODO: Find best switch-over point
56165616
let end = pos + len;

napi/parser/src-js/generated/deserialize/ts.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
// Auto-generated code, DO NOT EDIT DIRECTLY!
22
// To edit this generated file you have to edit `tasks/ast_tools/src/generators/raw_transfer.rs`.
33

4-
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen;
4+
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceEndPos;
55

66
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
77
decodeStr = textDecoder.decode.bind(textDecoder),
88
{ fromCodePoint } = String;
99

1010
export function deserialize(buffer, sourceText, sourceByteLen) {
11+
sourceEndPos = sourceByteLen;
1112
let data = deserializeWith(buffer, sourceText, sourceByteLen, null, deserializeRawTransferData);
1213
resetBuffer();
1314
return data;
1415
}
1516

16-
function deserializeWith(buffer, sourceTextInput, sourceByteLenInput, getLocInput, deserialize) {
17+
function deserializeWith(buffer, sourceTextInput, sourceByteLen, getLocInput, deserialize) {
1718
uint8 = buffer;
1819
uint32 = buffer.uint32;
1920
float64 = buffer.float64;
2021
sourceText = sourceTextInput;
21-
sourceByteLen = sourceByteLenInput;
2222
sourceIsAscii = sourceText.length === sourceByteLen;
2323
return deserialize(uint32[536870902]);
2424
}
@@ -4830,7 +4830,7 @@ function deserializeStr(pos) {
48304830
len = uint32[pos32 + 2];
48314831
if (len === 0) return "";
48324832
pos = uint32[pos32];
4833-
if (sourceIsAscii && pos < sourceByteLen) return sourceText.substr(pos, len);
4833+
if (sourceIsAscii && pos < sourceEndPos) return sourceText.substr(pos, len);
48344834
// Longer strings use `TextDecoder`
48354835
// TODO: Find best switch-over point
48364836
let end = pos + len;

0 commit comments

Comments
 (0)