diff --git a/package-lock.json b/package-lock.json index 59edf5a86b..5a5574e1f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "assemblyscript", "version": "0.0.0", "license": "Apache-2.0", "dependencies": { diff --git a/src/compiler.ts b/src/compiler.ts index 0ee4365ecb..c01ef852cc 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -474,7 +474,7 @@ export class Compiler extends DiagnosticEmitter { module.setFeatures(featureFlags); // set up the main start function - let startFunctionInstance = program.makeNativeFunction(BuiltinNames.start, new Signature(program, [], Type.void)); + let startFunctionInstance = program.makeNativeFunction(BuiltinNames.start, Signature.create(program, [], Type.void)); startFunctionInstance.internalName = BuiltinNames.start; this.currentFlow = startFunctionInstance.flow; this.currentBody = new Array(); @@ -6430,8 +6430,8 @@ export class Compiler extends DiagnosticEmitter { assert(operandIndex == minOperands); // create the varargs stub - stub = original.newStub("varargs"); - stub.signature.requiredParameters = maxArguments; + stub = original.newStub("varargs", maxArguments); + original.varargsStub = stub; // compile initializers of omitted arguments in the scope of the stub, @@ -7102,8 +7102,7 @@ export class Compiler extends DiagnosticEmitter { } } - let signature = new Signature(this.program, parameterTypes, returnType, thisType); - signature.requiredParameters = numParameters; // ! + let signature = Signature.create(this.program, parameterTypes, returnType, thisType, numParameters); instance = new Function( prototype.name, prototype, @@ -8669,7 +8668,7 @@ export class Compiler extends DiagnosticEmitter { ) ), null, - new Signature(this.program, null, classInstance.type, classInstance.type), + Signature.create(this.program, [], classInstance.type, classInstance.type), contextualTypeArguments ); } diff --git a/src/program.ts b/src/program.ts index 63360df1be..abfcfe6162 100644 --- a/src/program.ts +++ b/src/program.ts @@ -472,7 +472,7 @@ export class Program extends DiagnosticEmitter { /** Managed classes contained in the program, by id. */ managedClasses: Map = new Map(); /** A set of unique function signatures contained in the program, by id. */ - uniqueSignatures: Signature[] = new Array(0); + uniqueSignatures: Map = new Map(); /** Module exports. */ moduleExports: Map = new Map(); /** Module imports. */ @@ -3217,7 +3217,7 @@ export class File extends Element { program.filesByName.set(this.internalName, this); let startFunction = this.program.makeNativeFunction( `start:${this.internalName}`, - new Signature(program, null, Type.void), + Signature.create(program, [], Type.void), this ); startFunction.internalName = startFunction.name; @@ -3826,12 +3826,12 @@ export class Function extends TypedElement { } /** Creates a stub for use with this function, i.e. for varargs or override calls. */ - newStub(postfix: string): Function { + newStub(postfix: string, requiredParameters: i32 = this.signature.requiredParameters): Function { let stub = new Function( this.original.name + STUB_DELIMITER + postfix, this.prototype, this.typeArguments, - this.signature.clone(), + this.signature.clone(requiredParameters), this.contextualTypeArguments ); stub.original = this.original; diff --git a/src/resolver.ts b/src/resolver.ts index f9baff992d..20005b954d 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -422,9 +422,7 @@ export class Resolver extends DiagnosticEmitter { ); if (!returnType) return null; } - let signature = new Signature(this.program, parameterTypes, returnType, thisType); - signature.requiredParameters = requiredParameters; - signature.hasRest = hasRest; + let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest); return node.isNullable ? signature.type.asNullable() : signature.type; } @@ -2706,7 +2704,14 @@ export class Resolver extends DiagnosticEmitter { } const type = this.resolveExpression(expr, tempFlow, ctxType, reportMode); if (type) { - signatureReference.returnType = type; + functionType.signatureReference = Signature.create( + this.program, + signatureReference.parameterTypes, + type, + signatureReference.thisType, + signatureReference.requiredParameters, + signatureReference.hasRest, + ); } } return functionType; @@ -2853,8 +2858,7 @@ export class Resolver extends DiagnosticEmitter { returnType = type; } - let signature = new Signature(this.program, parameterTypes, returnType, thisType); - signature.requiredParameters = requiredParameters; + let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters); let nameInclTypeParameters = prototype.name; if (instanceKey.length) nameInclTypeParameters += `<${instanceKey}>`; diff --git a/src/types.ts b/src/types.ts index a0074edc70..baad2a89f5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -421,12 +421,17 @@ export class Type { /** Tests if this type equals the specified. */ equals(other: Type): bool { - if (this.kind != other.kind) return false; + if (this.kind != other.kind) { + return false; + } if (this.isReference) { + let selfSignatureReference = this.signatureReference; + let otherSignatureReference = other.signatureReference; + return ( - this.classReference == other.classReference && - this.signatureReference == other.signatureReference && - this.isNullableReference == other.isNullableReference + this.classReference == other.classReference + && selfSignatureReference == otherSignatureReference + && this.isNullableReference == other.isNullableReference ); } return true; @@ -882,58 +887,71 @@ export function typesToString(types: Type[]): string { /** Represents a fully resolved function signature. */ export class Signature { - /** Unique id representing this signature. */ - id: u32 = 0; - /** Parameter types, if any, excluding `this`. */ - parameterTypes: Type[]; - /** Number of required parameters excluding `this`. Other parameters are considered optional. */ - requiredParameters: i32; - /** Return type. */ - returnType: Type; - /** This type, if an instance signature. */ - thisType: Type | null; - /** Whether the last parameter is a rest parameter. */ - hasRest: bool; - /** Respective function type. */ - type: Type; - /** The program that created this signature. */ - program: Program; - - /** Constructs a new signature. */ - constructor( + /** Construct a new signature. */ + public static create( + /** The program that created this signature. */ program: Program, - parameterTypes: Type[] | null = null, - returnType: Type | null = null, - thisType: Type | null = null - ) { - this.parameterTypes = parameterTypes ? parameterTypes : []; - this.requiredParameters = 0; - this.returnType = returnType ? returnType : Type.void; - this.thisType = thisType; - this.program = program; - this.hasRest = false; + /** Parameter types, if any, excluding `this`. */ + parameterTypes: Type[] = [], + /** Return type. */ + returnType: Type = Type.void, + /** This type, if an instance signature. */ + thisType: Type | null = null, + /** Number of required parameters excluding `this`. Other parameters are considered optional. */ + requiredParameters: i32 = parameterTypes ? parameterTypes.length : 0, + /** Whether the last parameter is a rest parameter. */ + hasRest: bool = false, + ): Signature { + // get the usize type, and the type of the signature let usizeType = program.options.usizeType; let type = new Type( usizeType.kind, usizeType.flags & ~TypeFlags.Value | TypeFlags.Reference, usizeType.size ); - this.type = type; - type.signatureReference = this; + // calculate the properties let signatureTypes = program.uniqueSignatures; - let length = signatureTypes.length; - for (let i = 0; i < length; i++) { - let compare = unchecked(signatureTypes[i]); - if (this.equals(compare)) { - this.id = compare.id; - return this; - } + let nextId = program.nextSignatureId; + + // construct the signature and calculate it's unique key + let signature = new Signature(program, parameterTypes, returnType, thisType, requiredParameters, hasRest, nextId, type); + let uniqueKey = signature.toString(); + + // check if it exists, and return it + if (signatureTypes.has(uniqueKey)) { + let existing = assert(signatureTypes.get(uniqueKey)); + assert(signature.equals(existing)); + return existing; } - this.id = program.nextSignatureId++; - signatureTypes.push(this); + + // otherwise increment the program's signature id, set the signature reference of the type, and memoize the signature + program.nextSignatureId = nextId + 1; + type.signatureReference = signature; + signatureTypes.set(uniqueKey, signature); + return signature; } + /** Constructs a new signature. */ + private constructor( + /** The program that created this signature. */ + public readonly program: Program, + /** Parameter types, if any, excluding `this`. */ + public readonly parameterTypes: Type[], + /** Return type. */ + public readonly returnType: Type, + /** This type, if an instance signature. */ + public readonly thisType: Type | null, + /** Number of required parameters excluding `this`. Other parameters are considered optional. */ + public readonly requiredParameters: i32, + /** Whether the last parameter is a rest parameter. */ + public readonly hasRest: bool, + /** Unique id representing this signature. */ + public readonly id: u32, + /** Respective function type. */ + public readonly type: Type, + ) {} + get paramRefs(): TypeRef { let thisType = this.thisType; let parameterTypes = this.parameterTypes; @@ -975,15 +993,15 @@ export class Signature { if (!this.returnType.equals(other.returnType)) return false; // check parameter types - let thisParameterTypes = this.parameterTypes; + let selfParameterTypes = this.parameterTypes; let otherParameterTypes = other.parameterTypes; - let numParameters = thisParameterTypes.length; - if (numParameters != otherParameterTypes.length) return false; + let numParameters = selfParameterTypes.length; + if (numParameters != otherParameterTypes.length) return false; for (let i = 0; i < numParameters; ++i) { - let thisParameterType = unchecked(thisParameterTypes[i]); + let selfParameterType = unchecked(selfParameterTypes[i]); let otherParameterType = unchecked(otherParameterTypes[i]); - if (!thisParameterType.equals(otherParameterType)) return false; + if (!selfParameterType.equals(otherParameterType)) return false; } return true; } @@ -1105,7 +1123,6 @@ export class Signature { let thisType = this.thisType; if (thisType) { sb.push(validWat ? "this:" : "this: "); - assert(!thisType.signatureReference); sb.push(thisType.toString(validWat)); index = 1; } @@ -1127,18 +1144,20 @@ export class Signature { } /** Creates a clone of this signature that is safe to modify. */ - clone(): Signature { + clone(requiredParameters: i32 = this.requiredParameters, hasRest: bool = this.hasRest): Signature { let parameterTypes = this.parameterTypes; let numParameterTypes = parameterTypes.length; let cloneParameterTypes = new Array(numParameterTypes); for (let i = 0; i < numParameterTypes; ++i) { unchecked(cloneParameterTypes[i] = parameterTypes[i]); } - return new Signature( + return Signature.create( this.program, cloneParameterTypes, this.returnType, - this.thisType + this.thisType, + requiredParameters, + hasRest ); } } diff --git a/tests/compiler/builtins.debug.wat b/tests/compiler/builtins.debug.wat index 9986eafa4e..22e152bde3 100644 --- a/tests/compiler/builtins.debug.wat +++ b/tests/compiler/builtins.debug.wat @@ -3213,11 +3213,11 @@ local.set $48 i32.const 0 local.set $49 - i32.const 46 + i32.const 51 local.set $50 - i32.const 47 + i32.const 52 local.set $51 - i32.const 47 + i32.const 52 local.set $52 i32.const 256 local.set $63 @@ -3262,7 +3262,7 @@ unreachable end local.get $50 - i32.const 46 + i32.const 51 i32.eq i32.eqz if diff --git a/tests/compiler/builtins.release.wat b/tests/compiler/builtins.release.wat index de997e4994..7ef85acda2 100644 --- a/tests/compiler/builtins.release.wat +++ b/tests/compiler/builtins.release.wat @@ -735,9 +735,9 @@ i32.const 5 f64.const 0 f64.const 0 - f64.const 46 - f64.const 47 - f64.const 47 + f64.const 51 + f64.const 52 + f64.const 52 call $~lib/builtins/trace global.get $~lib/memory/__stack_pointer local.tee $0