From e977691226d48a43a0541d7d252f9a4a6577fde4 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sat, 20 Sep 2025 00:25:22 +0800 Subject: [PATCH 1/2] chore: refactor unary postfix code emitter to reduce nested tostack (#2949) --- src/builtins.ts | 161 +++++++++++++++++++++++++++++++++++++++++++++++- src/resolver.ts | 144 ++----------------------------------------- 2 files changed, 163 insertions(+), 142 deletions(-) diff --git a/src/builtins.ts b/src/builtins.ts index 94596b4849..f876049603 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -43,7 +43,8 @@ import { NodeKind, LiteralExpression, ArrayLiteralExpression, - IdentifierExpression + IdentifierExpression, + NamedTypeNode } from "./ast"; import { @@ -85,7 +86,9 @@ import { DecoratorFlags, Class, PropertyPrototype, - VariableLikeElement + VariableLikeElement, + Element, + OperatorKind } from "./program"; import { @@ -94,11 +97,13 @@ import { } from "./flow"; import { - ReportMode + ReportMode, + Resolver } from "./resolver"; import { CommonFlags, + CommonNames, Feature, featureToString, TypeinfoFlags @@ -768,6 +773,17 @@ export namespace BuiltinNames { export const Object = "~lib/object/Object"; } +/** Builtin types context. */ +export class BuiltinTypesContext { + constructor( + public resolver: Resolver, + public node: NamedTypeNode, + public ctxElement: Element, + public ctxTypes: Map | null, + public reportMode: ReportMode + ) {} +} + /** Builtin variable compilation context. */ export class BuiltinVariableContext { constructor( @@ -804,6 +820,9 @@ export class BuiltinFunctionContext { ) {} } +/** Builtin types map. */ +export const builtinTypes = new Map Type | null>(); + /** Builtin functions map. */ export const builtinFunctions = new Map ExpressionRef>(); @@ -811,6 +830,142 @@ export const builtinFunctions = new Map export const builtinVariables_onCompile = new Map void>(); export const builtinVariables_onAccess = new Map ExpressionRef>(); +// === Builtin Types ========================================================================== +function builtin_resolveNativeType(ctx: BuiltinTypesContext): Type | null { + let resolver = ctx.resolver; + let node = ctx.node; + let ctxElement = ctx.ctxElement; + let ctxTypes = ctx.ctxTypes; + let reportMode = ctx.reportMode; + const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode); + if (!typeArgumentNode) return null; + let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); + if (!typeArgument) return null; + switch (typeArgument.kind) { + case TypeKind.I8: + case TypeKind.I16: + case TypeKind.I32: return Type.i32; + case TypeKind.Isize: if (!resolver.program.options.isWasm64) return Type.i32; + case TypeKind.I64: return Type.i64; + case TypeKind.U8: + case TypeKind.U16: + case TypeKind.U32: + case TypeKind.Bool: return Type.u32; + case TypeKind.Usize: if (!resolver.program.options.isWasm64) return Type.u32; + case TypeKind.U64: return Type.u64; + case TypeKind.F32: return Type.f32; + case TypeKind.F64: return Type.f64; + case TypeKind.V128: return Type.v128; + case TypeKind.Void: return Type.void; + default: assert(false); + } + return null; +} +builtinTypes.set(CommonNames.native, builtin_resolveNativeType); + +function builtin_resolveIndexOfType(ctx: BuiltinTypesContext): Type | null { + let resolver = ctx.resolver; + let node = ctx.node; + let ctxElement = ctx.ctxElement; + let ctxTypes = ctx.ctxTypes; + let reportMode = ctx.reportMode; + const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode); + if (!typeArgumentNode) return null; + let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); + if (!typeArgument) return null; + let classReference = typeArgument.classReference; + if (!classReference) { + if (reportMode == ReportMode.Report) { + resolver.error( + DiagnosticCode.Index_signature_is_missing_in_type_0, + typeArgumentNode.range, typeArgument.toString() + ); + } + return null; + } + let overload = classReference.lookupOverload(OperatorKind.IndexedGet); + if (overload) { + let parameterTypes = overload.signature.parameterTypes; + if (overload.is(CommonFlags.Static)) { + assert(parameterTypes.length == 2); + return parameterTypes[1]; + } else { + assert(parameterTypes.length == 1); + return parameterTypes[0]; + } + } + if (reportMode == ReportMode.Report) { + resolver.error( + DiagnosticCode.Index_signature_is_missing_in_type_0, + typeArgumentNode.range, typeArgument.toString() + ); + } + return null; +} +builtinTypes.set(CommonNames.indexof, builtin_resolveIndexOfType); + +function builtin_resolveValueOfType(ctx: BuiltinTypesContext): Type | null { + let resolver = ctx.resolver; + let node = ctx.node; + let ctxElement = ctx.ctxElement; + let ctxTypes = ctx.ctxTypes; + let reportMode = ctx.reportMode; + const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode); + if (!typeArgumentNode) return null; + let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); + if (!typeArgument) return null; + let classReference = typeArgument.getClassOrWrapper(resolver.program); + if (classReference) { + let overload = classReference.lookupOverload(OperatorKind.IndexedGet); + if (overload) return overload.signature.returnType; + } + if (reportMode == ReportMode.Report) { + resolver.error( + DiagnosticCode.Index_signature_is_missing_in_type_0, + typeArgumentNode.range, typeArgument.toString() + ); + } + return null; +} +builtinTypes.set(CommonNames.valueof, builtin_resolveValueOfType); + +function builtin_resolveReturnOfType(ctx: BuiltinTypesContext): Type | null { + let resolver = ctx.resolver; + let node = ctx.node; + let ctxElement = ctx.ctxElement; + let ctxTypes = ctx.ctxTypes; + let reportMode = ctx.reportMode; + const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode); + if (!typeArgumentNode) return null; + let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); + if (!typeArgument) return null; + let signatureReference = typeArgument.getSignature(); + if (signatureReference) return signatureReference.returnType; + if (reportMode == ReportMode.Report) { + resolver.error( + DiagnosticCode.Type_0_has_no_call_signatures, + typeArgumentNode.range, typeArgument.toString() + ); + } + return null; +} +builtinTypes.set(CommonNames.returnof, builtin_resolveReturnOfType); + +function builtin_resolveNonnullableType(ctx: BuiltinTypesContext): Type | null { + let resolver = ctx.resolver; + let node = ctx.node; + let ctxElement = ctx.ctxElement; + let ctxTypes = ctx.ctxTypes; + let reportMode = ctx.reportMode; + const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode); + if (!typeArgumentNode) return null; + let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); + if (!typeArgument) return null; + if (!typeArgument.isNullableReference) return typeArgument; + return typeArgument.nonNullableType; +} +builtinTypes.set(CommonNames.nonnull, builtin_resolveNonnullableType); + // === Static type evaluation ================================================================= // helper global used by checkConstantType diff --git a/src/resolver.ts b/src/resolver.ts index 4718529d5d..2218265ae8 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -103,7 +103,9 @@ import { } from "./tokenizer"; import { - BuiltinNames + BuiltinNames, + builtinTypes, + BuiltinTypesContext } from "./builtins"; /** Indicates whether errors are reported or not. */ @@ -299,11 +301,7 @@ export class Resolver extends DiagnosticEmitter { // Handle special built-in types if (isSimpleType) { let text = nameNode.identifier.text; - if (text == CommonNames.native) return this.resolveBuiltinNativeType(node, ctxElement, ctxTypes, reportMode); - if (text == CommonNames.indexof) return this.resolveBuiltinIndexofType(node, ctxElement, ctxTypes, reportMode); - if (text == CommonNames.valueof) return this.resolveBuiltinValueofType(node, ctxElement, ctxTypes, reportMode); - if (text == CommonNames.returnof) return this.resolveBuiltinReturnTypeType(node, ctxElement, ctxTypes, reportMode); - if (text == CommonNames.nonnull) return this.resolveBuiltinNotNullableType(node, ctxElement, ctxTypes, reportMode); + if (builtinTypes.has(text)) return assert(builtinTypes.get(text))(new BuiltinTypesContext(this, node, ctxElement, ctxTypes, reportMode)); } // Resolve normally @@ -441,138 +439,6 @@ export class Resolver extends DiagnosticEmitter { return node.isNullable ? signature.type.asNullable() : signature.type; } - private resolveBuiltinNativeType( - /** The type to resolve. */ - node: NamedTypeNode, - /** Contextual element. */ - ctxElement: Element, - /** Contextual types, i.e. `T`. */ - ctxTypes: Map | null = null, - /** How to proceed with eventual diagnostics. */ - reportMode: ReportMode = ReportMode.Report - ): Type | null { - const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); - if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); - if (!typeArgument) return null; - switch (typeArgument.kind) { - case TypeKind.I8: - case TypeKind.I16: - case TypeKind.I32: return Type.i32; - case TypeKind.Isize: if (!this.program.options.isWasm64) return Type.i32; - case TypeKind.I64: return Type.i64; - case TypeKind.U8: - case TypeKind.U16: - case TypeKind.U32: - case TypeKind.Bool: return Type.u32; - case TypeKind.Usize: if (!this.program.options.isWasm64) return Type.u32; - case TypeKind.U64: return Type.u64; - case TypeKind.F32: return Type.f32; - case TypeKind.F64: return Type.f64; - case TypeKind.V128: return Type.v128; - case TypeKind.Void: return Type.void; - default: assert(false); - } - return null; - } - - private resolveBuiltinIndexofType( - /** The type to resolve. */ - node: NamedTypeNode, - /** Contextual element. */ - ctxElement: Element, - /** Contextual types, i.e. `T`. */ - ctxTypes: Map | null = null, - /** How to proceed with eventual diagnostics. */ - reportMode: ReportMode = ReportMode.Report - ): Type | null { - const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); - if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); - if (!typeArgument) return null; - let classReference = typeArgument.classReference; - if (!classReference) { - if (reportMode == ReportMode.Report) { - this.error( - DiagnosticCode.Index_signature_is_missing_in_type_0, - typeArgumentNode.range, typeArgument.toString() - ); - } - return null; - } - let overload = classReference.lookupOverload(OperatorKind.IndexedGet); - if (overload) { - let parameterTypes = overload.signature.parameterTypes; - if (overload.is(CommonFlags.Static)) { - assert(parameterTypes.length == 2); - return parameterTypes[1]; - } else { - assert(parameterTypes.length == 1); - return parameterTypes[0]; - } - } - if (reportMode == ReportMode.Report) { - this.error( - DiagnosticCode.Index_signature_is_missing_in_type_0, - typeArgumentNode.range, typeArgument.toString() - ); - } - return null; - } - - private resolveBuiltinValueofType( - /** The type to resolve. */ - node: NamedTypeNode, - /** Contextual element. */ - ctxElement: Element, - /** Contextual types, i.e. `T`. */ - ctxTypes: Map | null = null, - /** How to proceed with eventual diagnostics. */ - reportMode: ReportMode = ReportMode.Report - ): Type | null { - const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); - if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); - if (!typeArgument) return null; - let classReference = typeArgument.getClassOrWrapper(this.program); - if (classReference) { - let overload = classReference.lookupOverload(OperatorKind.IndexedGet); - if (overload) return overload.signature.returnType; - } - if (reportMode == ReportMode.Report) { - this.error( - DiagnosticCode.Index_signature_is_missing_in_type_0, - typeArgumentNode.range, typeArgument.toString() - ); - } - return null; - } - - private resolveBuiltinReturnTypeType( - /** The type to resolve. */ - node: NamedTypeNode, - /** Contextual element. */ - ctxElement: Element, - /** Contextual types, i.e. `T`. */ - ctxTypes: Map | null = null, - /** How to proceed with eventualy diagnostics. */ - reportMode: ReportMode = ReportMode.Report - ): Type | null { - const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); - if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); - if (!typeArgument) return null; - let signatureReference = typeArgument.getSignature(); - if (signatureReference) return signatureReference.returnType; - if (reportMode == ReportMode.Report) { - this.error( - DiagnosticCode.Type_0_has_no_call_signatures, - typeArgumentNode.range, typeArgument.toString() - ); - } - return null; - } - private resolveBuiltinNotNullableType( /** The type to resolve. */ node: NamedTypeNode, @@ -3814,7 +3680,7 @@ export class Resolver extends DiagnosticEmitter { return instance; } - private ensureOneTypeArgument( + ensureOneTypeArgument( /** The type to resolve. */ node: NamedTypeNode, /** How to proceed with eventual diagnostics. */ From 4ae894c52088519f3e39adebeb61143099c4d042 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Fri, 26 Sep 2025 11:37:23 +0800 Subject: [PATCH 2/2] fix review --- src/resolver.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/resolver.ts b/src/resolver.ts index 2218265ae8..0823838fa2 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -301,7 +301,11 @@ export class Resolver extends DiagnosticEmitter { // Handle special built-in types if (isSimpleType) { let text = nameNode.identifier.text; - if (builtinTypes.has(text)) return assert(builtinTypes.get(text))(new BuiltinTypesContext(this, node, ctxElement, ctxTypes, reportMode)); + if (builtinTypes.has(text)) { + let fn = assert(builtinTypes.get(text)); + let ctx = new BuiltinTypesContext(this, node, ctxElement, ctxTypes, reportMode); + return fn(ctx); + } } // Resolve normally