diff --git a/package-lock.json b/package-lock.json index 253556d7..8262e0bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1771,9 +1771,10 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, - "license": "MIT", "engines": { "node": "*" } @@ -5141,7 +5142,9 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-intrinsic": { diff --git a/src/backend/binaryen/wasm_expr_gen.ts b/src/backend/binaryen/wasm_expr_gen.ts index a516d8d3..d16f0e4a 100644 --- a/src/backend/binaryen/wasm_expr_gen.ts +++ b/src/backend/binaryen/wasm_expr_gen.ts @@ -1792,71 +1792,7 @@ export class WASMExpressionGen { args, funcDecl, ); - let specializedFuncName = funcName; - /* If a function is a generic function, we need to generate a specialized type function here */ - if (funcDecl && funcDecl.funcType.typeArguments) { - /* record the original information */ - const oriFuncType = funcDecl.funcType; - const oriFuncCtx = this.wasmCompiler.currentFuncCtx; - const oriFuncParams = funcDecl.parameters; - const oriFuncVars = funcDecl.varList; - /* change typeArgument to the specialize version */ - funcDecl.funcType = funcType; - if (!funcType.specialTypeArguments) { - throw new Error('not recorded the specialized type yet'); - } - let specializedSuffix = ''; - for (const specializedTypeArg of funcType.specialTypeArguments!) { - specializedSuffix = specializedSuffix.concat( - '_', - specializedTypeArg.typeId.toString(), - ); - } - specializedFuncName = funcName.concat(specializedSuffix); - funcDecl.name = specializedFuncName; - if (funcDecl.parameters) { - for (const p of funcDecl.parameters) { - if ( - p.type instanceof TypeParameterType || - p.type instanceof ValueTypeWithArguments - ) { - this.specializeType(p.type, funcType); - } - } - } - if (funcDecl.varList) { - for (const v of funcDecl.varList) { - if ( - v.type instanceof TypeParameterType || - v.type instanceof ValueTypeWithArguments - ) { - this.specializeType(v.type, funcType); - } - } - } - - this.wasmCompiler.parseFunc(funcDecl); - /* restore the information */ - this.wasmCompiler.currentFuncCtx = oriFuncCtx; - funcDecl.name = funcName; - funcDecl.funcType = oriFuncType; - funcDecl.parameters = oriFuncParams; - funcDecl.varList = oriFuncVars; - } - return this.module.call(specializedFuncName, callArgsRefs, returnType); - } - - private specializeType( - type: TypeParameterType | ValueTypeWithArguments, - root: ValueTypeWithArguments, - ) { - if (type instanceof TypeParameterType) { - const specialType = root.getSpecialTypeArg(type)!; - type.setSpecialTypeArgument(specialType); - } else { - const specTypeArgs = root.getSpecialTypeArgs(type.typeArguments!); - type.setSpecialTypeArguments(specTypeArgs); - } + return this.module.call(funcName, callArgsRefs, returnType); } private wasmObjFieldSet( diff --git a/src/expression.ts b/src/expression.ts index 4d9546b7..1481ea7f 100644 --- a/src/expression.ts +++ b/src/expression.ts @@ -7,8 +7,14 @@ import ts from 'typescript'; import { ParserContext } from './frontend.js'; import { ClosureEnvironment, FunctionScope } from './scope.js'; import { Variable } from './variable.js'; -import { getCurScope, addSourceMapLoc } from './utils.js'; -import { Type, TypeKind, builtinTypes } from './type.js'; +import { getCurScope, addSourceMapLoc, isTypeGeneric } from './utils.js'; +import { + TSFunction, + Type, + TypeKind, + TypeResolver, + builtinTypes, +} from './type.js'; import { Logger } from './log.js'; import { SourceMapLoc } from './backend/binaryen/utils.js'; import { ExpressionError } from './error.js'; @@ -530,11 +536,13 @@ export default class ExpressionProcessor { const varReferenceScope = scope!.getNearestFunctionScope(); let variable: Variable | undefined = undefined; let maybeClosureVar = false; + let exprType: Type = builtinTypes.get('undefined')!; if (varReferenceScope) { while (scope) { variable = scope.findVariable(targetIdentifier, false); if (variable) { + exprType = variable.varType; break; } @@ -560,8 +568,9 @@ export default class ExpressionProcessor { this.parserCtx.typeChecker!.getSymbolAtLocation(node); if (symbol && symbol.valueDeclaration) { declNode = symbol.valueDeclaration; + exprType = this.typeResolver.generateNodeType(declNode); } - res.setExprType(this.typeResolver.generateNodeType(declNode)); + res.setExprType(exprType); break; } case ts.SyntaxKind.BinaryExpression: { @@ -634,7 +643,7 @@ export default class ExpressionProcessor { } case ts.SyntaxKind.CallExpression: { const callExprNode = node; - const expr = this.visitNode(callExprNode.expression); + let expr = this.visitNode(callExprNode.expression); const args = new Array( callExprNode.arguments.length, ); @@ -648,6 +657,68 @@ export default class ExpressionProcessor { res.setExprType(this.typeResolver.generateNodeType(node)); break; } + + // iff a generic function is specialized and called + const origType = this.typeResolver.generateNodeType( + callExprNode.expression, + ); + if ( + isTypeGeneric(origType) && + callExprNode.expression.kind === ts.SyntaxKind.Identifier + ) { + // the function name of the CallExpression is corrected to the specialized function name + let typeArguments: Type[] | undefined; + + // explicitly declare specialization type typeArguments + // e.g. + // function genericFunc (v: T){...} + // genericFunc(5); + if (callExprNode.typeArguments) { + typeArguments = callExprNode.typeArguments.map((t) => { + return this.typeResolver.generateNodeType(t); + }); + } + // specialize by passing parameters + // e.g. + // function genericFunc (v: T){...} + // genericFunc('hello'); + if (!typeArguments) { + typeArguments = callExprNode.arguments.map((t) => { + return this.typeResolver.generateNodeType(t); + }); + } + + if (typeArguments) { + let genericInheritance = false; + typeArguments.forEach((t) => { + if (isTypeGeneric(t)) { + genericInheritance = true; + } + }); + + if (!genericInheritance) { + const typeNames = new Array(); + typeArguments.forEach((v) => { + typeNames.push(`${v.kind}`); + }); + const newIdentifierName = + (expr as IdentifierExpression).identifierName + + '<' + + typeNames.join(',') + + '>'; + expr = new IdentifierExpression(newIdentifierName); + + // the function type of the CallExpression is corrected to the specialized function type + const specializedType = + this.parserCtx.currentScope!.findIdentifier( + newIdentifierName, + ); + if (specializedType) + expr.setExprType(specializedType as Type); + } + } + } + const callExpr = new CallExpression( expr, args, diff --git a/src/scope.ts b/src/scope.ts index cc636a9c..0ece0df8 100644 --- a/src/scope.ts +++ b/src/scope.ts @@ -64,6 +64,8 @@ export class Scope { private localIndex = -1; public mangledName = ''; private modifiers: ts.Node[] = []; + // iff this Scope is specialized + private _genericOwner?: Scope; constructor(parent: Scope | null) { this.parent = parent; @@ -179,6 +181,14 @@ export class Scope { this.modifiers.push(modifier); } + setGenericOwner(genericOwner: Scope) { + this._genericOwner = genericOwner; + } + + get genericOwner(): Scope | undefined { + return this._genericOwner; + } + protected _nestFindScopeItem( name: string, searchFunc: (scope: Scope) => T | undefined, @@ -521,10 +531,11 @@ export class Scope { scope.localIndex = this.localIndex; scope.mangledName = this.mangledName; scope.modifiers = this.modifiers; + if (this.genericOwner) scope.setGenericOwner(this.genericOwner); } - // deep copy - clone(scope: Scope) { + // process generic specialization + specialize(scope: Scope) { scope.kind = this.kind; scope.name = this.name; scope.children = new Array(); @@ -537,6 +548,7 @@ export class Scope { scope.localIndex = this.localIndex; scope.mangledName = this.mangledName; scope.modifiers = this.modifiers; + if (this.genericOwner) scope.setGenericOwner(this.genericOwner); } } @@ -560,15 +572,13 @@ export class ClosureEnvironment extends Scope { copy(scope: ClosureEnvironment) { super.copy(scope); scope.kind = this.kind; - scope.parent = this.parent; scope.hasFreeVar = this.hasFreeVar; scope.contextVariable = this.contextVariable; } - clone(scope: ClosureEnvironment) { - super.clone(scope); + specialize(scope: ClosureEnvironment) { + super.specialize(scope); scope.kind = this.kind; - scope.parent = this.parent; scope.hasFreeVar = this.hasFreeVar; scope.contextVariable = this.contextVariable; } @@ -671,8 +681,6 @@ export class FunctionScope extends ClosureEnvironment { /* ori func name iff func is declare */ oriFuncName: string | undefined = undefined; debugLocations: SourceMapLoc[] = []; - // iff this FunctionScope is specialized - private _genericOwner?: FunctionScope; constructor(parent: Scope) { super(parent); @@ -726,18 +734,9 @@ export class FunctionScope extends ClosureEnvironment { return this._className !== ''; } - setGenericOwner(genericOwner: FunctionScope) { - this._genericOwner = genericOwner; - } - - get genericOwner(): FunctionScope | undefined { - return this._genericOwner; - } - copy(funcScope: FunctionScope) { super.copy(funcScope); funcScope.kind = this.kind; - funcScope.parent = this.parent!; funcScope.parameterArray = this.parameterArray; funcScope.envParamLen = this.envParamLen; funcScope.functionType = this.functionType; @@ -747,10 +746,9 @@ export class FunctionScope extends ClosureEnvironment { funcScope.debugLocations = this.debugLocations; } - clone(funcScope: FunctionScope) { - super.clone(funcScope); + specialize(funcScope: FunctionScope) { + super.specialize(funcScope); funcScope.kind = this.kind; - funcScope.parent = this.parent!; funcScope.parameterArray = new Array(); funcScope.envParamLen = this.envParamLen; funcScope.functionType = this.functionType; @@ -782,8 +780,6 @@ export class BlockScope extends ClosureEnvironment { export class ClassScope extends Scope { kind = ScopeKind.ClassScope; private _classType: TSClass = new TSClass(); - // iff this ClassScope is specialized - private _genericOwner?: ClassScope; constructor(parent: Scope, name = '') { super(parent); @@ -803,26 +799,16 @@ export class ClassScope extends Scope { return this._classType; } - setGenericOwner(genericOwner: ClassScope) { - this._genericOwner = genericOwner; - } - - get genericOwner(): ClassScope | undefined { - return this._genericOwner; - } - copy(classScope: ClassScope) { super.copy(classScope); classScope.kind = this.kind; - classScope.parent = this.parent; classScope.name = this.name; classScope._classType = this._classType; } - clone(classScope: ClassScope) { - super.clone(classScope); + specialize(classScope: ClassScope) { + super.specialize(classScope); classScope.kind = this.kind; - classScope.parent = this.parent; classScope.name = this.name; classScope._classType = this._classType; } diff --git a/src/semantics/expression_builder.ts b/src/semantics/expression_builder.ts index 5f4f8183..d13ce2f3 100644 --- a/src/semantics/expression_builder.ts +++ b/src/semantics/expression_builder.ts @@ -886,6 +886,7 @@ function buildArrayLiteralExpression( init_types = new Set(); } + // element type calculated from exprType let element_type: ValueType | undefined; if (array_type instanceof ArrayType) { element_type = (array_type).element; @@ -921,18 +922,27 @@ function buildArrayLiteralExpression( ); } - // return new NewLiteralArrayValue(array_type!, init_values); const elem_type = (array_type as ArrayType).element; - return new NewLiteralArrayValue( - array_type!, + const initValues = expr.arrayValues.length == 0 ? [] : init_values.map((v) => { return elem_type.equals(v.type) ? v : newCastValue(elem_type, v); - }), - ); + }); + // process generic array type + if (initValues.length > 0) { + // actual element type + const value_type = initValues[0].type; + if ( + elem_type.kind == ValueTypeKind.TYPE_PARAMETER && + !value_type.equals(elem_type) + ) + array_type = createArrayType(context, value_type); + } + + return new NewLiteralArrayValue(array_type!, initValues); } export function isEqualOperator(kind: ts.SyntaxKind): boolean { @@ -1278,6 +1288,14 @@ export function newCastValue( return value; } + if ( + type.kind === ValueTypeKind.GENERIC && + value_type.kind !== ValueTypeKind.GENERIC + ) { + /* no cast is required from other types to generic type */ + return value; + } + throw Error(`cannot make cast value from "${value_type}" to "${type}"`); } @@ -1760,6 +1778,16 @@ class GuessTypeArguments { return; } + if (templateType.kind == ValueTypeKind.UNION) { + const unionType = templateType as UnionType; + unionType.types.forEach((t) => { + if (t.kind == ValueTypeKind.TYPE_PARAMETER) { + this.updateTypeMap(t as TypeParameterType, valueType); + } + }); + return; + } + if (valueType.kind != templateType.kind) { throw Error( `Cannot guess the value type: template: ${templateType}, valueType: ${valueType}`, @@ -1781,9 +1809,6 @@ class GuessTypeArguments { valueType as FunctionType, ); break; - - case ValueTypeKind.UNION: - break; // TODO } } @@ -1983,7 +2008,6 @@ function buildCallExpression( func_type, specialTypeArgs, ) as FunctionType; - func_type.setSpecialTypeArguments(specialTypeArgs); (func as FunctionCallBaseValue).funcType = func_type; } diff --git a/src/semantics/index.ts b/src/semantics/index.ts index eccffb51..819fb591 100644 --- a/src/semantics/index.ts +++ b/src/semantics/index.ts @@ -19,7 +19,8 @@ import { } from './semantics_nodes.js'; import { Logger } from '../log.js'; import { ParserContext } from '../frontend.js'; -import { TSClass, TSInterface } from '../type.js'; +import { TSClass, TSInterface, TypeResolver } from '../type.js'; +import { Parameter } from '../variable.js'; import { ValueType, ValueTypeKind, @@ -205,8 +206,27 @@ function createFunctionDeclareNode( const this_type = getMethodClassType(f, context); const parameters: VarDeclareNode[] = []; if (f.genericOwner) { - f.genericOwner.paramArray.forEach((p) => { - f.addParameter(p); + const genericOwner = f.genericOwner as FunctionScope; + const specializedArgs = f.funcType.specializedArguments!; + genericOwner.paramArray.forEach((v) => { + const specializedType = TypeResolver.createSpecializedType( + v.varType, + specializedArgs, + (f.genericOwner as FunctionScope).funcType, + ); + const newParam = new Parameter( + v.varName, + specializedType, + v.varModifiers, + v.varIndex, + v.isOptional, + v.destructuring, + ); + if (v.initExpression) newParam.setInitExpr(v.initExpression); + newParam.setIsLocalVar(v.isLocalVar()); + newParam.needReBinding = v.needReBinding; + newParam.tsNode = v.tsNode; + f.addParameter(newParam); }); /* at this time, we set parameters for the specialized FunctionScope, * so we need to initialize their index once diff --git a/src/semantics/statement_builder.ts b/src/semantics/statement_builder.ts index 4e09889d..baf46a5a 100644 --- a/src/semantics/statement_builder.ts +++ b/src/semantics/statement_builder.ts @@ -38,8 +38,9 @@ import { } from './semantics_nodes.js'; import { Variable } from '../variable.js'; - -import { Scope, ScopeKind } from '../scope.js'; +import { TypeResolver } from '../type.js'; +import { isTypeGeneric } from '../utils.js'; +import { ClassScope, FunctionScope, Scope, ScopeKind } from '../scope.js'; import { Statement, @@ -135,6 +136,61 @@ export function createLocalSymbols( let varList: VarDeclareNode[] | undefined = undefined; let symbols: Map | undefined = undefined; + if (scope.genericOwner) { + const origVarArr = scope.genericOwner.varArray; + if (scope instanceof FunctionScope) { + const specializedArgs = scope.funcType.specializedArguments!; + origVarArr.forEach((v) => { + if (v.varName == '@context' || v.varName == 'this') return; + if (!isTypeGeneric(v.varType)) { + scope.addVariable(v); + } else { + const newType = TypeResolver.createSpecializedType( + v.varType, + specializedArgs, + (scope.genericOwner as FunctionScope).funcType, + ); + const newVar = new Variable( + v.varName, + newType, + v.varModifiers, + v.varIndex, + v.isLocalVar(), + ); + if (v.initExpression) newVar.setInitExpr(v.initExpression); + newVar.needReBinding = v.needReBinding; + newVar.tsNode = v.tsNode; + scope.addVariable(newVar); + } + }); + } else if (scope instanceof ClassScope) { + const specializedArgs = scope.classType.specializedArguments!; + origVarArr.forEach((v) => { + if (v.varName == '@context' || v.varName == 'this') return; + if (!isTypeGeneric(v.varType)) { + scope.addVariable(v); + } else { + const newType = TypeResolver.createSpecializedType( + v.varType, + specializedArgs, + (scope.genericOwner as ClassScope).classType, + ); + const newVar = new Variable( + v.varName, + newType, + v.varModifiers, + v.varIndex, + v.isLocalVar(), + ); + if (v.initExpression) newVar.setInitExpr(v.initExpression); + newVar.needReBinding = v.needReBinding; + newVar.tsNode = v.tsNode; + scope.addVariable(newVar); + } + }); + } + } + const vararr = scope!.varArray; if (vararr.length > 0) { symbols = new Map(); diff --git a/src/type.ts b/src/type.ts index 8ca7a5ef..db0e11ee 100644 --- a/src/type.ts +++ b/src/type.ts @@ -21,10 +21,12 @@ import { Logger } from './log.js'; import { DefaultTypeId, createClassScopeByClassType, + createFunctionScopeByFunctionType, isTypeGeneric, } from './utils.js'; import { TypeError, UnimplementError } from './error.js'; import { BuiltinNames } from '../lib/builtin/builtin_name.js'; +import { UnionType } from './semantics/value_types.js'; export const enum TypeKind { VOID = 'void', @@ -286,6 +288,7 @@ export interface ClassMethod { export class TSTypeWithArguments extends Type { private _typeArguments?: TSTypeParameter[]; + private _specializedArguments?: Type[]; private _genericOwner?: TSTypeWithArguments; private _belongedScope?: Scope; @@ -330,6 +333,14 @@ export class TSTypeWithArguments extends Type { setBelongedScope(scope: Scope | undefined) { this._belongedScope = scope; } + + get specializedArguments(): Type[] | undefined { + return this._specializedArguments; + } + + setSpecializedArguments(types: Type[] | undefined) { + this._specializedArguments = types; + } } const enum TraverseStatus { @@ -892,6 +903,7 @@ export class TypeResolver { aliasTypes, type as TSClass, ) as TSClass; + specificType.setSpecializedArguments(aliasTypes); specificType.isLiteral = true; // TODO: in this case, specificType can't be recursive this.typeIdAllocate(specificType); @@ -912,13 +924,50 @@ export class TypeResolver { return this.tsTypeToType(t); }, ); + // generate a name for the new specialized type + const typeNames = new Array(); + specializedTypes.forEach((v) => { + typeNames.push(`${v.kind}`); + }); + const typeSignature = '<' + typeNames.join(',') + '>'; + + // if a specialized type already exists + const cache = this.specializedTypeCache.get(type); + let found: Type | undefined; + if (cache) { + cache.forEach((v) => { + if (v.has(typeSignature)) { + found = v.get(typeSignature); + } + }); + } + if (found) break; + + // perform specialization operation type = TypeResolver.createSpecializedType( type, specializedTypes, type, ); + if (type instanceof TSTypeWithArguments) + type.setSpecializedArguments(specializedTypes); + const origType = this.generateNodeType(symbolNode); + + // update specializedTypeCache + if (this.specializedTypeCache.has(origType)) { + const value = new Map(); + value.set(typeSignature, type); + this.specializedTypeCache + .get(origType)! + .push(value); + } else { + const value = new Map(); + value.set(typeSignature, type); + this.specializedTypeCache.set(origType, [value]); + } } } + this.tsTypeMap.set(tsType, type); this.addTypeToTypeMap(type, symbolNode); break; } @@ -974,7 +1023,6 @@ export class TypeResolver { this.typechecker!.getTypeAtLocation(typeAliasNode); const type = this.generateNodeType(typeAliasNode.type); - // if it is a generic type, complete its typeArguments if ( tsType.aliasTypeArguments && type instanceof TSTypeWithArguments @@ -987,6 +1035,124 @@ export class TypeResolver { this.currentScope!.addType(typeName, type); break; } + case ts.SyntaxKind.CallExpression: { + // When a generic function is called, it first needs to be specialized + const callExprNode = node; + const origType = this.generateNodeType(callExprNode.expression); + // iff a generic function is specialized and called + if ( + isTypeGeneric(origType) && + callExprNode.expression.kind === ts.SyntaxKind.Identifier + ) { + let typeArguments: Type[] | undefined; + + // explicitly declare specialization type typeArguments + // e.g. + // function genericFunc (v: T){...} + // genericFunc(5); + if (callExprNode.typeArguments) { + typeArguments = callExprNode.typeArguments.map((t) => { + return this.generateNodeType(t); + }); + } + // specialize by passing parameters + // e.g. + // function genericFunc (v: T){...} + // genericFunc('hello'); + if (!typeArguments) { + const _typeArguments: Type[] = []; + // argument type + const _arguments = callExprNode.arguments.map((t) => { + return this.generateNodeType(t); + }); + // paramter type + const _paramters = ( + origType as TSFunction + ).getParamTypes(); + + // TODO: Handling optional parameters + for (let i = 0; i < _paramters.length; i++) { + if (_paramters[i].kind == TypeKind.TYPE_PARAMETER) { + _typeArguments.push(_arguments[i]); + } + } + typeArguments = _typeArguments; + } + + const typeNames = new Array(); + typeArguments.forEach((v) => { + typeNames.push(`${v.kind}`); + }); + const typeSignature = '<' + typeNames.join(',') + '>'; + // if a specialized type already exists + const cache = this.specializedTypeCache.get(origType); + let found; + if (cache) { + cache.forEach((v) => { + if (v.has(typeSignature)) { + found = v.get(typeSignature); + } + }); + } + if (found) break; + + if ( + origType instanceof TSFunction && + origType.belongedScope + ) { + // create a new specialized function type + const specializedType = + TypeResolver.createSpecializedType( + origType, + typeArguments, + origType as TSFunction, + ) as TSFunction; + specializedType.setSpecializedArguments(typeArguments); + + // generate a name for the new specialized FunctionScope + const typeNames = new Array(); + typeArguments.forEach((v) => { + typeNames.push(`${v.kind}`); + }); + const typeSignature = '<' + typeNames.join(',') + '>'; + const newScopeName = + origType.belongedScope.getName() + typeSignature; + + const newFunctionScope = + createFunctionScopeByFunctionType( + origType.belongedScope as FunctionScope, + origType.belongedScope.parent!, + specializedType, + newScopeName, + ); + specializedType.setBelongedScope(newFunctionScope); + origType.belongedScope.parent?.addType( + newScopeName, + specializedType, + ); + + if (this.specializedTypeCache.has(origType)) { + const value = new Map(); + value.set(typeSignature, specializedType); + this.specializedTypeCache + .get(origType)! + .push(value); + } else { + const value = new Map(); + value.set(typeSignature, specializedType); + this.specializedTypeCache.set(origType, [value]); + } + } + } + break; + } + case ts.SyntaxKind.AsExpression: { + const asExprNode = node; + const typeNode = asExprNode.type; + const type = this.generateNodeType(typeNode); + this.addTypeToTypeMap(type, node); + break; + } default: break; } @@ -1061,6 +1227,9 @@ export class TypeResolver { /* For "this" keyword, tsc will inference the actual type */ tsType = this.typechecker!.getDeclaredTypeOfSymbol(tsType.symbol); } + if (this.tsTypeMap.has(tsType)) { + return this.tsTypeMap.get(tsType)!; + } let type = this.tsTypeToType(tsType); /* for example, a: string[] = new Array(), the type of new Array() should be string[] @@ -1225,6 +1394,7 @@ export class TypeResolver { typeArguments, tsType as TSClass, ); + (res as TSClass).setSpecializedArguments(typeArguments); if (this.specializedTypeCache.has(tsType)) { const value = new Map(); value.set(typeSignature, res); @@ -1528,6 +1698,25 @@ export class TypeResolver { let tsType = this.tsTypeToType( this.typechecker!.getTypeAtLocation(valueDecl), ); + // e.g. + // type ItemGenerator = (item: T, index: U) => void + // function test_func(func: ItemGenerator, a: T, b: U) {...} + if ( + tsType instanceof TSTypeWithArguments && + isTypeGeneric(tsType) + ) { + const param = valueDecl as ts.ParameterDeclaration; + const typeArguments = this.typechecker!.getTypeAtLocation( + param.type!, + ).aliasTypeArguments!.map((t) => { + return this.tsTypeToType(t); + }); + tsType = TypeResolver.createSpecializedType( + tsType, + typeArguments, + tsType, + ); + } /* builtin wasm types */ const maybeWasmType = TypeResolver.maybeBuiltinWasmType(valueDecl); @@ -1691,6 +1880,7 @@ export class TypeResolver { newName, ) as TSInterface; infc.setBase(baseInfcType); + baseInfcType.setSpecializedArguments(_effectTypeArguments); baseInfcType.overrideOrOwnMethods.forEach((t) => { infc.overrideOrOwnMethods.add(t); }); @@ -1881,6 +2071,7 @@ export class TypeResolver { baseInfcType, newName, ) as TSInterface; + baseInfcType.setSpecializedArguments(_effectTypeArguments); classType.setImplInfc(baseInfcType); let _baseInfcType: TSClass | null = baseInfcType; while (_baseInfcType) { @@ -1917,6 +2108,7 @@ export class TypeResolver { baseClassType, newName, ) as TSClass; + baseClassType.setSpecializedArguments(_effectTypeArguments); classType.setBase(baseClassType); } @@ -1952,6 +2144,7 @@ export class TypeResolver { ctorScope.envParamLen = 2; const ctorType = new TSFunction(FunctionKind.CONSTRUCTOR); + ctorType.belongedClass = classType; ctorType.setBelongedScope(ctorScope); ctorType.returnType = classType; ctorType.isMethod = true; @@ -1974,6 +2167,9 @@ export class TypeResolver { if (!newType) throw new TypeError('unimpl TSTypeParameter exchange'); + if (newType instanceof TSTypeWithArguments) + newType.setSpecializedArguments(effectTypeArguments); + ctorType.addParamType(paramTypes[i]); ctorScope.addParameter( new Parameter(`@anonymous${i}`, paramTypes[i]), @@ -2471,6 +2667,23 @@ export class TypeResolver { ), ); } + case TypeKind.UNION: { + const unionType = type as TSUnion; + const newUnion = new TSUnion(); + unionType.types.forEach((t) => { + if (t.kind == TypeKind.UNDEFINED) { + newUnion.addType(t); + } else { + const newType = this.createSpecializedType( + t, + specializedArgs, + containType as TSTypeWithArguments, + ); + newUnion.addType(newType); + } + }); + return newUnion; + } case TypeKind.FUNCTION: { const funcType = type as TSFunction; const newFuncType = funcType.clone(); @@ -2481,13 +2694,14 @@ export class TypeResolver { // regenerate the parameter list newFuncType.setParamTypes([]); funcType.getParamTypes().forEach((paramType) => { - newFuncType.addParamType( - this.createSpecializedType( - paramType, - specializedArgs, - containType as TSTypeWithArguments, - ), + const newParamType = this.createSpecializedType( + paramType, + specializedArgs, + containType as TSTypeWithArguments, ); + if (newParamType instanceof TSTypeWithArguments) + newParamType.setSpecializedArguments(specializedArgs); + newFuncType.addParamType(newParamType); }); // prevent infinite recursive call @@ -2503,6 +2717,10 @@ export class TypeResolver { specializedArgs, containType as TSTypeWithArguments, ); + if (newFuncType.returnType instanceof TSTypeWithArguments) + newFuncType.returnType.setSpecializedArguments( + specializedArgs, + ); return newFuncType; } case TypeKind.CLASS: @@ -2565,6 +2783,10 @@ export class TypeResolver { specializedArgs, classType, ); + if (type instanceof TSTypeWithArguments) + type.setSpecializedArguments( + specializedArgs, + ); baseSpecializedArgs.push(type); } } @@ -2572,14 +2794,16 @@ export class TypeResolver { newType.className + '_' + base?.className.split('_').reverse()[0]; - newType.setBase( - TypeResolver.createSpecializedType( - base!, - baseSpecializedArgs, - base!, - newName, - ) as TSClass, + const newBaseType = TypeResolver.createSpecializedType( + base!, + baseSpecializedArgs, + base!, + newName, + ) as TSClass; + newBaseType.setSpecializedArguments( + baseSpecializedArgs, ); + newType.setBase(newBaseType); } else { newType.setBase(base!); } @@ -2605,16 +2829,20 @@ export class TypeResolver { specializedArgs, classType, ); + if (type instanceof TSTypeWithArguments) + type.setSpecializedArguments( + specializedArgs, + ); infcSpecializedArgs.push(type); } } - newType.setImplInfc( - TypeResolver.createSpecializedType( - implInfc, - infcSpecializedArgs, - implInfc, - ) as TSInterface, - ); + const infc = TypeResolver.createSpecializedType( + implInfc, + infcSpecializedArgs, + implInfc, + ) as TSInterface; + infc.setSpecializedArguments(infcSpecializedArgs); + newType.setImplInfc(infc); } } else { newType.setImplInfc(implInfc); @@ -2626,34 +2854,44 @@ export class TypeResolver { // specialized member variables classType.fields.forEach((field) => { + const newFieldType = this.createSpecializedType( + field.type, + specializedArgs, + containType as TSTypeWithArguments, + ); + if (newFieldType instanceof TSTypeWithArguments) + newFieldType.setSpecializedArguments(specializedArgs); newType.addMemberField({ name: field.name, - type: this.createSpecializedType( - field.type, - specializedArgs, - containType as TSTypeWithArguments, - ), + type: newFieldType, }); }); // specialized member functions classType.memberFuncs.forEach((func) => { + const newFuncType = this.createSpecializedType( + func.type, + specializedArgs, + containType as TSTypeWithArguments, + ) as TSFunction; + newFuncType.setSpecializedArguments(specializedArgs); newType.addMethod({ name: func.name, - type: this.createSpecializedType( - func.type, - specializedArgs, - containType as TSTypeWithArguments, - ) as TSFunction, + type: newFuncType, }); }); classType.staticFields.forEach((field) => { + const newStaticFieldType = this.createSpecializedType( + field.type, + specializedArgs, + containType as TSTypeWithArguments, + ); + if (newStaticFieldType instanceof TSTypeWithArguments) + newStaticFieldType.setSpecializedArguments( + specializedArgs, + ); newType.addStaticMemberField({ name: field.name, - type: this.createSpecializedType( - field.type, - specializedArgs, - containType as TSTypeWithArguments, - ), + type: newStaticFieldType, }); }); // specialized constructor @@ -2663,6 +2901,7 @@ export class TypeResolver { specializedArgs, containType as TSTypeWithArguments, ) as TSFunction; + newType.ctorType.setSpecializedArguments(specializedArgs); newType.ctorType.returnType = newType; } @@ -2687,7 +2926,6 @@ export class TypeResolver { return specializedArgs[i]; } } - // return typeArg; } return builtinTypes.get('any')!; diff --git a/src/utils.ts b/src/utils.ts index 013c250e..59b3224e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -433,17 +433,20 @@ export function processEscape(str: string) { * @param originalClassScope the original ClassScope to be specialized * @param parent the parent of the original ClassScope * @param classType the new class type corresponding to specialized ClassScope: TSClass => ClassScope + * @param newName the name of new ClassScope * @returns a new specialized ClassScope */ export function createClassScopeByClassType( originalClassScope: ClassScope, parent: Scope, classType: TSClass, + newName?: string, ) { const newClassScope = new ClassScope(parent); - originalClassScope.clone(newClassScope); + originalClassScope.specialize(newClassScope); newClassScope.setGenericOwner(originalClassScope); - newClassScope.setName(classType.className); + const name = newName ? newName : classType.className; + newClassScope.setName(name); newClassScope.setClassType(classType); originalClassScope.children.forEach((s) => { @@ -494,17 +497,20 @@ export function createClassScopeByClassType( * @param originalFunctionScope the original FunctionScope to be specialized * @param parent the parent of the original FunctionScope * @param functionType the new function type corresponding to specialized FunctionScope: TSFunction => FunctionScope + * @param newName the name of new FunctionScope * @returns a new specialized FunctionScope */ export function createFunctionScopeByFunctionType( originalFunctionScope: FunctionScope, parent: Scope, functionType: TSFunction, + newName?: string, ) { const newFuncScope = new FunctionScope(parent); const className = parent instanceof ClassScope ? parent.className : ''; newFuncScope.setClassName(className); - newFuncScope.setFuncName(originalFunctionScope.funcName); + const name = newName ? newName : originalFunctionScope.funcName; + newFuncScope.setFuncName(name); newFuncScope.envParamLen = originalFunctionScope.envParamLen; newFuncScope.setGenericOwner(originalFunctionScope); @@ -529,6 +535,7 @@ export function createFunctionScopeByFunctionType( } }); newFuncScope.setFuncType(functionType); + functionType.setBelongedScope(newFuncScope); return newFuncScope; } diff --git a/tests/samples/generic_class.ts b/tests/samples/generic_class.ts index c71dd9f5..b90c7080 100644 --- a/tests/samples/generic_class.ts +++ b/tests/samples/generic_class.ts @@ -198,4 +198,23 @@ export function test_GenericClassWithTypeAlias() { const obj2: MyObject = {a: 123, b: true}; console.log(obj2.a); console.log(obj2.b); +} + +interface II { + get(info: T) : T; +} + +class A implements II { + get(info: T) { + return info; + } +} + +function get(obj: any) { + return (obj as II).get(1); +} + +export function test_AS() { + const a = new A(); + console.log(get(a)); } \ No newline at end of file diff --git a/tests/samples/generic_param.ts b/tests/samples/generic_param.ts index 850abb70..4ae03355 100644 --- a/tests/samples/generic_param.ts +++ b/tests/samples/generic_param.ts @@ -18,4 +18,22 @@ export function testGenericParam() { const fn: ForCallback = (item: number, index: number): number => {return item + index} anyFunc(fn(1, 2)); +} + +type ItemGenerator = (item: T, index?: U) => void + +function func1(func: ItemGenerator, a: T, b: U) { + func(a, b); +} + +function func2(func: ItemGenerator, a: T, b: U) { + func(b, a); +} + +export function typeFunc() { + const itemGenerator1: ItemGenerator = (item: boolean, index?: number) => {console.log(item)}; + func1(itemGenerator1, true, 444); + + const itemGenerator2: ItemGenerator = (item: string, index?: boolean) => {console.log(index)}; + func2(itemGenerator2, false, 'hello'); } \ No newline at end of file diff --git a/tools/validate/wamr/validation.json b/tools/validate/wamr/validation.json index 255288c2..68a50e0d 100644 --- a/tools/validate/wamr/validation.json +++ b/tools/validate/wamr/validation.json @@ -3455,6 +3455,11 @@ "name": "testGenericParam", "args": [], "result": "hi\n3" + }, + { + "name": "typeFunc", + "args": [], + "result": "true\nfalse" } ] }, @@ -3485,6 +3490,11 @@ "name": "test_GenericClassWithTypeAlias", "args": [], "result": "John\n18\n123\ntrue" + }, + { + "name": "test_AS", + "args": [], + "result": "1" } ] },