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
5 changes: 4 additions & 1 deletion src/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,8 @@ export interface GeneratedRangeStartItem {
flags: number;
line?: number;
column: number;
definitionIdx?: number;
definition?: {
sourceIdx: number;
scopeIdx: number;
};
}
104 changes: 98 additions & 6 deletions src/decode/decode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,14 +410,78 @@ describe("decode", () => {
assertEquals(info.ranges[0]?.values, [""]);
});

it("handles trailing VLQs in GENERATED_RANGE_START items", () => {
const info = new ScopeInfoBuilder().startRange(0, 0).endRange(10, 0)
.build();
const map = encode(info);

assertExists(map.scopes);

const parts = map.scopes.split(",");
parts[0] += encodeUnsigned(42);
parts[0] += encodeSigned(-16);
map.scopes = parts.join(",");

assertEquals(decode(map), { ...info, hasVariableAndBindingInfo: false });
});

it("handles trailing VLQs in GENERATED_RANGE_CALL_SITE items", () => {
const info = new ScopeInfoBuilder().startSource().startScope(0, 0, {
key: "fn",
}).endScope(10, 0).endSource().startRange(0, 0, {
scopeKey: "fn",
callSite: { sourceIndex: 0, line: 2, column: 4 },
}).endRange(10, 0).build();
const map = encode(info);

assertExists(map.scopes);

const parts = map.scopes.split(",");
parts[3] += encodeUnsigned(42);
parts[3] += encodeSigned(-16);
map.scopes = parts.join(",");

assertEquals(decode(map), { ...info, hasVariableAndBindingInfo: false });
});

it("throws if GENERATED_RANGE_START.definition has an invalid source index in strict mode", () => {
const encoder = new ItemEncoder();
encoder.addUnsignedVLQs(
Tag.GENERATED_RANGE_START,
GeneratedRangeFlags.HAS_DEFINITION,
0,
).addSignedVLQs(1).addUnsignedVLQs(0).finishItem();
encoder.addUnsignedVLQs(Tag.GENERATED_RANGE_END, 2).finishItem();
const map = createMap(encoder.encode(), []);

assertThrows(() => decode(map, { mode: DecodeMode.STRICT }));
});

it("ignores if GENERATED_RANGE_START.definition has an invalid source index in lax mode", () => {
const encoder = new ItemEncoder();
encoder.addUnsignedVLQs(
Tag.GENERATED_RANGE_START,
GeneratedRangeFlags.HAS_DEFINITION,
0,
).addSignedVLQs(1).addUnsignedVLQs(0).finishItem();
encoder.addUnsignedVLQs(Tag.GENERATED_RANGE_END, 2).finishItem();
const map = createMap(encoder.encode(), []);

const info = decode(map, { mode: DecodeMode.LAX });

assertExists(info.ranges[0]);
assertStrictEquals(info.ranges[0].originalScope, undefined);
});

