Support calling the result of a getter (#252)

This commit is contained in:
Andy Hanson 2018-09-09 18:19:11 -07:00 committed by Daniel Wirtz
parent c27b6e8951
commit 3605630747
3 changed files with 251 additions and 39 deletions

View File

@ -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, (<Field>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(<Property>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 {
(<Field>target).memoryOffset
);
}
case ElementKind.PROPERTY: { // instance property (here: getter)
let prototype = (<Property>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, (<Property>target).simpleName, (<Property>target).parent.toString()
);
return module.createUnreachable();
}
case ElementKind.PROPERTY: {// instance property (here: getter)
return this.compileGetter(<Property>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, (<Property>target).simpleName, (<Property>target).parent.toString()
);
return this.module.createUnreachable();
}
}
compileTernaryExpression(expression: TernaryExpression, contextualType: Type): ExpressionRef {
var ifThen = expression.ifThen;
var ifElse = expression.ifElse;

View File

@ -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();
}

View File

@ -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)
)
)
)