mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-08 08:44:37 +08:00
Lower each closure argument of an implicit-any call once
To key an implicit-any function's instance, storedImplicitArgumentType lowers a JavaScript arrow or function-expression argument to read its function type, then discards the value; the call's argument list then lowers the closure again. Every such closure was lowered twice, and a closure nested in another one four times, and each lowering lifted its own copy of the body. The first lowering now keeps its value (Lowerer.speculatedClosures), and the argument's own lowering in the same function and with the same type bindings takes it instead of lowering the closure again. A lowering that reported diagnostics is not kept.
This commit is contained in:
@@ -2082,6 +2082,10 @@ function storedImplicitArgumentType(lowerer: Lowerer, arg: ts.Expression): IrTyp
|
||||
isJsSourceFile(ts.getSourceFileOfNode(arg)) &&
|
||||
(ts.isArrowFunction(arg) || ts.isFunctionExpression(arg))
|
||||
) {
|
||||
// A closure lowered here before (another call site's instance key, or
|
||||
// a repeated demand) keeps its value for the argument's own lowering.
|
||||
const known = lowerer.speculatedClosure(arg);
|
||||
if (known !== null) return known.type;
|
||||
const saved = lowerer.diagSink;
|
||||
const diagnostics: typeof lowerer.diags = [];
|
||||
lowerer.diagSink = diagnostics;
|
||||
@@ -2092,8 +2096,17 @@ function storedImplicitArgumentType(lowerer: Lowerer, arg: ts.Expression): IrTyp
|
||||
lowerer.diagSink = saved;
|
||||
}
|
||||
// Direct inferred helpers can receive native callbacks even when their
|
||||
// result is an opaque handle that cannot cross checked storage.
|
||||
if (diagnostics.length === 0 && value?.type.kind === "func") return value.type;
|
||||
// result is an opaque handle that cannot cross checked storage. The
|
||||
// argument's own lowering then takes this value instead of lowering
|
||||
// the closure a second time.
|
||||
if (diagnostics.length === 0 && value?.type.kind === "func") {
|
||||
lowerer.speculatedClosures.set(arg, {
|
||||
ctx: lowerer.ctx,
|
||||
bindings: lowerer.typeParamBindings,
|
||||
value,
|
||||
});
|
||||
return value.type;
|
||||
}
|
||||
}
|
||||
if (arg.kind === ts.SyntaxKind.ThisKeyword) return lowerer.resolveThis()?.type ?? null;
|
||||
if (ts.isAwaitExpression(arg)) {
|
||||
|
||||
@@ -3174,6 +3174,14 @@ export class Lowerer {
|
||||
* (builtins/fs-paths.ts): each lowers to the hidden local that holds its
|
||||
* value, which is null until the path's lowering has saved it. */
|
||||
readonly stagedArguments = new Map<ts.Expression, { localId: string; type: IrType } | null>();
|
||||
/** Closure arguments an implicit-any call already lowered to learn their
|
||||
* type (storedImplicitArgumentType), by node: the argument's own lowering
|
||||
* in the same function and with the same type bindings takes that value
|
||||
* once instead of lowering the closure again. */
|
||||
readonly speculatedClosures = new Map<
|
||||
ts.Expression,
|
||||
{ ctx: FnCtx; bindings: Map<ts.Symbol, IrType> | null; value: IrExpr }
|
||||
>();
|
||||
/** What each fs path argument of a URL-path call lowers to (the plans
|
||||
* of builtins/fs-paths.ts). */
|
||||
readonly fsPathPlans = new Map<ts.Expression, FsPathPlan>();
|
||||
@@ -12938,12 +12946,30 @@ export class Lowerer {
|
||||
/* ── expressions ──────────────────────────────────────────────────── */
|
||||
|
||||
lowerExpr(expr: ts.Expression): IrExpr {
|
||||
if (this.speculatedClosures.size > 0) {
|
||||
const speculated = this.speculatedClosure(expr);
|
||||
if (speculated !== null) {
|
||||
this.speculatedClosures.delete(expr);
|
||||
return speculated;
|
||||
}
|
||||
}
|
||||
const streamValue = this.valueStreamReceivers.get(expr);
|
||||
if (streamValue)
|
||||
return valueStreamOf(varRef(streamValue.localId, streamValue.type, locOf(expr)));
|
||||
return this.stagedArgument(expr) ?? lowerExpr(this, expr);
|
||||
}
|
||||
|
||||
/** The value storedImplicitArgumentType lowered for `expr` in the active
|
||||
* function and type bindings (speculatedClosures), or null. */
|
||||
speculatedClosure(expr: ts.Expression): IrExpr | null {
|
||||
const entry = this.speculatedClosures.get(expr);
|
||||
return entry !== undefined &&
|
||||
entry.ctx === this.ctx &&
|
||||
entry.bindings === this.typeParamBindings
|
||||
? entry.value
|
||||
: null;
|
||||
}
|
||||
|
||||
/** The saved value of a staged fs argument (stagedArguments, keyed
|
||||
* without type-only wrappers), or null for every other expression. */
|
||||
private stagedArgument(node: ts.Expression): IrExpr | null {
|
||||
|
||||
Reference in New Issue
Block a user