it("throws if GENERATED_RANGE_START.definition is not a valid original scope in strict mode", () => {
const encoder = new ItemEncoder();
encoder.addUnsignedVLQs(Tag.ORIGINAL_SCOPE_START, 0, 0, 0).finishItem();
encoder.addUnsignedVLQs(Tag.ORIGINAL_SCOPE_END, 1, 0).finishItem();
encoder.addUnsignedVLQs(
Tag.GENERATED_RANGE_START,
GeneratedRangeFlags.HAS_DEFINITION,
0,
1,
).finishItem();
).addSignedVLQs(0, 1).finishItem();
encoder.addUnsignedVLQs(Tag.GENERATED_RANGE_END, 2).finishItem();
const map = createMap(encoder.encode(), []);

Expand All @@ -426,12 +490,13 @@ describe("decode", () => {

it("ignores if GENERATED_RANGE_START.definition is not a valid original scope in lax mode", () => {
const encoder = new ItemEncoder();
encoder.addUnsignedVLQs(Tag.ORIGINAL_SCOPE_START, 0, 0, 0).finishItem();
encoder.addUnsignedVLQs(Tag.ORIGINAL_SCOPE_END, 1, 0).finishItem();
encoder.addUnsignedVLQs(
Tag.GENERATED_RANGE_START,
GeneratedRangeFlags.HAS_DEFINITION,
0,
1,
).finishItem();
).addSignedVLQs(0, 1).finishItem();
encoder.addUnsignedVLQs(Tag.GENERATED_RANGE_END, 2).finishItem();
const map = createMap(encoder.encode(), []);

Expand All @@ -441,6 +506,34 @@ describe("decode", () => {
assertStrictEquals(info.ranges[0].originalScope, undefined);
});

it("decodes cross-source and same-source GENERATED_RANGE_START.definition indices", () => {
const scopes = new ScopeInfoBuilder()
.startSource()
.startScope(0, 0, { key: "s0_root" })
.startScope(1, 0, { key: "s0_child" })
.endScope(2, 0)
.endScope(3, 0)
.endSource()
.startSource()
.startScope(0, 0, { key: "s1_root" })
.startScope(1, 0, { key: "s1_child" })
.endScope(2, 0)
.endScope(3, 0)
.endSource()
.startRange(0, 0, { scopeKey: "s1_child" })
.startRange(0, 2, { scopeKey: "s1_root" })
.endRange(0, 4)
.startRange(0, 5, { scopeKey: "s0_child" })
.endRange(0, 8)
.endRange(0, 10)
.build();

const map = encode(scopes);
const decoded = decode(map, { mode: DecodeMode.STRICT });

assertEquals(decoded, { ...scopes, hasVariableAndBindingInfo: false });
});

it("throws for free GENERATED_RANGE_CALL_SITE items in strict mode", () => {
const encoder = new ItemEncoder();
encoder.addUnsignedVLQs(Tag.GENERATED_RANGE_CALL_SITE);
Expand Down Expand Up @@ -492,8 +585,7 @@ describe("decode", () => {
Tag.GENERATED_RANGE_START,
GeneratedRangeFlags.HAS_DEFINITION,
0,
0,
).addSignedVLQs(0).finishItem();
).addSignedVLQs(0, 0).finishItem();
// Initial binding for the variable is "bar" (index 2).
encoder.addUnsignedVLQs(Tag.GENERATED_RANGE_BINDINGS, 2).finishItem();

Expand Down
43 changes: 29 additions & 14 deletions src/decode/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ const DEFAULT_SCOPE_STATE = {
const DEFAULT_RANGE_STATE = {
line: 0,
column: 0,
defSourceIdx: 0,
defScopeIdx: 0,
};

Expand All @@ -133,7 +134,8 @@ class Decoder {
readonly #scopeStack: OriginalScope[] = [];
readonly #rangeStack: GeneratedRange[] = [];

#flatOriginalScopes: OriginalScope[] = [];
#flatOriginalScopes: (OriginalScope[] | null)[] = [];
#currentFlatScopes: OriginalScope[] = [];
#subRangeBindingsForRange = new Map<
GeneratedRange,
Map<number, [number, number, number][]>
Expand Down Expand Up @@ -161,6 +163,7 @@ class Decoder {
switch (tag) {
case Tag.EMPTY: {
this.#scopes.push(null);
this.#flatOriginalScopes.push(null);
break;
}
case Tag.ORIGINAL_SCOPE_START: {
Expand Down Expand Up @@ -205,15 +208,20 @@ class Decoder {
: undefined;
const column = iter.nextUnsignedVLQ();

const definitionIdx = flags & GeneratedRangeFlags.HAS_DEFINITION
? iter.nextSignedVLQ()
: undefined;
let definition: { sourceIdx: number; scopeIdx: number } | undefined;
if (flags & GeneratedRangeFlags.HAS_DEFINITION) {
const sourceIdx = iter.nextSignedVLQ();
const scopeIdx = sourceIdx === 0
? iter.nextSignedVLQ()
: iter.nextUnsignedVLQ();
definition = { sourceIdx, scopeIdx };
}

this.#handleGeneratedRangeStartItem({
flags,
line,
column,
definitionIdx,
definition,
});
break;
}
Expand Down Expand Up @@ -301,6 +309,7 @@ class Decoder {
this.#scopes = [];
this.#ranges = [];
this.#flatOriginalScopes = [];
this.#currentFlatScopes = [];
this.#seenOriginalScopeVariables = false;
this.#seenGeneratedRangeBindings = false;

Expand Down Expand Up @@ -340,7 +349,7 @@ class Decoder {
);

this.#scopeStack.push(scope);
this.#flatOriginalScopes.push(scope);
this.#currentFlatScopes.push(scope);
}

#handleOriginalScopeVariablesItem(variableIdxs: number[]) {
Expand Down Expand Up @@ -385,6 +394,8 @@ class Decoder {
parent.children.push(scope);
} else {
this.#scopes.push([scope]);
this.#flatOriginalScopes.push(this.#currentFlatScopes);
this.#currentFlatScopes = [];
this.#scopeState.line = 0;
this.#scopeState.column = 0;
}
Expand Down Expand Up @@ -415,16 +426,20 @@ class Decoder {
children: [],
};

if (item.definitionIdx !== undefined) {
this.#rangeState.defScopeIdx += item.definitionIdx;
if (
this.#rangeState.defScopeIdx < 0 ||
this.#rangeState.defScopeIdx >= this.#flatOriginalScopes.length
) {
if (item.definition !== undefined) {
this.#rangeState.defSourceIdx += item.definition.sourceIdx;
if (item.definition.sourceIdx !== 0) {
this.#rangeState.defScopeIdx = 0;
}
this.#rangeState.defScopeIdx += item.definition.scopeIdx;

const originalScope = this
.#flatOriginalScopes[this.#rangeState.defSourceIdx]
?.[this.#rangeState.defScopeIdx];
if (!originalScope) {
this.#throwInStrictMode("Invalid definition scope index");
} else {
range.originalScope =
this.#flatOriginalScopes[this.#rangeState.defScopeIdx];
range.originalScope = originalScope;
}
}

Expand Down
43 changes: 34 additions & 9 deletions src/encode/encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const DEFAULT_SCOPE_STATE = {
const DEFAULT_RANGE_STATE = {
line: 0,
column: 0,
defSourceIdx: 0,
defScopeIdx: 0,
};

Expand All @@ -38,7 +39,11 @@ export class Encoder {
#encodedItems: string[] = [];
#currentItem: string = "";

#scopeToCount = new Map<OriginalScope, number>();
#scopeToLocation = new Map<
OriginalScope,
{ sourceIdx: number; scopeIdx: number }
>();
#currentSourceIdx = 0;
#scopeCounter = 0;

constructor(info: ScopeInfo, names: string[]) {
Expand All @@ -52,11 +57,13 @@ export class Encoder {

encode(): string {
this.#encodedItems = [];
this.#info.scopes.forEach((scopes) => {
this.#info.scopes.forEach((scopes, sourceIdx) => {
if (scopes === null) {
this.#encodedItems.push(EncodedTag.EMPTY);
return;
}
this.#currentSourceIdx = sourceIdx;
this.#scopeCounter = 0;
this.#scopeState.line = 0;
this.#scopeState.column = 0;
scopes.forEach((scope) => this.#encodeOriginalScope(scope));
Expand Down Expand Up @@ -111,7 +118,10 @@ export class Encoder {
if (encodedKind !== undefined) this.#encodeSigned(encodedKind);
this.#finishItem();

this.#scopeToCount.set(scope, this.#scopeCounter++);
this.#scopeToLocation.set(scope, {
sourceIdx: this.#currentSourceIdx,
scopeIdx: this.#scopeCounter++,
});
}

#encodeOriginalScopeVariables(scope: OriginalScope) {
Expand Down Expand Up @@ -168,17 +178,23 @@ export class Encoder {
this.#rangeState.line = line;
this.#rangeState.column = column;

let encodedDefinition;
let encodedDefSourceIdx: number | undefined;
let encodedDefScopeIdx: number | undefined;
if (range.originalScope) {
const definitionIdx = this.#scopeToCount.get(range.originalScope);
if (definitionIdx === undefined) {
const location = this.#scopeToLocation.get(range.originalScope);
if (location === undefined) {
throw new Error("Unknown OriginalScope for definition!");
}

flags |= GeneratedRangeFlags.HAS_DEFINITION;

encodedDefinition = definitionIdx - this.#rangeState.defScopeIdx;
this.#rangeState.defScopeIdx = definitionIdx;
encodedDefSourceIdx = location.sourceIdx - this.#rangeState.defSourceIdx;
this.#rangeState.defSourceIdx = location.sourceIdx;

encodedDefScopeIdx = encodedDefSourceIdx === 0
? location.scopeIdx - this.#rangeState.defScopeIdx
: location.scopeIdx;
this.#rangeState.defScopeIdx = location.scopeIdx;
}

if (range.isStackFrame) flags |= GeneratedRangeFlags.IS_STACK_FRAME;
Expand All @@ -187,7 +203,16 @@ export class Encoder {
this.#encodeTag(EncodedTag.GENERATED_RANGE_START).#encodeUnsigned(flags);
if (encodedLine > 0) this.#encodeUnsigned(encodedLine);
this.#encodeUnsigned(encodedColumn);
if (encodedDefinition !== undefined) this.#encodeSigned(encodedDefinition);
if (
encodedDefSourceIdx !== undefined && encodedDefScopeIdx !== undefined
) {
this.#encodeSigned(encodedDefSourceIdx);
if (encodedDefSourceIdx === 0) {
this.#encodeSigned(encodedDefScopeIdx);
} else {
this.#encodeUnsigned(encodedDefScopeIdx);
}
}
this.#finishItem();
}

Expand Down
Loading