Skip to content

Commit 6e3c348

Browse files
committed
Implement calls to 'super()'
1 parent 65c4acd commit 6e3c348

File tree

6 files changed

+484
-2
lines changed

6 files changed

+484
-2
lines changed

src/ast.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ export function nodeIsCallable(kind: NodeKind): bool {
120120
case NodeKind.CALL:
121121
case NodeKind.ELEMENTACCESS:
122122
case NodeKind.PARENTHESIZED:
123-
case NodeKind.PROPERTYACCESS: return true;
123+
case NodeKind.PROPERTYACCESS:
124+
case NodeKind.SUPER: return true;
124125
}
125126
return false;
126127
}

src/compiler.ts

+44
Original file line numberDiff line numberDiff line change
@@ -5230,6 +5230,19 @@ export class Compiler extends DiagnosticEmitter {
52305230
break;
52315231
}
52325232

5233+
// call to `super()`
5234+
case ElementKind.CLASS: {
5235+
if (expression.expression.kind == NodeKind.SUPER) {
5236+
let classInstance = assert(currentFunction.parent);
5237+
assert(classInstance.kind == ElementKind.CLASS);
5238+
let expr = this.compileSuperInstantiate(<Class>classInstance, expression.arguments, expression);
5239+
this.currentType = Type.void;
5240+
let thisLocal = assert(this.currentFunction.flow.getScopedLocal("this"));
5241+
return module.createSetLocal(thisLocal.index, expr);
5242+
}
5243+
// otherwise fall-through
5244+
}
5245+
52335246
// not supported
52345247
default: {
52355248
this.error(
@@ -6658,6 +6671,37 @@ export class Compiler extends DiagnosticEmitter {
66586671
return expr;
66596672
}
66606673

6674+
compileSuperInstantiate(classInstance: Class, argumentExpressions: Expression[], reportNode: Node): ExpressionRef {
6675+
// traverse to the top-most visible constructor (except the current one)
6676+
var currentClassInstance: Class | null = classInstance.base;
6677+
var constructorInstance: Function | null = null;
6678+
while (currentClassInstance) {
6679+
constructorInstance = currentClassInstance.constructorInstance;
6680+
if (constructorInstance) break; // TODO: check visibility
6681+
currentClassInstance = currentClassInstance.base;
6682+
}
6683+
6684+
// if a constructor is present, allocate the necessary memory for `this` and call it
6685+
var expr: ExpressionRef;
6686+
if (constructorInstance) {
6687+
expr = this.compileCallDirect(constructorInstance, argumentExpressions, reportNode,
6688+
this.makeAllocate(classInstance, reportNode)
6689+
);
6690+
6691+
// otherwise simply allocate a new instance and initialize its fields
6692+
} else {
6693+
if (argumentExpressions.length) {
6694+
this.error(
6695+
DiagnosticCode.Expected_0_arguments_but_got_1,
6696+
reportNode.range, "0", argumentExpressions.length.toString(10)
6697+
);
6698+
}
6699+
expr = this.makeAllocate(classInstance, reportNode);
6700+
}
6701+
this.currentType = classInstance.type;
6702+
return expr;
6703+
}
6704+
66616705
compileParenthesizedExpression(
66626706
expression: ParenthesizedExpression,
66636707
contextualType: Type

src/resolver.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1242,8 +1242,16 @@ export class Resolver extends DiagnosticEmitter {
12421242

12431243
// Lay out fields in advance
12441244
case ElementKind.FIELD_PROTOTYPE: {
1245-
if (!instance.members) instance.members = new Map();
12461245
let fieldDeclaration = (<FieldPrototype>member).declaration;
1246+
if (!instance.members) instance.members = new Map();
1247+
else if (instance.members.has(member.simpleName)) {
1248+
this.error(
1249+
DiagnosticCode.Duplicate_identifier_0,
1250+
fieldDeclaration.name.range,
1251+
member.simpleName
1252+
);
1253+
break;
1254+
}
12471255
let fieldType: Type | null = null;
12481256
// TODO: handle duplicate non-private fields
12491257
if (!fieldDeclaration.type) {
+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
(module
2+
(type $v (func))
3+
(type $ii (func (param i32) (result i32)))
4+
(type $iiiiv (func (param i32 i32 i32 i32)))
5+
(type $FUNCSIG$i (func (result i32)))
6+
(import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32)))
7+
(memory $0 1)
8+
(data (i32.const 8) "\0d\00\00\00c\00a\00l\00l\00-\00s\00u\00p\00e\00r\00.\00t\00s")
9+
(table $0 1 anyfunc)
10+
(elem (i32.const 0) $null)
11+
(global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0))
12+
(global $~lib/allocator/arena/offset (mut i32) (i32.const 0))
13+
(export "memory" (memory $0))
14+
(export "table" (table $0))
15+
(start $start)
16+
(func $~lib/allocator/arena/__memory_allocate (; 1 ;) (type $ii) (param $0 i32) (result i32)
17+
(local $1 i32)
18+
(local $2 i32)
19+
(local $3 i32)
20+
get_local $0
21+
i32.const 1073741824
22+
i32.gt_u
23+
if
24+
unreachable
25+
end
26+
get_global $~lib/allocator/arena/offset
27+
tee_local $1
28+
get_local $0
29+
i32.const 1
30+
get_local $0
31+
i32.const 1
32+
i32.gt_u
33+
select
34+
i32.add
35+
i32.const 7
36+
i32.add
37+
i32.const -8
38+
i32.and
39+
tee_local $2
40+
current_memory
41+
tee_local $3
42+
i32.const 16
43+
i32.shl
44+
i32.gt_u
45+
if
46+
get_local $3
47+
get_local $2
48+
get_local $1
49+
i32.sub
50+
i32.const 65535
51+
i32.add
52+
i32.const -65536
53+
i32.and
54+
i32.const 16
55+
i32.shr_u
56+
tee_local $0
57+
get_local $3
58+
get_local $0
59+
i32.gt_s
60+
select
61+
grow_memory
62+
i32.const 0
63+
i32.lt_s
64+
if
65+
get_local $0
66+
grow_memory
67+
i32.const 0
68+
i32.lt_s
69+
if
70+
unreachable
71+
end
72+
end
73+
end
74+
get_local $2
75+
set_global $~lib/allocator/arena/offset
76+
get_local $1
77+
)
78+
(func $call-super/B#constructor (; 2 ;) (type $FUNCSIG$i) (result i32)
79+
(local $0 i32)
80+
i32.const 8
81+
call $~lib/allocator/arena/__memory_allocate
82+
tee_local $0
83+
i32.const 1
84+
i32.store
85+
get_local $0
86+
i32.const 2
87+
i32.store offset=4
88+
get_local $0
89+
i32.eqz
90+
if
91+
i32.const 4
92+
call $~lib/allocator/arena/__memory_allocate
93+
tee_local $0
94+
i32.const 1
95+
i32.store
96+
end
97+
get_local $0
98+
i32.eqz
99+
if
100+
i32.const 8
101+
call $~lib/allocator/arena/__memory_allocate
102+
tee_local $0
103+
i32.const 1
104+
i32.store
105+
get_local $0
106+
i32.const 2
107+
i32.store offset=4
108+
end
109+
get_local $0
110+
i32.load
111+
i32.const 1
112+
i32.ne
113+
if
114+
i32.const 0
115+
i32.const 8
116+
i32.const 13
117+
i32.const 4
118+
call $~lib/env/abort
119+
unreachable
120+
end
121+
get_local $0
122+
i32.load offset=4
123+
i32.const 2
124+
i32.ne
125+
if
126+
i32.const 0
127+
i32.const 8
128+
i32.const 14
129+
i32.const 4
130+
call $~lib/env/abort
131+
unreachable
132+
end
133+
get_local $0
134+
)
135+
(func $call-super/test (; 3 ;) (type $v)
136+
(local $0 i32)
137+
call $call-super/B#constructor
138+
tee_local $0
139+
i32.load
140+
i32.const 1
141+
i32.ne
142+
if
143+
i32.const 0
144+
i32.const 8
145+
i32.const 20
146+
i32.const 2
147+
call $~lib/env/abort
148+
unreachable
149+
end
150+
get_local $0
151+
i32.load offset=4
152+
i32.const 2
153+
i32.ne
154+
if
155+
i32.const 0
156+
i32.const 8
157+
i32.const 21
158+
i32.const 2
159+
call $~lib/env/abort
160+
unreachable
161+
end
162+
)
163+
(func $start (; 4 ;) (type $v)
164+
i32.const 40
165+
set_global $~lib/allocator/arena/startOffset
166+
get_global $~lib/allocator/arena/startOffset
167+
set_global $~lib/allocator/arena/offset
168+
call $call-super/test
169+
)
170+
(func $null (; 5 ;) (type $v)
171+
nop
172+
)
173+
)

tests/compiler/call-super.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import "allocator/arena";
2+
3+
class A {
4+
a: i32 = 1;
5+
constructor() {
6+
}
7+
}
8+
9+
class B extends A {
10+
b: i32 = 2;
11+
constructor() {
12+
super();
13+
assert(this.a == 1);
14+
assert(this.b == 2);
15+
}
16+
}
17+
18+
function test(): void {
19+
var b = new B();
20+
assert(b.a == 1);
21+
assert(b.b == 2);
22+
}
23+
24+
test();

0 commit comments

Comments
 (0)