diff --git a/src/compiler.ts b/src/compiler.ts index f2c106f08d..b77e18416f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -530,9 +530,13 @@ export class Compiler extends DiagnosticEmitter { } } var virtualCalls = this.virtualCalls; - for (let _values = Set_values(virtualCalls), i = 0, k = _values.length; i < k; ++i) { - let instance = unchecked(_values[i]); - this.finalizeVirtualStub(instance); + while (virtualCalls.size) { + // finalizing a stub may discover more virtual calls, so do this in a loop + for (let _values = Set_values(virtualCalls), i = 0, k = _values.length; i < k; ++i) { + let instance = unchecked(_values[i]); + this.finalizeVirtualStub(instance); + virtualCalls.delete(instance); + } } // finalize runtime features diff --git a/tests/compiler/class-overloading.optimized.wat b/tests/compiler/class-overloading.optimized.wat index c30108abdd..dfab86c32c 100644 --- a/tests/compiler/class-overloading.optimized.wat +++ b/tests/compiler/class-overloading.optimized.wat @@ -14,11 +14,13 @@ (data (i32.const 1228) "\02\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\02\00\00\00F") (data (i32.const 1260) "\04\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\04\00\00\00I\00B") (data (i32.const 1292) "\04\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\04\00\00\00I\00C") + (data (i32.const 1324) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00n\00o\00t\00 \00i\00m\00p\00l\00e\00m\00e\00n\00t\00e\00d") (global $class-overloading/which (mut i32) (i32.const 1056)) (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) (global $class-overloading/a (mut i32) (i32.const 0)) (global $class-overloading/ia (mut i32) (i32.const 0)) (global $class-overloading/ic (mut i32) (i32.const 0)) + (global $class-overloading/b2 (mut i32) (i32.const 0)) (global $~started (mut i32) (i32.const 0)) (export "_start" (func $~start)) (export "memory" (memory $0)) @@ -264,7 +266,8 @@ call $class-overloading/B#constructor ) (func $start:class-overloading - i32.const 1324 + (local $0 i32) + i32.const 1388 global.set $~lib/rt/stub/offset i32.const 0 call $class-overloading/B#constructor @@ -633,6 +636,53 @@ call $~lib/builtins/abort unreachable end + i32.const 14 + call $~lib/rt/stub/__new + local.tee $0 + if (result i32) + local.get $0 + else + i32.const 13 + call $~lib/rt/stub/__new + end + global.set $class-overloading/b2 + block $__inlined_func$class-overloading/A2#foo@virtual + global.get $class-overloading/b2 + i32.const 8 + i32.sub + i32.load + i32.const 14 + i32.eq + if + i32.const 15 + call $~lib/rt/stub/__new + local.tee $0 + if (result i32) + local.get $0 + else + i32.const 16 + call $~lib/rt/stub/__new + end + i32.const 8 + i32.sub + i32.load + i32.const 15 + i32.eq + br_if $__inlined_func$class-overloading/A2#foo@virtual + i32.const 1344 + i32.const 1152 + i32.const 186 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + i32.const 1344 + i32.const 1152 + i32.const 198 + i32.const 5 + call $~lib/builtins/abort + unreachable + end ) (func $~start global.get $~started diff --git a/tests/compiler/class-overloading.ts b/tests/compiler/class-overloading.ts index d8f9cf1241..ee4aa5a906 100644 --- a/tests/compiler/class-overloading.ts +++ b/tests/compiler/class-overloading.ts @@ -175,3 +175,35 @@ var ic: IC = new CC(); which = ""; ic.foo(); assert(which == "IC"); + +// Should make stubs for functions discovered when compiling other virtual stubs +class A1 { + public bar(): i32 { + return this.baz(); + // 4) discovers A1#baz + } + public baz(): i32 { + throw new Error("not implemented"); + // 5) discovers B1#baz (overload) + } +} +class B1 extends A1 { + public baz(): i32 { + return 3; + // 6) complete + } +} +class A2 { + foo(): i32 { + throw new Error("not implemented"); + // 2) discovers B2#foo (overload) + } +} +class B2 extends A2 { + foo(): i32 { + return new B1().bar(); + // 3) discovers B1#bar (alias of A1#bar) + } +} +var b2: A2 = new B2(); +assert(b2.foo() == 3); // 1) discovers A2#foo diff --git a/tests/compiler/class-overloading.untouched.wat b/tests/compiler/class-overloading.untouched.wat index 09eeaebd7e..24e8560204 100644 --- a/tests/compiler/class-overloading.untouched.wat +++ b/tests/compiler/class-overloading.untouched.wat @@ -16,6 +16,7 @@ (data (i32.const 204) "\02\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\02\00\00\00F\00") (data (i32.const 236) "\04\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\04\00\00\00I\00B\00") (data (i32.const 268) "\04\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\04\00\00\00I\00C\00") + (data (i32.const 300) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00n\00o\00t\00 \00i\00m\00p\00l\00e\00m\00e\00n\00t\00e\00d\00") (table $0 1 funcref) (global $class-overloading/which (mut i32) (i32.const 32)) (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) @@ -25,8 +26,9 @@ (global $class-overloading/c (mut i32) (i32.const 0)) (global $class-overloading/ia (mut i32) (i32.const 0)) (global $class-overloading/ic (mut i32) (i32.const 0)) + (global $class-overloading/b2 (mut i32) (i32.const 0)) (global $~started (mut i32) (i32.const 0)) - (global $~lib/memory/__heap_base i32 (i32.const 292)) + (global $~lib/memory/__heap_base i32 (i32.const 352)) (export "_start" (func $~start)) (export "memory" (memory $0)) (func $~lib/rt/stub/computeSize (param $0 i32) (result i32) @@ -589,6 +591,41 @@ end local.get $0 ) + (func $class-overloading/A2#constructor (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 13 + call $~lib/rt/stub/__new + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $class-overloading/B2#constructor (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 14 + call $~lib/rt/stub/__new + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + call $class-overloading/A2#constructor + local.set $0 + local.get $0 + ) + (func $class-overloading/A2#foo (param $0 i32) (result i32) + i32.const 320 + i32.const 128 + i32.const 198 + i32.const 5 + call $~lib/builtins/abort + unreachable + ) (func $start:class-overloading (local $0 i32) global.get $~lib/memory/__heap_base @@ -1074,6 +1111,22 @@ call $~lib/builtins/abort unreachable end + i32.const 0 + call $class-overloading/B2#constructor + global.set $class-overloading/b2 + global.get $class-overloading/b2 + call $class-overloading/A2#foo@virtual + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 128 + i32.const 209 + i32.const 1 + call $~lib/builtins/abort + unreachable + end ) (func $~start global.get $~started @@ -1402,4 +1455,102 @@ end unreachable ) + (func $class-overloading/A1#constructor (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 16 + call $~lib/rt/stub/__new + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $class-overloading/B1#constructor (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 15 + call $~lib/rt/stub/__new + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + call $class-overloading/A1#constructor + local.set $0 + local.get $0 + ) + (func $class-overloading/A1#baz (param $0 i32) (result i32) + i32.const 320 + i32.const 128 + i32.const 186 + i32.const 5 + call $~lib/builtins/abort + unreachable + ) + (func $class-overloading/A1#bar (param $0 i32) (result i32) + local.get $0 + call $class-overloading/A1#baz@virtual + ) + (func $class-overloading/B2#foo (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + i32.const 0 + call $class-overloading/B1#constructor + local.tee $1 + call $class-overloading/A1#bar + local.set $2 + local.get $1 + call $~lib/rt/stub/__release + local.get $2 + ) + (func $class-overloading/A2#foo@virtual (param $0 i32) (result i32) + (local $1 i32) + block $default + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.set $1 + local.get $1 + i32.const 14 + i32.eq + br_if $case0 + br $default + end + local.get $0 + call $class-overloading/B2#foo + return + end + local.get $0 + call $class-overloading/A2#foo + ) + (func $class-overloading/B1#baz (param $0 i32) (result i32) + i32.const 3 + ) + (func $class-overloading/A1#baz@virtual (param $0 i32) (result i32) + (local $1 i32) + block $default + block $case0 + local.get $0 + i32.const 8 + i32.sub + i32.load + local.set $1 + local.get $1 + i32.const 15 + i32.eq + br_if $case0 + br $default + end + local.get $0 + call $class-overloading/B1#baz + return + end + local.get $0 + call $class-overloading/A1#baz + ) )