Skip to content

Commit

Permalink
progress circular ref
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheeguerin committed Oct 10, 2023
1 parent c9c6f0e commit ce6bc00
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 46 deletions.
53 changes: 15 additions & 38 deletions packages/compiler/src/emitter-framework/asset-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "../core/index.js";
import { CustomKeyMap } from "./custom-key-map.js";
import { Placeholder } from "./placeholder.js";
import { resolveDeclarationReferenceScope } from "./ref-scope.js";
import { TypeEmitter } from "./type-emitter.js";
import {
AssetEmitter,
Expand Down Expand Up @@ -224,50 +225,36 @@ export function createAssetEmitter<T, TOptions extends object>(
lexicalTypeStack,
context,
},
cb: (entity) => invokeReference(this, entity),
cb: (entity) => invokeReference(this, entity, true),
});

placeholder = new Placeholder();
return this.result.rawCode(placeholder);
} else {
return invokeReference(this, entity);
return invokeReference(this, entity, false);
}

function invokeReference(
assetEmitter: AssetEmitter<T, TOptions>,
entity: EmitEntity<T>
entity: EmitEntity<T>,
circular: boolean
): EmitEntity<T> {
if (entity.kind !== "declaration") {
typeEmitter.circularReference(entity);
return entity;
}

let ref;
const scope = currentScope();
compilerAssert(
scope,
"Emit context must have a scope set in order to create references to declarations."
);
const targetScope = entity.scope;
const targetChain = scopeChain(targetScope);
const currentChain = scopeChain(scope);
let diffStart = 0;
while (
targetChain[diffStart] &&
currentChain[diffStart] &&
targetChain[diffStart] === currentChain[diffStart]
) {
diffStart++;
}

const pathUp: Scope<T>[] = currentChain.slice(diffStart);
const pathDown: Scope<T>[] = targetChain.slice(diffStart);
if (circular) {
ref = typeEmitter.circularReference(entity, scope);
} else {
if (entity.kind !== "declaration") {
return entity;
}

let ref = typeEmitter.reference(
entity,
pathUp,
pathDown,
targetChain[diffStart - 1] ?? null
);
const { pathUp, pathDown, commonScope } = resolveDeclarationReferenceScope(entity, scope);
ref = typeEmitter.reference(entity, pathUp, pathDown, commonScope);
}

if (!(ref instanceof EmitterResult)) {
ref = assetEmitter.result.rawCode(ref) as RawCode<T>;
Expand Down Expand Up @@ -299,16 +286,6 @@ export function createAssetEmitter<T, TOptions extends object>(

return ref;
}

function scopeChain(scope: Scope<T> | null) {
const chain = [];
while (scope) {
chain.unshift(scope);
scope = scope.parentScope;
}

return chain;
}
},

emitDeclarationName(type): string | undefined {
Expand Down
40 changes: 40 additions & 0 deletions packages/compiler/src/emitter-framework/ref-scope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Declaration, Scope } from "./types.js";

export function scopeChain<T>(scope: Scope<T> | null) {
const chain = [];
while (scope) {
chain.unshift(scope);
scope = scope.parentScope;
}

return chain;
}

/**
* Resolve relative scopes between the current scope and the target declaration.
* @param target The target declaration
* @param currentScope Current scope
* @returns
*/
export function resolveDeclarationReferenceScope<T>(
target: Declaration<T>,
currentScope: Scope<T>
) {
const targetScope = target.scope;
const targetChain = scopeChain(targetScope);
const currentChain = scopeChain(currentScope);
let diffStart = 0;
while (
targetChain[diffStart] &&
currentChain[diffStart] &&
targetChain[diffStart] === currentChain[diffStart]
) {
diffStart++;
}

const pathUp: Scope<T>[] = currentChain.slice(diffStart);
const pathDown: Scope<T>[] = targetChain.slice(diffStart);

const commonScope = targetChain[diffStart - 1] ?? null;
return { pathUp, pathDown, commonScope };
}
12 changes: 7 additions & 5 deletions packages/compiler/src/emitter-framework/type-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from "../core/index.js";
import { code, StringBuilder } from "./builders/string-builder.js";
import { Placeholder } from "./placeholder.js";
import { resolveDeclarationReferenceScope } from "./ref-scope.js";
import {
AssetEmitter,
Context,
Expand Down Expand Up @@ -702,11 +703,12 @@ export class TypeEmitter<T, TOptions extends object = Record<string, never>> {
return this.emitter.result.none();
}

circularReference(target: EmitEntity<T>): EmitEntity<T> | T {
// if (target.kind === "declaration") {
// return this.reference(target, pathUp, pathDown, commonScope);
// }
return this.emitter.result.none();
circularReference(target: EmitEntity<T>, scope: Scope<T>): EmitEntity<T> | T {
if (target.kind !== "declaration") {
return target; // TODO ? throw maybe ?
}
const { pathUp, pathDown, commonScope } = resolveDeclarationReferenceScope(target, scope);
return this.reference(target, pathUp, pathDown, commonScope);
}

declarationName(declarationType: TypeSpecDeclaration): string | undefined {
Expand Down
9 changes: 6 additions & 3 deletions packages/compiler/test/emitter-framework/emitter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -778,8 +778,11 @@ describe("emitter-framework: object emitter", () => {
return { scope: sourceFile.globalScope };
}

circularReference(target: EmitEntity<any>) {
invalidReferences.push({ entity: target });
circularReference(target: EmitEntity<any>, scope: Scope<any>) {
if (target.kind !== "declaration") {
invalidReferences.push({ entity: target });
}
return super.circularReference(target, scope);
}
};

Expand Down Expand Up @@ -817,7 +820,7 @@ describe("emitter-framework: object emitter", () => {
it("without circular reference inline types cause no issue", async () => {
const invalidReferences = await findCircularReferences({
noDeclaration: true,
circleReference: true,
circleReference: false,
});
strictEqual(invalidReferences.length, 0);
});
Expand Down

0 comments on commit ce6bc00

Please sign in to comment.