diff --git a/src/compiler.ts b/src/compiler.ts index 77bdc5e8..cb2cbe3d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5152,7 +5152,7 @@ export class Compiler extends DiagnosticEmitter { } else { this.error( DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, - expression.range, (target).type.toString() + expression.range, type.toString() ); return module.createUnreachable(); } @@ -5167,7 +5167,20 @@ export class Compiler extends DiagnosticEmitter { ); break; } - case ElementKind.PROPERTY: // TODO + + case ElementKind.PROPERTY: { + indexArg = this.compileGetter(target, expression.expression); + let type = this.currentType; + signature = type.signatureReference; + if (!signature) { + this.error( + DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, + expression.range, type.toString() + ); + return module.createUnreachable(); + } + break; + } // not supported default: { @@ -6711,43 +6724,8 @@ export class Compiler extends DiagnosticEmitter { (target).memoryOffset ); } - case ElementKind.PROPERTY: { // instance property (here: getter) - let prototype = (target).getterPrototype; - if (prototype) { - let instance = this.resolver.resolveFunction(prototype, null); - if (!instance) return module.createUnreachable(); - let signature = instance.signature; - if (!this.checkCallSignature( // reports - signature, - 0, - instance.is(CommonFlags.INSTANCE), - propertyAccess - )) { - return module.createUnreachable(); - } - let inline = (instance.decoratorFlags & DecoratorFlags.INLINE) != 0; - if (instance.is(CommonFlags.INSTANCE)) { - let parent = assert(instance.parent); - assert(parent.kind == ElementKind.CLASS); - let thisExpression = assert(this.resolver.currentThisExpression); - let thisExpr = this.compileExpressionRetainType( - thisExpression, - this.options.usizeType, - WrapMode.NONE - ); - this.currentType = signature.returnType; - return this.compileCallDirect(instance, [], propertyAccess, thisExpr, inline); - } else { - this.currentType = signature.returnType; - return this.compileCallDirect(instance, [], propertyAccess, 0, inline); - } - } else { - this.error( - DiagnosticCode.Property_0_does_not_exist_on_type_1, - propertyAccess.range, (target).simpleName, (target).parent.toString() - ); - return module.createUnreachable(); - } + case ElementKind.PROPERTY: {// instance property (here: getter) + return this.compileGetter(target, propertyAccess); } } this.error( @@ -6757,6 +6735,45 @@ export class Compiler extends DiagnosticEmitter { return module.createUnreachable(); } + private compileGetter(target: Property, reportNode: Node): ExpressionRef { + var prototype = target.getterPrototype; + if (prototype) { + let instance = this.resolver.resolveFunction(prototype, null); + if (!instance) return this.module.createUnreachable(); + let signature = instance.signature; + if (!this.checkCallSignature( // reports + signature, + 0, + instance.is(CommonFlags.INSTANCE), + reportNode + )) { + return this.module.createUnreachable(); + } + let inline = (instance.decoratorFlags & DecoratorFlags.INLINE) != 0; + if (instance.is(CommonFlags.INSTANCE)) { + let parent = assert(instance.parent); + assert(parent.kind == ElementKind.CLASS); + let thisExpression = assert(this.resolver.currentThisExpression); //!!! + let thisExpr = this.compileExpressionRetainType( + thisExpression, + this.options.usizeType, + WrapMode.NONE + ); + this.currentType = signature.returnType; + return this.compileCallDirect(instance, [], reportNode, thisExpr, inline); + } else { + this.currentType = signature.returnType; + return this.compileCallDirect(instance, [], reportNode, 0, inline); + } + } else { + this.error( + DiagnosticCode.Property_0_does_not_exist_on_type_1, + reportNode.range, (target).simpleName, (target).parent.toString() + ); + return this.module.createUnreachable(); + } + } + compileTernaryExpression(expression: TernaryExpression, contextualType: Type): ExpressionRef { var ifThen = expression.ifThen; var ifElse = expression.ifElse; diff --git a/tests/compiler/getter-call.ts b/tests/compiler/getter-call.ts new file mode 100644 index 00000000..a75450f2 --- /dev/null +++ b/tests/compiler/getter-call.ts @@ -0,0 +1,13 @@ +import "allocator/arena"; + +class C { + get x(): () => i32 { + return (): i32 => 42; + } +} + +export function test(): i32 { + // TODO: GH#251 return new C().x(); + let c = new C(); + return c.x(); +} diff --git a/tests/compiler/getter-call.untouched.wat b/tests/compiler/getter-call.untouched.wat new file mode 100644 index 00000000..40e11317 --- /dev/null +++ b/tests/compiler/getter-call.untouched.wat @@ -0,0 +1,182 @@ +(module + (type $i (func (result i32))) + (type $ii (func (param i32) (result i32))) + (type $v (func)) + (global $~lib/internal/allocator/AL_BITS i32 (i32.const 3)) + (global $~lib/internal/allocator/AL_SIZE i32 (i32.const 8)) + (global $~lib/internal/allocator/AL_MASK i32 (i32.const 7)) + (global $~lib/internal/allocator/MAX_SIZE_32 i32 (i32.const 1073741824)) + (global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0)) + (global $~lib/allocator/arena/offset (mut i32) (i32.const 0)) + (global $~argc (mut i32) (i32.const 0)) + (global $HEAP_BASE i32 (i32.const 8)) + (table 1 1 anyfunc) + (elem (i32.const 0) $getter-call/C#get:x~anonymous|0) + (memory $0 0) + (export "memory" (memory $0)) + (export "table" (table $0)) + (export "test" (func $getter-call/test)) + (start $start) + (func $~lib/allocator/arena/__memory_allocate (; 0 ;) (type $ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (if + (i32.gt_u + (get_local $0) + (get_global $~lib/internal/allocator/MAX_SIZE_32) + ) + (unreachable) + ) + (set_local $1 + (get_global $~lib/allocator/arena/offset) + ) + (set_local $4 + (i32.and + (i32.add + (i32.add + (get_local $1) + (select + (tee_local $2 + (get_local $0) + ) + (tee_local $3 + (i32.const 1) + ) + (i32.gt_u + (get_local $2) + (get_local $3) + ) + ) + ) + (get_global $~lib/internal/allocator/AL_MASK) + ) + (i32.xor + (get_global $~lib/internal/allocator/AL_MASK) + (i32.const -1) + ) + ) + ) + (set_local $5 + (current_memory) + ) + (if + (i32.gt_u + (get_local $4) + (i32.shl + (get_local $5) + (i32.const 16) + ) + ) + (block + (set_local $2 + (i32.shr_u + (i32.and + (i32.add + (i32.sub + (get_local $4) + (get_local $1) + ) + (i32.const 65535) + ) + (i32.xor + (i32.const 65535) + (i32.const -1) + ) + ) + (i32.const 16) + ) + ) + (set_local $3 + (select + (tee_local $3 + (get_local $5) + ) + (tee_local $6 + (get_local $2) + ) + (i32.gt_s + (get_local $3) + (get_local $6) + ) + ) + ) + (if + (i32.lt_s + (grow_memory + (get_local $3) + ) + (i32.const 0) + ) + (if + (i32.lt_s + (grow_memory + (get_local $2) + ) + (i32.const 0) + ) + (unreachable) + ) + ) + ) + ) + (set_global $~lib/allocator/arena/offset + (get_local $4) + ) + (get_local $1) + ) + (func $~lib/memory/memory.allocate (; 1 ;) (type $ii) (param $0 i32) (result i32) + (return + (call $~lib/allocator/arena/__memory_allocate + (get_local $0) + ) + ) + ) + (func $getter-call/C#get:x~anonymous|0 (; 2 ;) (type $i) (result i32) + (i32.const 42) + ) + (func $getter-call/C#get:x (; 3 ;) (type $ii) (param $0 i32) (result i32) + (i32.const 0) + ) + (func $getter-call/test (; 4 ;) (type $i) (result i32) + (local $0 i32) + (set_local $0 + (block (result i32) + (set_local $0 + (call $~lib/memory/memory.allocate + (i32.const 0) + ) + ) + (get_local $0) + ) + ) + (set_global $~argc + (i32.const 0) + ) + (call_indirect (type $i) + (call $getter-call/C#get:x + (get_local $0) + ) + ) + ) + (func $start (; 5 ;) (type $v) + (set_global $~lib/allocator/arena/startOffset + (i32.and + (i32.add + (get_global $HEAP_BASE) + (get_global $~lib/internal/allocator/AL_MASK) + ) + (i32.xor + (get_global $~lib/internal/allocator/AL_MASK) + (i32.const -1) + ) + ) + ) + (set_global $~lib/allocator/arena/offset + (get_global $~lib/allocator/arena/startOffset) + ) + ) +)