Skip to content

Commit 0392487

Browse files
authored
API: make object registries project-specific to avoid id collisions on types and signatures (#4341)
1 parent f6e1c6d commit 0392487

7 files changed

Lines changed: 783 additions & 320 deletions

File tree

_packages/native-preview/src/api/async/api.ts

Lines changed: 142 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ import {
3131
readSourceFileHash,
3232
RemoteSourceFile,
3333
} from "../node/node.ts";
34-
import {
35-
type ObjectFactories,
36-
ObjectRegistry,
37-
} from "../objectRegistry.ts";
3834
import type {
3935
APIOptions,
4036
LSPConnectionOptions,
@@ -236,9 +232,9 @@ export class Snapshot {
236232
private projectMap: Map<Path, Project>;
237233
private toPath: (fileName: string) => Path;
238234
private client: Client;
239-
private objectRegistry: SnapshotObjectRegistry;
240235
private disposed: boolean = false;
241236
private onDispose: () => void;
237+
private snapshotRegistry: SnapshotObjectRegistry;
242238

243239
constructor(
244240
data: UpdateSnapshotResponse,
@@ -251,21 +247,11 @@ export class Snapshot {
251247
this.client = client;
252248
this.toPath = toPath;
253249
this.onDispose = onDispose;
250+
this.snapshotRegistry = new SnapshotObjectRegistry(client, this.id);
254251

255-
this.objectRegistry = new SnapshotObjectRegistry(
256-
{
257-
createSymbol: symbolData => new Symbol(symbolData, this.objectRegistry),
258-
createType: typeData => new TypeObject(typeData, this.objectRegistry),
259-
createSignature: sigData => new Signature(sigData, this.objectRegistry),
260-
},
261-
client,
262-
this.id,
263-
);
264-
265-
// Create projects
266252
this.projectMap = new Map();
267253
for (const projData of data.projects) {
268-
const project = new Project(projData, this.id, client, this.objectRegistry, sourceFileCache, toPath);
254+
const project = new Project(projData, this.id, client, sourceFileCache, toPath, this.snapshotRegistry);
269255
this.projectMap.set(toPath(projData.configFileName), project);
270256
}
271257
}
@@ -297,7 +283,11 @@ export class Snapshot {
297283
async dispose(): Promise<void> {
298284
if (this.disposed) return;
299285
this.disposed = true;
300-
this.objectRegistry.clear();
286+
for (const project of this.projectMap.values()) {
287+
project.dispose();
288+
}
289+
this.projectMap.clear();
290+
this.snapshotRegistry.clear();
301291
this.onDispose();
302292
await this.client.apiRequest("release", { snapshot: this.id });
303293
}
@@ -313,18 +303,128 @@ export class Snapshot {
313303
}
314304
}
315305

316-
class SnapshotObjectRegistry extends ObjectRegistry<Symbol, TypeObject, Signature> {
306+
class SnapshotObjectRegistry {
307+
private readonly symbols: Map<number, Symbol> = new Map();
308+
private readonly client: Client;
309+
private readonly snapshotId: number;
310+
311+
constructor(client: Client, snapshotId: number) {
312+
this.client = client;
313+
this.snapshotId = snapshotId;
314+
}
315+
316+
getOrCreateSymbol(data: SymbolResponse): Symbol {
317+
let symbol = this.symbols.get(data.id);
318+
if (!symbol) {
319+
symbol = new Symbol(data, this);
320+
this.symbols.set(data.id, symbol);
321+
}
322+
return symbol;
323+
}
324+
325+
getSymbol(id: number): Symbol | undefined {
326+
return this.symbols.get(id);
327+
}
328+
329+
clear(): void {
330+
this.symbols.clear();
331+
}
332+
333+
async fetchSymbol(source: Symbol | Signature | Type, method: string, handle: number | undefined, projectId?: string): Promise<Symbol> {
334+
if (!handle) return undefined as unknown as Symbol;
335+
const cached = this.getSymbol(handle);
336+
if (cached) return cached;
337+
338+
const data = await this.client.apiRequest<SymbolResponse | null>(method, {
339+
snapshot: this.snapshotId,
340+
project: projectId,
341+
objectId: source.id,
342+
});
343+
if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`);
344+
return this.getOrCreateSymbol(data);
345+
}
346+
347+
async fetchSymbols(source: Symbol | Signature | Type, method: string, handles?: readonly number[], projectId?: string): Promise<readonly Symbol[]> {
348+
if (handles) {
349+
const result = new Array<Symbol>(handles.length);
350+
let allCached = true;
351+
for (let i = 0; i < handles.length; i++) {
352+
const cached = this.getSymbol(handles[i]);
353+
if (!cached) {
354+
allCached = false;
355+
break;
356+
}
357+
result[i] = cached;
358+
}
359+
if (allCached) return result;
360+
}
361+
const symbolData = await this.client.apiRequest<SymbolResponse[] | null>(method, {
362+
snapshot: this.snapshotId,
363+
project: projectId,
364+
objectId: source.id,
365+
});
366+
if (symbolData == null) return [];
367+
else return symbolData.map(data => this.getOrCreateSymbol(data));
368+
}
369+
}
370+
371+
class ProjectObjectRegistry {
317372
private client: Client;
318373
private snapshotId: number;
374+
private projectId: string;
375+
private snapshotRegistry: SnapshotObjectRegistry;
376+
private types: Map<number, TypeObject> = new Map();
377+
private signatures: Map<number, Signature> = new Map();
319378

320379
constructor(
321-
factories: ObjectFactories<Symbol, TypeObject, Signature>,
322380
client: Client,
323381
snapshotId: number,
382+
projectId: string,
383+
snapshotRegistry: SnapshotObjectRegistry,
324384
) {
325-
super(factories);
326385
this.client = client;
327386
this.snapshotId = snapshotId;
387+
this.projectId = projectId;
388+
this.snapshotRegistry = snapshotRegistry;
389+
}
390+
391+
getOrCreateSymbol(data: SymbolResponse): Symbol {
392+
return this.snapshotRegistry.getOrCreateSymbol(data);
393+
}
394+
395+
getSymbol(id: number): Symbol | undefined {
396+
return this.snapshotRegistry.getSymbol(id);
397+
}
398+
399+
getOrCreateType(data: TypeResponse): TypeObject {
400+
let type = this.types.get(data.id);
401+
if (!type) {
402+
type = new TypeObject(data, this);
403+
this.types.set(data.id, type);
404+
}
405+
return type;
406+
}
407+
408+
getType(id: number): TypeObject | undefined {
409+
return this.types.get(id);
410+
}
411+
412+
getOrCreateSignature(data: SignatureResponse): Signature {
413+
let sig = this.signatures.get(data.id);
414+
if (!sig) {
415+
sig = new Signature(data, this);
416+
this.signatures.set(data.id, sig);
417+
}
418+
return sig;
419+
}
420+
421+
getSignature(id: number): Signature | undefined {
422+
return this.signatures.get(id);
423+
}
424+
425+
clear(): void {
426+
this.types.clear();
427+
this.signatures.clear();
328428
}
329429

330430
async fetchType<T extends Type>(source: Symbol | Signature | Type, method: string, handle: number | undefined): Promise<T> {
@@ -334,23 +434,15 @@ class SnapshotObjectRegistry extends ObjectRegistry<Symbol, TypeObject, Signatur
334434

335435
const data = await this.client.apiRequest<TypeResponse | null>(method, {
336436
snapshot: this.snapshotId,
437+
project: this.projectId,
337438
objectId: source.id,
338439
});
339440
if (!data) throw new Error(`${method} returned null type for ${source.constructor.name} ${source.id}`);
340441
return this.getOrCreateType(data) as unknown as T;
341442
}
342443

343444
async fetchSymbol(source: Symbol | Signature | Type, method: string, handle: number | undefined): Promise<Symbol> {
344-
if (!handle) return undefined as unknown as Symbol;
345-
const cached = this.getSymbol(handle);
346-
if (cached) return cached;
347-
348-
const data = await this.client.apiRequest<SymbolResponse | null>(method, {
349-
snapshot: this.snapshotId,
350-
objectId: source.id,
351-
});
352-
if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`);
353-
return this.getOrCreateSymbol(data);
445+
return this.snapshotRegistry.fetchSymbol(source, method, handle, this.projectId);
354446
}
355447

356448
async fetchSignature(source: Symbol | Signature | Type, method: string, handle: number | undefined): Promise<Signature> {
@@ -360,6 +452,7 @@ class SnapshotObjectRegistry extends ObjectRegistry<Symbol, TypeObject, Signatur
360452

361453
const data = await this.client.apiRequest<SignatureResponse | null>(method, {
362454
snapshot: this.snapshotId,
455+
project: this.projectId,
363456
objectId: source.id,
364457
});
365458
if (!data) throw new Error(`${method} returned null signature for ${source.constructor.name} ${source.id}`);
@@ -382,32 +475,15 @@ class SnapshotObjectRegistry extends ObjectRegistry<Symbol, TypeObject, Signatur
382475
}
383476
const typesData = await this.client.apiRequest<TypeResponse[] | null>(method, {
384477
snapshot: this.snapshotId,
478+
project: this.projectId,
385479
objectId: source.id,
386480
});
387481
if (typesData == null) return [];
388482
else return typesData.map(data => this.getOrCreateType(data));
389483
}
390484

391485
async fetchSymbols(source: Symbol | Signature | Type, method: string, handles?: readonly number[]): Promise<readonly Symbol[]> {
392-
if (handles) {
393-
const result = new Array<Symbol>(handles.length);
394-
let allCached = true;
395-
for (let i = 0; i < handles.length; i++) {
396-
const cached = this.getSymbol(handles[i]);
397-
if (!cached) {
398-
allCached = false;
399-
break;
400-
}
401-
result[i] = cached;
402-
}
403-
if (allCached) return result;
404-
}
405-
const symbolData = await this.client.apiRequest<SymbolResponse[] | null>(method, {
406-
snapshot: this.snapshotId,
407-
objectId: source.id,
408-
});
409-
if (symbolData == null) return [];
410-
else return symbolData.map(data => this.getOrCreateSymbol(data));
486+
return this.snapshotRegistry.fetchSymbols(source, method, handles, this.projectId);
411487
}
412488
}
413489

@@ -426,9 +502,9 @@ export class Project {
426502
data: ProjectResponse,
427503
snapshotId: number,
428504
client: Client,
429-
objectRegistry: SnapshotObjectRegistry,
430505
sourceFileCache: SourceFileCache,
431506
toPath: (fileName: string) => Path,
507+
snapshotRegistry: SnapshotObjectRegistry,
432508
) {
433509
this.id = data.id;
434510
this.configFileName = data.configFileName;
@@ -442,6 +518,7 @@ export class Project {
442518
sourceFileCache,
443519
toPath,
444520
);
521+
const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this.id, snapshotRegistry);
445522
this.checker = new Checker(
446523
snapshotId,
447524
this.id,
@@ -450,6 +527,10 @@ export class Project {
450527
);
451528
this.emitter = new Emitter(client);
452529
}
530+
531+
dispose(): void {
532+
this.checker.dispose();
533+
}
453534
}
454535

455536
export class Program {
@@ -571,20 +652,24 @@ export class Checker {
571652
private snapshotId: number;
572653
private projectId: string;
573654
private client: Client;
574-
private objectRegistry: SnapshotObjectRegistry;
655+
private objectRegistry: ProjectObjectRegistry;
575656

576657
constructor(
577658
snapshotId: number,
578659
projectId: string,
579660
client: Client,
580-
objectRegistry: SnapshotObjectRegistry,
661+
objectRegistry: ProjectObjectRegistry,
581662
) {
582663
this.snapshotId = snapshotId;
583664
this.projectId = projectId;
584665
this.client = client;
585666
this.objectRegistry = objectRegistry;
586667
}
587668

669+
dispose(): void {
670+
this.objectRegistry.clear();
671+
}
672+
588673
getSymbolAtLocation(node: Node): Promise<Symbol | undefined>;
589674
getSymbolAtLocation(nodes: readonly Node[]): Promise<(Symbol | undefined)[]>;
590675
async getSymbolAtLocation(nodeOrNodes: Node | readonly Node[]): Promise<Symbol | (Symbol | undefined)[] | undefined> {
@@ -1166,7 +1251,7 @@ export class Symbol {
11661251
}
11671252

11681253
class TypeObject implements Type {
1169-
private objectRegistry: SnapshotObjectRegistry;
1254+
private objectRegistry: ProjectObjectRegistry;
11701255

11711256
readonly id: number;
11721257
readonly flags: TypeFlags;
@@ -1194,7 +1279,7 @@ class TypeObject implements Type {
11941279
readonly baseType!: number;
11951280
readonly substConstraint!: number;
11961281

1197-
constructor(data: TypeResponse, objectRegistry: SnapshotObjectRegistry) {
1282+
constructor(data: TypeResponse, objectRegistry: ProjectObjectRegistry) {
11981283
this.objectRegistry = objectRegistry;
11991284

12001285
this.id = data.id;
@@ -1295,7 +1380,7 @@ class TypeObject implements Type {
12951380

12961381
export class Signature {
12971382
private flags: number;
1298-
private objectRegistry: SnapshotObjectRegistry;
1383+
private objectRegistry: ProjectObjectRegistry;
12991384

13001385
readonly id: number;
13011386
readonly declaration?: NodeHandle | undefined;
@@ -1304,7 +1389,7 @@ export class Signature {
13041389
readonly thisParameter?: number | undefined;
13051390
readonly target?: number | undefined;
13061391

1307-
constructor(data: SignatureResponse, objectRegistry: SnapshotObjectRegistry) {
1392+
constructor(data: SignatureResponse, objectRegistry: ProjectObjectRegistry) {
13081393
this.id = data.id;
13091394
this.flags = data.flags;
13101395
this.objectRegistry = objectRegistry;

0 commit comments

Comments
 (0)