From d65262056f05742f28706b731435eb01532db9d0 Mon Sep 17 00:00:00 2001
From: Matt Johnson-Pint <mjp@hypermode.com>
Date: Wed, 5 Feb 2025 13:07:28 -0800
Subject: [PATCH 1/5] feat: support 'this' type on methods and properties

---
 src/resolver.ts                        |   43 +-
 tests/compiler/issues/2904.debug.wat   | 2774 ++++++++++++++++++++++++
 tests/compiler/issues/2904.json        |    3 +
 tests/compiler/issues/2904.release.wat | 1659 ++++++++++++++
 tests/compiler/issues/2904.ts          |   37 +
 5 files changed, 4508 insertions(+), 8 deletions(-)
 create mode 100644 tests/compiler/issues/2904.debug.wat
 create mode 100644 tests/compiler/issues/2904.json
 create mode 100644 tests/compiler/issues/2904.release.wat
 create mode 100644 tests/compiler/issues/2904.ts

diff --git a/src/resolver.ts b/src/resolver.ts
index 389d3d8971..10c6725ec7 100644
--- a/src/resolver.ts
+++ b/src/resolver.ts
@@ -748,7 +748,7 @@ export class Resolver extends DiagnosticEmitter {
     }
 
     // otherwise resolve the non-generic call as usual
-    return this.resolveFunction(prototype, null, new Map(), reportMode);
+    return this.resolveFunction(prototype, null, new Map(), reportMode, ctxFlow);
   }
 
   private inferGenericTypeArguments(
@@ -1515,7 +1515,10 @@ export class Resolver extends DiagnosticEmitter {
           let member = classLikeTarget.getMember(propertyName);
           if (member) {
             if (member.kind == ElementKind.PropertyPrototype) {
-              let propertyInstance = this.resolveProperty(<PropertyPrototype>member, reportMode);
+              if (!member.is(CommonFlags.Static)) {
+                this.currentThisExpression = targetNode;
+              }
+              let propertyInstance = this.resolveProperty(<PropertyPrototype>member, reportMode, ctxFlow);
               if (!propertyInstance) return null;
               member = propertyInstance;
               if (propertyInstance.is(CommonFlags.Static)) {
@@ -2851,14 +2854,25 @@ export class Resolver extends DiagnosticEmitter {
     /** Contextual types, i.e. `T`. */
     ctxTypes: Map<string,Type> = new Map(),
     /** How to proceed with eventual diagnostics. */
-    reportMode: ReportMode = ReportMode.Report
+    reportMode: ReportMode = ReportMode.Report,
+    /** Contextual flow. */
+    ctxFlow?: Flow,
   ): Function | null {
     let classInstance: Class | null = null; // if an instance method
     let instanceKey = typeArguments ? typesToString(typeArguments) : "";
 
     // Instance method prototypes are pre-bound to their concrete class as their parent
     if (prototype.is(CommonFlags.Instance)) {
-      classInstance = assert(prototype.getBoundClassOrInterface());
+
+      if (this.currentThisExpression && ctxFlow) {
+        let element = this.lookupExpression(this.currentThisExpression, ctxFlow);
+        if (element?.kind == ElementKind.Class) {
+          classInstance = <Class>element;
+        }
+      }
+      if (!classInstance) {
+        classInstance = assert(prototype.getBoundClassOrInterface());
+      }
 
       // check if this exact concrete class and function combination is known already
       let resolvedInstance = prototype.getResolvedInstance(instanceKey);
@@ -2998,7 +3012,8 @@ export class Resolver extends DiagnosticEmitter {
     prototype.setResolvedInstance(instanceKey, instance);
 
     // check against overridden base member
-    if (classInstance) {
+    if (prototype.is(CommonFlags.Instance)) {
+      let classInstance = assert(prototype.getBoundClassOrInterface());
       let methodOrPropertyName = instance.declaration.name.text;
       let baseClass = classInstance.base;
       if (baseClass) {
@@ -3492,6 +3507,14 @@ export class Resolver extends DiagnosticEmitter {
                   );
                   break;
                 }
+                if (boundPrototype.typeNode?.kind == NodeKind.NamedType && (<NamedTypeNode>boundPrototype.typeNode).name.identifier.text == CommonNames.this_) {
+                  this.error(
+                    DiagnosticCode.Not_implemented_0,
+                    assert(boundPrototype.typeNode).range,
+                    "Polymorphic 'this' typed fields"
+                  );
+                  break;
+                }
                 let needsLayout = true;
                 if (base) {
                   let existingMember = base.getMember(boundPrototype.name);
@@ -3749,7 +3772,9 @@ export class Resolver extends DiagnosticEmitter {
     /** The prototype of the property. */
     prototype: PropertyPrototype,
     /** How to proceed with eventual diagnostics. */
-    reportMode: ReportMode = ReportMode.Report
+    reportMode: ReportMode = ReportMode.Report,
+    /** Contextual flow. */
+    ctxFlow?: Flow,
   ): Property | null {
     let instance = prototype.instance;
     if (instance) return instance;
@@ -3763,7 +3788,8 @@ export class Resolver extends DiagnosticEmitter {
         getterPrototype,
         null,
         new Map(),
-        reportMode
+        reportMode,
+        ctxFlow
       );
       if (getterInstance) {
         instance.getterInstance = getterInstance;
@@ -3776,7 +3802,8 @@ export class Resolver extends DiagnosticEmitter {
         setterPrototype,
         null,
         new Map(),
-        reportMode
+        reportMode,
+        ctxFlow
       );
       if (setterInstance) {
         instance.setterInstance = setterInstance;
diff --git a/tests/compiler/issues/2904.debug.wat b/tests/compiler/issues/2904.debug.wat
new file mode 100644
index 0000000000..f4d1dbeb53
--- /dev/null
+++ b/tests/compiler/issues/2904.debug.wat
@@ -0,0 +1,2774 @@
+(module
+ (type $0 (func (param i32) (result i32)))
+ (type $1 (func (param i32 i32)))
+ (type $2 (func (param i32)))
+ (type $3 (func))
+ (type $4 (func (param i32 i32) (result i32)))
+ (type $5 (func (param i32 i32 i32)))
+ (type $6 (func (param i32 i32 i32 i32)))
+ (type $7 (func (param i32 i32 i64) (result i32)))
+ (type $8 (func (result i32)))
+ (type $9 (func (param i32 i32 i32 i32 i32) (result i32)))
+ (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
+ (global $~lib/rt/itcms/total (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/state (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/iter (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/white (mut i32) (i32.const 0))
+ (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0))
+ (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1))
+ (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2))
+ (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0))
+ (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0))
+ (global $~lib/native/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0))
+ (global $issues/2904/y1 (mut i32) (i32.const 0))
+ (global $~lib/native/ASC_SHRINK_LEVEL i32 (i32.const 0))
+ (global $issues/2904/y2 (mut i32) (i32.const 0))
+ (global $~lib/rt/__rtti_base i32 (i32.const 512))
+ (global $~lib/memory/__data_end i32 (i32.const 540))
+ (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33308))
+ (global $~lib/memory/__heap_base i32 (i32.const 33308))
+ (memory $0 1)
+ (data $0 (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00")
+ (data $1 (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00")
+ (data $2 (i32.const 144) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
+ (data $3 (i32.const 176) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
+ (data $4 (i32.const 204) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00\00\00\00\00\00\00\00\00")
+ (data $5 (i32.const 268) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s\00\00\00\00\00\00\00\00\00")
+ (data $6 (i32.const 320) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
+ (data $7 (i32.const 348) "<\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
+ (data $8 (i32.const 412) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\18\00\00\00Y\00o\00u\00 \00m\00a\00d\00e\00 \00i\00t\00!\00\00\00\00\00")
+ (data $9 (i32.const 460) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\1c\00\00\00i\00s\00s\00u\00e\00s\00/\002\009\000\004\00.\00t\00s\00")
+ (data $10 (i32.const 512) "\06\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00 \00\00\00 \00\00\00")
+ (table $0 1 1 funcref)
+ (elem $0 (i32.const 1))
+ (export "memory" (memory $0))
+ (start $~start)
+ (func $~lib/rt/itcms/Object#set:nextWithColor (param $this i32) (param $nextWithColor i32)
+  local.get $this
+  local.get $nextWithColor
+  i32.store offset=4
+ )
+ (func $~lib/rt/itcms/Object#set:prev (param $this i32) (param $prev i32)
+  local.get $this
+  local.get $prev
+  i32.store offset=8
+ )
+ (func $~lib/rt/itcms/initLazy (param $space i32) (result i32)
+  local.get $space
+  local.get $space
+  call $~lib/rt/itcms/Object#set:nextWithColor
+  local.get $space
+  local.get $space
+  call $~lib/rt/itcms/Object#set:prev
+  local.get $space
+  return
+ )
+ (func $~lib/rt/itcms/Object#get:nextWithColor (param $this i32) (result i32)
+  local.get $this
+  i32.load offset=4
+ )
+ (func $~lib/rt/itcms/Object#get:next (param $this i32) (result i32)
+  local.get $this
+  call $~lib/rt/itcms/Object#get:nextWithColor
+  i32.const 3
+  i32.const -1
+  i32.xor
+  i32.and
+  return
+ )
+ (func $~lib/rt/itcms/Object#get:color (param $this i32) (result i32)
+  local.get $this
+  call $~lib/rt/itcms/Object#get:nextWithColor
+  i32.const 3
+  i32.and
+  return
+ )
+ (func $~lib/rt/itcms/visitRoots (param $cookie i32)
+  (local $pn i32)
+  (local $iter i32)
+  local.get $cookie
+  call $~lib/rt/__visit_globals
+  global.get $~lib/rt/itcms/pinSpace
+  local.set $pn
+  local.get $pn
+  call $~lib/rt/itcms/Object#get:next
+  local.set $iter
+  loop $while-continue|0
+   local.get $iter
+   local.get $pn
+   i32.ne
+   if
+    i32.const 1
+    drop
+    local.get $iter
+    call $~lib/rt/itcms/Object#get:color
+    i32.const 3
+    i32.eq
+    i32.eqz
+    if
+     i32.const 0
+     i32.const 96
+     i32.const 160
+     i32.const 16
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $iter
+    i32.const 20
+    i32.add
+    local.get $cookie
+    call $~lib/rt/__visit_members
+    local.get $iter
+    call $~lib/rt/itcms/Object#get:next
+    local.set $iter
+    br $while-continue|0
+   end
+  end
+ )
+ (func $~lib/rt/itcms/Object#set:color (param $this i32) (param $color i32)
+  local.get $this
+  local.get $this
+  call $~lib/rt/itcms/Object#get:nextWithColor
+  i32.const 3
+  i32.const -1
+  i32.xor
+  i32.and
+  local.get $color
+  i32.or
+  call $~lib/rt/itcms/Object#set:nextWithColor
+ )
+ (func $~lib/rt/itcms/Object#get:prev (param $this i32) (result i32)
+  local.get $this
+  i32.load offset=8
+ )
+ (func $~lib/rt/itcms/Object#set:next (param $this i32) (param $obj i32)
+  local.get $this
+  local.get $obj
+  local.get $this
+  call $~lib/rt/itcms/Object#get:nextWithColor
+  i32.const 3
+  i32.and
+  i32.or
+  call $~lib/rt/itcms/Object#set:nextWithColor
+ )
+ (func $~lib/rt/itcms/Object#unlink (param $this i32)
+  (local $next i32)
+  (local $prev i32)
+  local.get $this
+  call $~lib/rt/itcms/Object#get:next
+  local.set $next
+  local.get $next
+  i32.const 0
+  i32.eq
+  if
+   i32.const 1
+   drop
+   local.get $this
+   call $~lib/rt/itcms/Object#get:prev
+   i32.const 0
+   i32.eq
+   if (result i32)
+    local.get $this
+    global.get $~lib/memory/__heap_base
+    i32.lt_u
+   else
+    i32.const 0
+   end
+   i32.eqz
+   if
+    i32.const 0
+    i32.const 96
+    i32.const 128
+    i32.const 18
+    call $~lib/builtins/abort
+    unreachable
+   end
+   return
+  end
+  local.get $this
+  call $~lib/rt/itcms/Object#get:prev
+  local.set $prev
+  i32.const 1
+  drop
+  local.get $prev
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 96
+   i32.const 132
+   i32.const 16
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $next
+  local.get $prev
+  call $~lib/rt/itcms/Object#set:prev
+  local.get $prev
+  local.get $next
+  call $~lib/rt/itcms/Object#set:next
+ )
+ (func $~lib/rt/itcms/Object#get:rtId (param $this i32) (result i32)
+  local.get $this
+  i32.load offset=12
+ )
+ (func $~lib/shared/typeinfo/Typeinfo#get:flags (param $this i32) (result i32)
+  local.get $this
+  i32.load
+ )
+ (func $~lib/rt/__typeinfo (param $id i32) (result i32)
+  (local $ptr i32)
+  global.get $~lib/rt/__rtti_base
+  local.set $ptr
+  local.get $id
+  local.get $ptr
+  i32.load
+  i32.gt_u
+  if
+   i32.const 224
+   i32.const 288
+   i32.const 21
+   i32.const 28
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $ptr
+  i32.const 4
+  i32.add
+  local.get $id
+  i32.const 4
+  i32.mul
+  i32.add
+  call $~lib/shared/typeinfo/Typeinfo#get:flags
+  return
+ )
+ (func $~lib/rt/itcms/Object#get:isPointerfree (param $this i32) (result i32)
+  (local $rtId i32)
+  local.get $this
+  call $~lib/rt/itcms/Object#get:rtId
+  local.set $rtId
+  local.get $rtId
+  i32.const 2
+  i32.le_u
+  if (result i32)
+   i32.const 1
+  else
+   local.get $rtId
+   call $~lib/rt/__typeinfo
+   i32.const 32
+   i32.and
+   i32.const 0
+   i32.ne
+  end
+  return
+ )
+ (func $~lib/rt/itcms/Object#linkTo (param $this i32) (param $list i32) (param $withColor i32)
+  (local $prev i32)
+  local.get $list
+  call $~lib/rt/itcms/Object#get:prev
+  local.set $prev
+  local.get $this
+  local.get $list
+  local.get $withColor
+  i32.or
+  call $~lib/rt/itcms/Object#set:nextWithColor
+  local.get $this
+  local.get $prev
+  call $~lib/rt/itcms/Object#set:prev
+  local.get $prev
+  local.get $this
+  call $~lib/rt/itcms/Object#set:next
+  local.get $list
+  local.get $this
+  call $~lib/rt/itcms/Object#set:prev
+ )
+ (func $~lib/rt/itcms/Object#makeGray (param $this i32)
+  (local $1 i32)
+  local.get $this
+  global.get $~lib/rt/itcms/iter
+  i32.eq
+  if
+   local.get $this
+   call $~lib/rt/itcms/Object#get:prev
+   local.tee $1
+   i32.eqz
+   if (result i32)
+    i32.const 0
+    i32.const 96
+    i32.const 148
+    i32.const 30
+    call $~lib/builtins/abort
+    unreachable
+   else
+    local.get $1
+   end
+   global.set $~lib/rt/itcms/iter
+  end
+  local.get $this
+  call $~lib/rt/itcms/Object#unlink
+  local.get $this
+  global.get $~lib/rt/itcms/toSpace
+  local.get $this
+  call $~lib/rt/itcms/Object#get:isPointerfree
+  if (result i32)
+   global.get $~lib/rt/itcms/white
+   i32.eqz
+  else
+   i32.const 2
+  end
+  call $~lib/rt/itcms/Object#linkTo
+ )
+ (func $~lib/rt/itcms/__visit (param $ptr i32) (param $cookie i32)
+  (local $obj i32)
+  local.get $ptr
+  i32.eqz
+  if
+   return
+  end
+  local.get $ptr
+  i32.const 20
+  i32.sub
+  local.set $obj
+  i32.const 0
+  drop
+  local.get $obj
+  call $~lib/rt/itcms/Object#get:color
+  global.get $~lib/rt/itcms/white
+  i32.eq
+  if
+   local.get $obj
+   call $~lib/rt/itcms/Object#makeGray
+   global.get $~lib/rt/itcms/visitCount
+   i32.const 1
+   i32.add
+   global.set $~lib/rt/itcms/visitCount
+  end
+ )
+ (func $~lib/rt/itcms/visitStack (param $cookie i32)
+  (local $ptr i32)
+  global.get $~lib/memory/__stack_pointer
+  local.set $ptr
+  loop $while-continue|0
+   local.get $ptr
+   global.get $~lib/memory/__heap_base
+   i32.lt_u
+   if
+    local.get $ptr
+    i32.load
+    local.get $cookie
+    call $~lib/rt/itcms/__visit
+    local.get $ptr
+    i32.const 4
+    i32.add
+    local.set $ptr
+    br $while-continue|0
+   end
+  end
+ )
+ (func $~lib/rt/common/BLOCK#get:mmInfo (param $this i32) (result i32)
+  local.get $this
+  i32.load
+ )
+ (func $~lib/rt/itcms/Object#get:size (param $this i32) (result i32)
+  i32.const 4
+  local.get $this
+  call $~lib/rt/common/BLOCK#get:mmInfo
+  i32.const 3
+  i32.const -1
+  i32.xor
+  i32.and
+  i32.add
+  return
+ )
+ (func $~lib/rt/tlsf/Root#set:flMap (param $this i32) (param $flMap i32)
+  local.get $this
+  local.get $flMap
+  i32.store
+ )
+ (func $~lib/rt/common/BLOCK#set:mmInfo (param $this i32) (param $mmInfo i32)
+  local.get $this
+  local.get $mmInfo
+  i32.store
+ )
+ (func $~lib/rt/tlsf/Block#set:prev (param $this i32) (param $prev i32)
+  local.get $this
+  local.get $prev
+  i32.store offset=4
+ )
+ (func $~lib/rt/tlsf/Block#set:next (param $this i32) (param $next i32)
+  local.get $this
+  local.get $next
+  i32.store offset=8
+ )
+ (func $~lib/rt/tlsf/Block#get:prev (param $this i32) (result i32)
+  local.get $this
+  i32.load offset=4
+ )
+ (func $~lib/rt/tlsf/Block#get:next (param $this i32) (result i32)
+  local.get $this
+  i32.load offset=8
+ )
+ (func $~lib/rt/tlsf/Root#get:flMap (param $this i32) (result i32)
+  local.get $this
+  i32.load
+ )
+ (func $~lib/rt/tlsf/removeBlock (param $root i32) (param $block i32)
+  (local $blockInfo i32)
+  (local $size i32)
+  (local $fl i32)
+  (local $sl i32)
+  (local $6 i32)
+  (local $7 i32)
+  (local $boundedSize i32)
+  (local $prev i32)
+  (local $next i32)
+  (local $root|11 i32)
+  (local $fl|12 i32)
+  (local $sl|13 i32)
+  (local $root|14 i32)
+  (local $fl|15 i32)
+  (local $sl|16 i32)
+  (local $head i32)
+  (local $root|18 i32)
+  (local $fl|19 i32)
+  (local $slMap i32)
+  (local $root|21 i32)
+  (local $fl|22 i32)
+  (local $slMap|23 i32)
+  local.get $block
+  call $~lib/rt/common/BLOCK#get:mmInfo
+  local.set $blockInfo
+  i32.const 1
+  drop
+  local.get $blockInfo
+  i32.const 1
+  i32.and
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 268
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $blockInfo
+  i32.const 3
+  i32.const -1
+  i32.xor
+  i32.and
+  local.set $size
+  i32.const 1
+  drop
+  local.get $size
+  i32.const 12
+  i32.ge_u
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 270
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $size
+  i32.const 256
+  i32.lt_u
+  if
+   i32.const 0
+   local.set $fl
+   local.get $size
+   i32.const 4
+   i32.shr_u
+   local.set $sl
+  else
+   local.get $size
+   local.tee $6
+   i32.const 1073741820
+   local.tee $7
+   local.get $6
+   local.get $7
+   i32.lt_u
+   select
+   local.set $boundedSize
+   i32.const 31
+   local.get $boundedSize
+   i32.clz
+   i32.sub
+   local.set $fl
+   local.get $boundedSize
+   local.get $fl
+   i32.const 4
+   i32.sub
+   i32.shr_u
+   i32.const 1
+   i32.const 4
+   i32.shl
+   i32.xor
+   local.set $sl
+   local.get $fl
+   i32.const 8
+   i32.const 1
+   i32.sub
+   i32.sub
+   local.set $fl
+  end
+  i32.const 1
+  drop
+  local.get $fl
+  i32.const 23
+  i32.lt_u
+  if (result i32)
+   local.get $sl
+   i32.const 16
+   i32.lt_u
+  else
+   i32.const 0
+  end
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 284
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $block
+  call $~lib/rt/tlsf/Block#get:prev
+  local.set $prev
+  local.get $block
+  call $~lib/rt/tlsf/Block#get:next
+  local.set $next
+  local.get $prev
+  if
+   local.get $prev
+   local.get $next
+   call $~lib/rt/tlsf/Block#set:next
+  end
+  local.get $next
+  if
+   local.get $next
+   local.get $prev
+   call $~lib/rt/tlsf/Block#set:prev
+  end
+  local.get $block
+  block $~lib/rt/tlsf/GETHEAD|inlined.0 (result i32)
+   local.get $root
+   local.set $root|11
+   local.get $fl
+   local.set $fl|12
+   local.get $sl
+   local.set $sl|13
+   local.get $root|11
+   local.get $fl|12
+   i32.const 4
+   i32.shl
+   local.get $sl|13
+   i32.add
+   i32.const 2
+   i32.shl
+   i32.add
+   i32.load offset=96
+   br $~lib/rt/tlsf/GETHEAD|inlined.0
+  end
+  i32.eq
+  if
+   local.get $root
+   local.set $root|14
+   local.get $fl
+   local.set $fl|15
+   local.get $sl
+   local.set $sl|16
+   local.get $next
+   local.set $head
+   local.get $root|14
+   local.get $fl|15
+   i32.const 4
+   i32.shl
+   local.get $sl|16
+   i32.add
+   i32.const 2
+   i32.shl
+   i32.add
+   local.get $head
+   i32.store offset=96
+   local.get $next
+   i32.eqz
+   if
+    block $~lib/rt/tlsf/GETSL|inlined.0 (result i32)
+     local.get $root
+     local.set $root|18
+     local.get $fl
+     local.set $fl|19
+     local.get $root|18
+     local.get $fl|19
+     i32.const 2
+     i32.shl
+     i32.add
+     i32.load offset=4
+     br $~lib/rt/tlsf/GETSL|inlined.0
+    end
+    local.set $slMap
+    local.get $root
+    local.set $root|21
+    local.get $fl
+    local.set $fl|22
+    local.get $slMap
+    i32.const 1
+    local.get $sl
+    i32.shl
+    i32.const -1
+    i32.xor
+    i32.and
+    local.tee $slMap
+    local.set $slMap|23
+    local.get $root|21
+    local.get $fl|22
+    i32.const 2
+    i32.shl
+    i32.add
+    local.get $slMap|23
+    i32.store offset=4
+    local.get $slMap
+    i32.eqz
+    if
+     local.get $root
+     local.get $root
+     call $~lib/rt/tlsf/Root#get:flMap
+     i32.const 1
+     local.get $fl
+     i32.shl
+     i32.const -1
+     i32.xor
+     i32.and
+     call $~lib/rt/tlsf/Root#set:flMap
+    end
+   end
+  end
+ )
+ (func $~lib/rt/tlsf/insertBlock (param $root i32) (param $block i32)
+  (local $blockInfo i32)
+  (local $block|3 i32)
+  (local $right i32)
+  (local $rightInfo i32)
+  (local $block|6 i32)
+  (local $block|7 i32)
+  (local $left i32)
+  (local $leftInfo i32)
+  (local $size i32)
+  (local $fl i32)
+  (local $sl i32)
+  (local $13 i32)
+  (local $14 i32)
+  (local $boundedSize i32)
+  (local $root|16 i32)
+  (local $fl|17 i32)
+  (local $sl|18 i32)
+  (local $head i32)
+  (local $root|20 i32)
+  (local $fl|21 i32)
+  (local $sl|22 i32)
+  (local $head|23 i32)
+  (local $root|24 i32)
+  (local $fl|25 i32)
+  (local $root|26 i32)
+  (local $fl|27 i32)
+  (local $slMap i32)
+  i32.const 1
+  drop
+  local.get $block
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 201
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $block
+  call $~lib/rt/common/BLOCK#get:mmInfo
+  local.set $blockInfo
+  i32.const 1
+  drop
+  local.get $blockInfo
+  i32.const 1
+  i32.and
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 203
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  block $~lib/rt/tlsf/GETRIGHT|inlined.0 (result i32)
+   local.get $block
+   local.set $block|3
+   local.get $block|3
+   i32.const 4
+   i32.add
+   local.get $block|3
+   call $~lib/rt/common/BLOCK#get:mmInfo
+   i32.const 3
+   i32.const -1
+   i32.xor
+   i32.and
+   i32.add
+   br $~lib/rt/tlsf/GETRIGHT|inlined.0
+  end
+  local.set $right
+  local.get $right
+  call $~lib/rt/common/BLOCK#get:mmInfo
+  local.set $rightInfo
+  local.get $rightInfo
+  i32.const 1
+  i32.and
+  if
+   local.get $root
+   local.get $right
+   call $~lib/rt/tlsf/removeBlock
+   local.get $block
+   local.get $blockInfo
+   i32.const 4
+   i32.add
+   local.get $rightInfo
+   i32.const 3
+   i32.const -1
+   i32.xor
+   i32.and
+   i32.add
+   local.tee $blockInfo
+   call $~lib/rt/common/BLOCK#set:mmInfo
+   block $~lib/rt/tlsf/GETRIGHT|inlined.1 (result i32)
+    local.get $block
+    local.set $block|6
+    local.get $block|6
+    i32.const 4
+    i32.add
+    local.get $block|6
+    call $~lib/rt/common/BLOCK#get:mmInfo
+    i32.const 3
+    i32.const -1
+    i32.xor
+    i32.and
+    i32.add
+    br $~lib/rt/tlsf/GETRIGHT|inlined.1
+   end
+   local.set $right
+   local.get $right
+   call $~lib/rt/common/BLOCK#get:mmInfo
+   local.set $rightInfo
+  end
+  local.get $blockInfo
+  i32.const 2
+  i32.and
+  if
+   block $~lib/rt/tlsf/GETFREELEFT|inlined.0 (result i32)
+    local.get $block
+    local.set $block|7
+    local.get $block|7
+    i32.const 4
+    i32.sub
+    i32.load
+    br $~lib/rt/tlsf/GETFREELEFT|inlined.0
+   end
+   local.set $left
+   local.get $left
+   call $~lib/rt/common/BLOCK#get:mmInfo
+   local.set $leftInfo
+   i32.const 1
+   drop
+   local.get $leftInfo
+   i32.const 1
+   i32.and
+   i32.eqz
+   if
+    i32.const 0
+    i32.const 368
+    i32.const 221
+    i32.const 16
+    call $~lib/builtins/abort
+    unreachable
+   end
+   local.get $root
+   local.get $left
+   call $~lib/rt/tlsf/removeBlock
+   local.get $left
+   local.set $block
+   local.get $block
+   local.get $leftInfo
+   i32.const 4
+   i32.add
+   local.get $blockInfo
+   i32.const 3
+   i32.const -1
+   i32.xor
+   i32.and
+   i32.add
+   local.tee $blockInfo
+   call $~lib/rt/common/BLOCK#set:mmInfo
+  end
+  local.get $right
+  local.get $rightInfo
+  i32.const 2
+  i32.or
+  call $~lib/rt/common/BLOCK#set:mmInfo
+  local.get $blockInfo
+  i32.const 3
+  i32.const -1
+  i32.xor
+  i32.and
+  local.set $size
+  i32.const 1
+  drop
+  local.get $size
+  i32.const 12
+  i32.ge_u
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 233
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  i32.const 1
+  drop
+  local.get $block
+  i32.const 4
+  i32.add
+  local.get $size
+  i32.add
+  local.get $right
+  i32.eq
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 234
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $right
+  i32.const 4
+  i32.sub
+  local.get $block
+  i32.store
+  local.get $size
+  i32.const 256
+  i32.lt_u
+  if
+   i32.const 0
+   local.set $fl
+   local.get $size
+   i32.const 4
+   i32.shr_u
+   local.set $sl
+  else
+   local.get $size
+   local.tee $13
+   i32.const 1073741820
+   local.tee $14
+   local.get $13
+   local.get $14
+   i32.lt_u
+   select
+   local.set $boundedSize
+   i32.const 31
+   local.get $boundedSize
+   i32.clz
+   i32.sub
+   local.set $fl
+   local.get $boundedSize
+   local.get $fl
+   i32.const 4
+   i32.sub
+   i32.shr_u
+   i32.const 1
+   i32.const 4
+   i32.shl
+   i32.xor
+   local.set $sl
+   local.get $fl
+   i32.const 8
+   i32.const 1
+   i32.sub
+   i32.sub
+   local.set $fl
+  end
+  i32.const 1
+  drop
+  local.get $fl
+  i32.const 23
+  i32.lt_u
+  if (result i32)
+   local.get $sl
+   i32.const 16
+   i32.lt_u
+  else
+   i32.const 0
+  end
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 251
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  block $~lib/rt/tlsf/GETHEAD|inlined.1 (result i32)
+   local.get $root
+   local.set $root|16
+   local.get $fl
+   local.set $fl|17
+   local.get $sl
+   local.set $sl|18
+   local.get $root|16
+   local.get $fl|17
+   i32.const 4
+   i32.shl
+   local.get $sl|18
+   i32.add
+   i32.const 2
+   i32.shl
+   i32.add
+   i32.load offset=96
+   br $~lib/rt/tlsf/GETHEAD|inlined.1
+  end
+  local.set $head
+  local.get $block
+  i32.const 0
+  call $~lib/rt/tlsf/Block#set:prev
+  local.get $block
+  local.get $head
+  call $~lib/rt/tlsf/Block#set:next
+  local.get $head
+  if
+   local.get $head
+   local.get $block
+   call $~lib/rt/tlsf/Block#set:prev
+  end
+  local.get $root
+  local.set $root|20
+  local.get $fl
+  local.set $fl|21
+  local.get $sl
+  local.set $sl|22
+  local.get $block
+  local.set $head|23
+  local.get $root|20
+  local.get $fl|21
+  i32.const 4
+  i32.shl
+  local.get $sl|22
+  i32.add
+  i32.const 2
+  i32.shl
+  i32.add
+  local.get $head|23
+  i32.store offset=96
+  local.get $root
+  local.get $root
+  call $~lib/rt/tlsf/Root#get:flMap
+  i32.const 1
+  local.get $fl
+  i32.shl
+  i32.or
+  call $~lib/rt/tlsf/Root#set:flMap
+  local.get $root
+  local.set $root|26
+  local.get $fl
+  local.set $fl|27
+  block $~lib/rt/tlsf/GETSL|inlined.1 (result i32)
+   local.get $root
+   local.set $root|24
+   local.get $fl
+   local.set $fl|25
+   local.get $root|24
+   local.get $fl|25
+   i32.const 2
+   i32.shl
+   i32.add
+   i32.load offset=4
+   br $~lib/rt/tlsf/GETSL|inlined.1
+  end
+  i32.const 1
+  local.get $sl
+  i32.shl
+  i32.or
+  local.set $slMap
+  local.get $root|26
+  local.get $fl|27
+  i32.const 2
+  i32.shl
+  i32.add
+  local.get $slMap
+  i32.store offset=4
+ )
+ (func $~lib/rt/tlsf/addMemory (param $root i32) (param $start i32) (param $endU64 i64) (result i32)
+  (local $end i32)
+  (local $root|4 i32)
+  (local $tail i32)
+  (local $tailInfo i32)
+  (local $size i32)
+  (local $leftSize i32)
+  (local $left i32)
+  (local $root|10 i32)
+  (local $tail|11 i32)
+  local.get $endU64
+  i32.wrap_i64
+  local.set $end
+  i32.const 1
+  drop
+  local.get $start
+  i64.extend_i32_u
+  local.get $endU64
+  i64.le_u
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 382
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $start
+  i32.const 4
+  i32.add
+  i32.const 15
+  i32.add
+  i32.const 15
+  i32.const -1
+  i32.xor
+  i32.and
+  i32.const 4
+  i32.sub
+  local.set $start
+  local.get $end
+  i32.const 15
+  i32.const -1
+  i32.xor
+  i32.and
+  local.set $end
+  block $~lib/rt/tlsf/GETTAIL|inlined.0 (result i32)
+   local.get $root
+   local.set $root|4
+   local.get $root|4
+   i32.load offset=1568
+   br $~lib/rt/tlsf/GETTAIL|inlined.0
+  end
+  local.set $tail
+  i32.const 0
+  local.set $tailInfo
+  local.get $tail
+  if
+   i32.const 1
+   drop
+   local.get $start
+   local.get $tail
+   i32.const 4
+   i32.add
+   i32.ge_u
+   i32.eqz
+   if
+    i32.const 0
+    i32.const 368
+    i32.const 389
+    i32.const 16
+    call $~lib/builtins/abort
+    unreachable
+   end
+   local.get $start
+   i32.const 16
+   i32.sub
+   local.get $tail
+   i32.eq
+   if
+    local.get $start
+    i32.const 16
+    i32.sub
+    local.set $start
+    local.get $tail
+    call $~lib/rt/common/BLOCK#get:mmInfo
+    local.set $tailInfo
+   else
+   end
+  else
+   i32.const 1
+   drop
+   local.get $start
+   local.get $root
+   i32.const 1572
+   i32.add
+   i32.ge_u
+   i32.eqz
+   if
+    i32.const 0
+    i32.const 368
+    i32.const 402
+    i32.const 5
+    call $~lib/builtins/abort
+    unreachable
+   end
+  end
+  local.get $end
+  local.get $start
+  i32.sub
+  local.set $size
+  local.get $size
+  i32.const 4
+  i32.const 12
+  i32.add
+  i32.const 4
+  i32.add
+  i32.lt_u
+  if
+   i32.const 0
+   return
+  end
+  local.get $size
+  i32.const 2
+  i32.const 4
+  i32.mul
+  i32.sub
+  local.set $leftSize
+  local.get $start
+  local.set $left
+  local.get $left
+  local.get $leftSize
+  i32.const 1
+  i32.or
+  local.get $tailInfo
+  i32.const 2
+  i32.and
+  i32.or
+  call $~lib/rt/common/BLOCK#set:mmInfo
+  local.get $left
+  i32.const 0
+  call $~lib/rt/tlsf/Block#set:prev
+  local.get $left
+  i32.const 0
+  call $~lib/rt/tlsf/Block#set:next
+  local.get $start
+  i32.const 4
+  i32.add
+  local.get $leftSize
+  i32.add
+  local.set $tail
+  local.get $tail
+  i32.const 0
+  i32.const 2
+  i32.or
+  call $~lib/rt/common/BLOCK#set:mmInfo
+  local.get $root
+  local.set $root|10
+  local.get $tail
+  local.set $tail|11
+  local.get $root|10
+  local.get $tail|11
+  i32.store offset=1568
+  local.get $root
+  local.get $left
+  call $~lib/rt/tlsf/insertBlock
+  i32.const 1
+  return
+ )
+ (func $~lib/rt/tlsf/initialize
+  (local $rootOffset i32)
+  (local $pagesBefore i32)
+  (local $pagesNeeded i32)
+  (local $root i32)
+  (local $root|4 i32)
+  (local $tail i32)
+  (local $fl i32)
+  (local $root|7 i32)
+  (local $fl|8 i32)
+  (local $slMap i32)
+  (local $sl i32)
+  (local $root|11 i32)
+  (local $fl|12 i32)
+  (local $sl|13 i32)
+  (local $head i32)
+  (local $memStart i32)
+  i32.const 0
+  drop
+  global.get $~lib/memory/__heap_base
+  i32.const 15
+  i32.add
+  i32.const 15
+  i32.const -1
+  i32.xor
+  i32.and
+  local.set $rootOffset
+  memory.size
+  local.set $pagesBefore
+  local.get $rootOffset
+  i32.const 1572
+  i32.add
+  i32.const 65535
+  i32.add
+  i32.const 65535
+  i32.const -1
+  i32.xor
+  i32.and
+  i32.const 16
+  i32.shr_u
+  local.set $pagesNeeded
+  local.get $pagesNeeded
+  local.get $pagesBefore
+  i32.gt_s
+  if (result i32)
+   local.get $pagesNeeded
+   local.get $pagesBefore
+   i32.sub
+   memory.grow
+   i32.const 0
+   i32.lt_s
+  else
+   i32.const 0
+  end
+  if
+   unreachable
+  end
+  local.get $rootOffset
+  local.set $root
+  local.get $root
+  i32.const 0
+  call $~lib/rt/tlsf/Root#set:flMap
+  local.get $root
+  local.set $root|4
+  i32.const 0
+  local.set $tail
+  local.get $root|4
+  local.get $tail
+  i32.store offset=1568
+  i32.const 0
+  local.set $fl
+  loop $for-loop|0
+   local.get $fl
+   i32.const 23
+   i32.lt_u
+   if
+    local.get $root
+    local.set $root|7
+    local.get $fl
+    local.set $fl|8
+    i32.const 0
+    local.set $slMap
+    local.get $root|7
+    local.get $fl|8
+    i32.const 2
+    i32.shl
+    i32.add
+    local.get $slMap
+    i32.store offset=4
+    i32.const 0
+    local.set $sl
+    loop $for-loop|1
+     local.get $sl
+     i32.const 16
+     i32.lt_u
+     if
+      local.get $root
+      local.set $root|11
+      local.get $fl
+      local.set $fl|12
+      local.get $sl
+      local.set $sl|13
+      i32.const 0
+      local.set $head
+      local.get $root|11
+      local.get $fl|12
+      i32.const 4
+      i32.shl
+      local.get $sl|13
+      i32.add
+      i32.const 2
+      i32.shl
+      i32.add
+      local.get $head
+      i32.store offset=96
+      local.get $sl
+      i32.const 1
+      i32.add
+      local.set $sl
+      br $for-loop|1
+     end
+    end
+    local.get $fl
+    i32.const 1
+    i32.add
+    local.set $fl
+    br $for-loop|0
+   end
+  end
+  local.get $rootOffset
+  i32.const 1572
+  i32.add
+  local.set $memStart
+  i32.const 0
+  drop
+  local.get $root
+  local.get $memStart
+  memory.size
+  i64.extend_i32_s
+  i64.const 16
+  i64.shl
+  call $~lib/rt/tlsf/addMemory
+  drop
+  local.get $root
+  global.set $~lib/rt/tlsf/ROOT
+ )
+ (func $~lib/rt/tlsf/checkUsedBlock (param $ptr i32) (result i32)
+  (local $block i32)
+  local.get $ptr
+  i32.const 4
+  i32.sub
+  local.set $block
+  local.get $ptr
+  i32.const 0
+  i32.ne
+  if (result i32)
+   local.get $ptr
+   i32.const 15
+   i32.and
+   i32.eqz
+  else
+   i32.const 0
+  end
+  if (result i32)
+   local.get $block
+   call $~lib/rt/common/BLOCK#get:mmInfo
+   i32.const 1
+   i32.and
+   i32.eqz
+  else
+   i32.const 0
+  end
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 562
+   i32.const 3
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $block
+  return
+ )
+ (func $~lib/rt/tlsf/freeBlock (param $root i32) (param $block i32)
+  i32.const 0
+  drop
+  local.get $block
+  local.get $block
+  call $~lib/rt/common/BLOCK#get:mmInfo
+  i32.const 1
+  i32.or
+  call $~lib/rt/common/BLOCK#set:mmInfo
+  local.get $root
+  local.get $block
+  call $~lib/rt/tlsf/insertBlock
+ )
+ (func $~lib/rt/tlsf/__free (param $ptr i32)
+  local.get $ptr
+  global.get $~lib/memory/__heap_base
+  i32.lt_u
+  if
+   return
+  end
+  global.get $~lib/rt/tlsf/ROOT
+  i32.eqz
+  if
+   call $~lib/rt/tlsf/initialize
+  end
+  global.get $~lib/rt/tlsf/ROOT
+  local.get $ptr
+  call $~lib/rt/tlsf/checkUsedBlock
+  call $~lib/rt/tlsf/freeBlock
+ )
+ (func $~lib/rt/itcms/free (param $obj i32)
+  local.get $obj
+  global.get $~lib/memory/__heap_base
+  i32.lt_u
+  if
+   local.get $obj
+   i32.const 0
+   call $~lib/rt/itcms/Object#set:nextWithColor
+   local.get $obj
+   i32.const 0
+   call $~lib/rt/itcms/Object#set:prev
+  else
+   global.get $~lib/rt/itcms/total
+   local.get $obj
+   call $~lib/rt/itcms/Object#get:size
+   i32.sub
+   global.set $~lib/rt/itcms/total
+   i32.const 0
+   drop
+   local.get $obj
+   i32.const 4
+   i32.add
+   call $~lib/rt/tlsf/__free
+  end
+ )
+ (func $~lib/rt/itcms/step (result i32)
+  (local $obj i32)
+  (local $1 i32)
+  (local $black i32)
+  (local $from i32)
+  block $break|0
+   block $case2|0
+    block $case1|0
+     block $case0|0
+      global.get $~lib/rt/itcms/state
+      local.set $1
+      local.get $1
+      i32.const 0
+      i32.eq
+      br_if $case0|0
+      local.get $1
+      i32.const 1
+      i32.eq
+      br_if $case1|0
+      local.get $1
+      i32.const 2
+      i32.eq
+      br_if $case2|0
+      br $break|0
+     end
+     i32.const 1
+     global.set $~lib/rt/itcms/state
+     i32.const 0
+     global.set $~lib/rt/itcms/visitCount
+     i32.const 0
+     call $~lib/rt/itcms/visitRoots
+     global.get $~lib/rt/itcms/toSpace
+     global.set $~lib/rt/itcms/iter
+     global.get $~lib/rt/itcms/visitCount
+     i32.const 1
+     i32.mul
+     return
+    end
+    global.get $~lib/rt/itcms/white
+    i32.eqz
+    local.set $black
+    global.get $~lib/rt/itcms/iter
+    call $~lib/rt/itcms/Object#get:next
+    local.set $obj
+    loop $while-continue|1
+     local.get $obj
+     global.get $~lib/rt/itcms/toSpace
+     i32.ne
+     if
+      local.get $obj
+      global.set $~lib/rt/itcms/iter
+      local.get $obj
+      call $~lib/rt/itcms/Object#get:color
+      local.get $black
+      i32.ne
+      if
+       local.get $obj
+       local.get $black
+       call $~lib/rt/itcms/Object#set:color
+       i32.const 0
+       global.set $~lib/rt/itcms/visitCount
+       local.get $obj
+       i32.const 20
+       i32.add
+       i32.const 0
+       call $~lib/rt/__visit_members
+       global.get $~lib/rt/itcms/visitCount
+       i32.const 1
+       i32.mul
+       return
+      end
+      local.get $obj
+      call $~lib/rt/itcms/Object#get:next
+      local.set $obj
+      br $while-continue|1
+     end
+    end
+    i32.const 0
+    global.set $~lib/rt/itcms/visitCount
+    i32.const 0
+    call $~lib/rt/itcms/visitRoots
+    global.get $~lib/rt/itcms/iter
+    call $~lib/rt/itcms/Object#get:next
+    local.set $obj
+    local.get $obj
+    global.get $~lib/rt/itcms/toSpace
+    i32.eq
+    if
+     i32.const 0
+     call $~lib/rt/itcms/visitStack
+     global.get $~lib/rt/itcms/iter
+     call $~lib/rt/itcms/Object#get:next
+     local.set $obj
+     loop $while-continue|2
+      local.get $obj
+      global.get $~lib/rt/itcms/toSpace
+      i32.ne
+      if
+       local.get $obj
+       call $~lib/rt/itcms/Object#get:color
+       local.get $black
+       i32.ne
+       if
+        local.get $obj
+        local.get $black
+        call $~lib/rt/itcms/Object#set:color
+        local.get $obj
+        i32.const 20
+        i32.add
+        i32.const 0
+        call $~lib/rt/__visit_members
+       end
+       local.get $obj
+       call $~lib/rt/itcms/Object#get:next
+       local.set $obj
+       br $while-continue|2
+      end
+     end
+     global.get $~lib/rt/itcms/fromSpace
+     local.set $from
+     global.get $~lib/rt/itcms/toSpace
+     global.set $~lib/rt/itcms/fromSpace
+     local.get $from
+     global.set $~lib/rt/itcms/toSpace
+     local.get $black
+     global.set $~lib/rt/itcms/white
+     local.get $from
+     call $~lib/rt/itcms/Object#get:next
+     global.set $~lib/rt/itcms/iter
+     i32.const 2
+     global.set $~lib/rt/itcms/state
+    end
+    global.get $~lib/rt/itcms/visitCount
+    i32.const 1
+    i32.mul
+    return
+   end
+   global.get $~lib/rt/itcms/iter
+   local.set $obj
+   local.get $obj
+   global.get $~lib/rt/itcms/toSpace
+   i32.ne
+   if
+    local.get $obj
+    call $~lib/rt/itcms/Object#get:next
+    global.set $~lib/rt/itcms/iter
+    i32.const 1
+    drop
+    local.get $obj
+    call $~lib/rt/itcms/Object#get:color
+    global.get $~lib/rt/itcms/white
+    i32.eqz
+    i32.eq
+    i32.eqz
+    if
+     i32.const 0
+     i32.const 96
+     i32.const 229
+     i32.const 20
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $obj
+    call $~lib/rt/itcms/free
+    i32.const 10
+    return
+   end
+   global.get $~lib/rt/itcms/toSpace
+   global.get $~lib/rt/itcms/toSpace
+   call $~lib/rt/itcms/Object#set:nextWithColor
+   global.get $~lib/rt/itcms/toSpace
+   global.get $~lib/rt/itcms/toSpace
+   call $~lib/rt/itcms/Object#set:prev
+   i32.const 0
+   global.set $~lib/rt/itcms/state
+   br $break|0
+  end
+  i32.const 0
+  return
+ )
+ (func $~lib/rt/itcms/interrupt
+  (local $budget i32)
+  i32.const 0
+  drop
+  i32.const 0
+  drop
+  i32.const 1024
+  i32.const 200
+  i32.mul
+  i32.const 100
+  i32.div_u
+  local.set $budget
+  loop $do-loop|0
+   local.get $budget
+   call $~lib/rt/itcms/step
+   i32.sub
+   local.set $budget
+   global.get $~lib/rt/itcms/state
+   i32.const 0
+   i32.eq
+   if
+    i32.const 0
+    drop
+    global.get $~lib/rt/itcms/total
+    i64.extend_i32_u
+    i32.const 200
+    i64.extend_i32_u
+    i64.mul
+    i64.const 100
+    i64.div_u
+    i32.wrap_i64
+    i32.const 1024
+    i32.add
+    global.set $~lib/rt/itcms/threshold
+    i32.const 0
+    drop
+    return
+   end
+   local.get $budget
+   i32.const 0
+   i32.gt_s
+   br_if $do-loop|0
+  end
+  i32.const 0
+  drop
+  global.get $~lib/rt/itcms/total
+  i32.const 1024
+  global.get $~lib/rt/itcms/total
+  global.get $~lib/rt/itcms/threshold
+  i32.sub
+  i32.const 1024
+  i32.lt_u
+  i32.mul
+  i32.add
+  global.set $~lib/rt/itcms/threshold
+  i32.const 0
+  drop
+ )
+ (func $~lib/rt/tlsf/computeSize (param $size i32) (result i32)
+  local.get $size
+  i32.const 12
+  i32.le_u
+  if (result i32)
+   i32.const 12
+  else
+   local.get $size
+   i32.const 4
+   i32.add
+   i32.const 15
+   i32.add
+   i32.const 15
+   i32.const -1
+   i32.xor
+   i32.and
+   i32.const 4
+   i32.sub
+  end
+  return
+ )
+ (func $~lib/rt/tlsf/prepareSize (param $size i32) (result i32)
+  local.get $size
+  i32.const 1073741820
+  i32.gt_u
+  if
+   i32.const 32
+   i32.const 368
+   i32.const 461
+   i32.const 29
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $size
+  call $~lib/rt/tlsf/computeSize
+  return
+ )
+ (func $~lib/rt/tlsf/roundSize (param $size i32) (result i32)
+  local.get $size
+  i32.const 536870910
+  i32.lt_u
+  if (result i32)
+   local.get $size
+   i32.const 1
+   i32.const 27
+   local.get $size
+   i32.clz
+   i32.sub
+   i32.shl
+   i32.add
+   i32.const 1
+   i32.sub
+  else
+   local.get $size
+  end
+  return
+ )
+ (func $~lib/rt/tlsf/searchBlock (param $root i32) (param $size i32) (result i32)
+  (local $fl i32)
+  (local $sl i32)
+  (local $requestSize i32)
+  (local $root|5 i32)
+  (local $fl|6 i32)
+  (local $slMap i32)
+  (local $head i32)
+  (local $flMap i32)
+  (local $root|10 i32)
+  (local $fl|11 i32)
+  (local $root|12 i32)
+  (local $fl|13 i32)
+  (local $sl|14 i32)
+  (local $root|15 i32)
+  (local $fl|16 i32)
+  (local $sl|17 i32)
+  local.get $size
+  i32.const 256
+  i32.lt_u
+  if
+   i32.const 0
+   local.set $fl
+   local.get $size
+   i32.const 4
+   i32.shr_u
+   local.set $sl
+  else
+   local.get $size
+   call $~lib/rt/tlsf/roundSize
+   local.set $requestSize
+   i32.const 4
+   i32.const 8
+   i32.mul
+   i32.const 1
+   i32.sub
+   local.get $requestSize
+   i32.clz
+   i32.sub
+   local.set $fl
+   local.get $requestSize
+   local.get $fl
+   i32.const 4
+   i32.sub
+   i32.shr_u
+   i32.const 1
+   i32.const 4
+   i32.shl
+   i32.xor
+   local.set $sl
+   local.get $fl
+   i32.const 8
+   i32.const 1
+   i32.sub
+   i32.sub
+   local.set $fl
+  end
+  i32.const 1
+  drop
+  local.get $fl
+  i32.const 23
+  i32.lt_u
+  if (result i32)
+   local.get $sl
+   i32.const 16
+   i32.lt_u
+  else
+   i32.const 0
+  end
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 334
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  block $~lib/rt/tlsf/GETSL|inlined.2 (result i32)
+   local.get $root
+   local.set $root|5
+   local.get $fl
+   local.set $fl|6
+   local.get $root|5
+   local.get $fl|6
+   i32.const 2
+   i32.shl
+   i32.add
+   i32.load offset=4
+   br $~lib/rt/tlsf/GETSL|inlined.2
+  end
+  i32.const 0
+  i32.const -1
+  i32.xor
+  local.get $sl
+  i32.shl
+  i32.and
+  local.set $slMap
+  i32.const 0
+  local.set $head
+  local.get $slMap
+  i32.eqz
+  if
+   local.get $root
+   call $~lib/rt/tlsf/Root#get:flMap
+   i32.const 0
+   i32.const -1
+   i32.xor
+   local.get $fl
+   i32.const 1
+   i32.add
+   i32.shl
+   i32.and
+   local.set $flMap
+   local.get $flMap
+   i32.eqz
+   if
+    i32.const 0
+    local.set $head
+   else
+    local.get $flMap
+    i32.ctz
+    local.set $fl
+    block $~lib/rt/tlsf/GETSL|inlined.3 (result i32)
+     local.get $root
+     local.set $root|10
+     local.get $fl
+     local.set $fl|11
+     local.get $root|10
+     local.get $fl|11
+     i32.const 2
+     i32.shl
+     i32.add
+     i32.load offset=4
+     br $~lib/rt/tlsf/GETSL|inlined.3
+    end
+    local.set $slMap
+    i32.const 1
+    drop
+    local.get $slMap
+    i32.eqz
+    if
+     i32.const 0
+     i32.const 368
+     i32.const 347
+     i32.const 18
+     call $~lib/builtins/abort
+     unreachable
+    end
+    block $~lib/rt/tlsf/GETHEAD|inlined.2 (result i32)
+     local.get $root
+     local.set $root|12
+     local.get $fl
+     local.set $fl|13
+     local.get $slMap
+     i32.ctz
+     local.set $sl|14
+     local.get $root|12
+     local.get $fl|13
+     i32.const 4
+     i32.shl
+     local.get $sl|14
+     i32.add
+     i32.const 2
+     i32.shl
+     i32.add
+     i32.load offset=96
+     br $~lib/rt/tlsf/GETHEAD|inlined.2
+    end
+    local.set $head
+   end
+  else
+   block $~lib/rt/tlsf/GETHEAD|inlined.3 (result i32)
+    local.get $root
+    local.set $root|15
+    local.get $fl
+    local.set $fl|16
+    local.get $slMap
+    i32.ctz
+    local.set $sl|17
+    local.get $root|15
+    local.get $fl|16
+    i32.const 4
+    i32.shl
+    local.get $sl|17
+    i32.add
+    i32.const 2
+    i32.shl
+    i32.add
+    i32.load offset=96
+    br $~lib/rt/tlsf/GETHEAD|inlined.3
+   end
+   local.set $head
+  end
+  local.get $head
+  return
+ )
+ (func $~lib/rt/tlsf/growMemory (param $root i32) (param $size i32)
+  (local $pagesBefore i32)
+  (local $root|3 i32)
+  (local $pagesNeeded i32)
+  (local $5 i32)
+  (local $6 i32)
+  (local $pagesWanted i32)
+  (local $pagesAfter i32)
+  i32.const 0
+  drop
+  local.get $size
+  i32.const 256
+  i32.ge_u
+  if
+   local.get $size
+   call $~lib/rt/tlsf/roundSize
+   local.set $size
+  end
+  memory.size
+  local.set $pagesBefore
+  local.get $size
+  i32.const 4
+  local.get $pagesBefore
+  i32.const 16
+  i32.shl
+  i32.const 4
+  i32.sub
+  block $~lib/rt/tlsf/GETTAIL|inlined.1 (result i32)
+   local.get $root
+   local.set $root|3
+   local.get $root|3
+   i32.load offset=1568
+   br $~lib/rt/tlsf/GETTAIL|inlined.1
+  end
+  i32.ne
+  i32.shl
+  i32.add
+  local.set $size
+  local.get $size
+  i32.const 65535
+  i32.add
+  i32.const 65535
+  i32.const -1
+  i32.xor
+  i32.and
+  i32.const 16
+  i32.shr_u
+  local.set $pagesNeeded
+  local.get $pagesBefore
+  local.tee $5
+  local.get $pagesNeeded
+  local.tee $6
+  local.get $5
+  local.get $6
+  i32.gt_s
+  select
+  local.set $pagesWanted
+  local.get $pagesWanted
+  memory.grow
+  i32.const 0
+  i32.lt_s
+  if
+   local.get $pagesNeeded
+   memory.grow
+   i32.const 0
+   i32.lt_s
+   if
+    unreachable
+   end
+  end
+  memory.size
+  local.set $pagesAfter
+  local.get $root
+  local.get $pagesBefore
+  i32.const 16
+  i32.shl
+  local.get $pagesAfter
+  i64.extend_i32_s
+  i64.const 16
+  i64.shl
+  call $~lib/rt/tlsf/addMemory
+  drop
+ )
+ (func $~lib/rt/tlsf/prepareBlock (param $root i32) (param $block i32) (param $size i32)
+  (local $blockInfo i32)
+  (local $remaining i32)
+  (local $spare i32)
+  (local $block|6 i32)
+  (local $block|7 i32)
+  local.get $block
+  call $~lib/rt/common/BLOCK#get:mmInfo
+  local.set $blockInfo
+  i32.const 1
+  drop
+  local.get $size
+  i32.const 4
+  i32.add
+  i32.const 15
+  i32.and
+  i32.eqz
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 361
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $blockInfo
+  i32.const 3
+  i32.const -1
+  i32.xor
+  i32.and
+  local.get $size
+  i32.sub
+  local.set $remaining
+  local.get $remaining
+  i32.const 4
+  i32.const 12
+  i32.add
+  i32.ge_u
+  if
+   local.get $block
+   local.get $size
+   local.get $blockInfo
+   i32.const 2
+   i32.and
+   i32.or
+   call $~lib/rt/common/BLOCK#set:mmInfo
+   local.get $block
+   i32.const 4
+   i32.add
+   local.get $size
+   i32.add
+   local.set $spare
+   local.get $spare
+   local.get $remaining
+   i32.const 4
+   i32.sub
+   i32.const 1
+   i32.or
+   call $~lib/rt/common/BLOCK#set:mmInfo
+   local.get $root
+   local.get $spare
+   call $~lib/rt/tlsf/insertBlock
+  else
+   local.get $block
+   local.get $blockInfo
+   i32.const 1
+   i32.const -1
+   i32.xor
+   i32.and
+   call $~lib/rt/common/BLOCK#set:mmInfo
+   block $~lib/rt/tlsf/GETRIGHT|inlined.3 (result i32)
+    local.get $block
+    local.set $block|7
+    local.get $block|7
+    i32.const 4
+    i32.add
+    local.get $block|7
+    call $~lib/rt/common/BLOCK#get:mmInfo
+    i32.const 3
+    i32.const -1
+    i32.xor
+    i32.and
+    i32.add
+    br $~lib/rt/tlsf/GETRIGHT|inlined.3
+   end
+   block $~lib/rt/tlsf/GETRIGHT|inlined.2 (result i32)
+    local.get $block
+    local.set $block|6
+    local.get $block|6
+    i32.const 4
+    i32.add
+    local.get $block|6
+    call $~lib/rt/common/BLOCK#get:mmInfo
+    i32.const 3
+    i32.const -1
+    i32.xor
+    i32.and
+    i32.add
+    br $~lib/rt/tlsf/GETRIGHT|inlined.2
+   end
+   call $~lib/rt/common/BLOCK#get:mmInfo
+   i32.const 2
+   i32.const -1
+   i32.xor
+   i32.and
+   call $~lib/rt/common/BLOCK#set:mmInfo
+  end
+ )
+ (func $~lib/rt/tlsf/allocateBlock (param $root i32) (param $size i32) (result i32)
+  (local $payloadSize i32)
+  (local $block i32)
+  local.get $size
+  call $~lib/rt/tlsf/prepareSize
+  local.set $payloadSize
+  local.get $root
+  local.get $payloadSize
+  call $~lib/rt/tlsf/searchBlock
+  local.set $block
+  local.get $block
+  i32.eqz
+  if
+   local.get $root
+   local.get $payloadSize
+   call $~lib/rt/tlsf/growMemory
+   local.get $root
+   local.get $payloadSize
+   call $~lib/rt/tlsf/searchBlock
+   local.set $block
+   i32.const 1
+   drop
+   local.get $block
+   i32.eqz
+   if
+    i32.const 0
+    i32.const 368
+    i32.const 499
+    i32.const 16
+    call $~lib/builtins/abort
+    unreachable
+   end
+  end
+  i32.const 1
+  drop
+  local.get $block
+  call $~lib/rt/common/BLOCK#get:mmInfo
+  i32.const 3
+  i32.const -1
+  i32.xor
+  i32.and
+  local.get $payloadSize
+  i32.ge_u
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 368
+   i32.const 501
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $root
+  local.get $block
+  call $~lib/rt/tlsf/removeBlock
+  local.get $root
+  local.get $block
+  local.get $payloadSize
+  call $~lib/rt/tlsf/prepareBlock
+  i32.const 0
+  drop
+  local.get $block
+  return
+ )
+ (func $~lib/rt/tlsf/__alloc (param $size i32) (result i32)
+  global.get $~lib/rt/tlsf/ROOT
+  i32.eqz
+  if
+   call $~lib/rt/tlsf/initialize
+  end
+  global.get $~lib/rt/tlsf/ROOT
+  local.get $size
+  call $~lib/rt/tlsf/allocateBlock
+  i32.const 4
+  i32.add
+  return
+ )
+ (func $~lib/rt/itcms/Object#set:rtId (param $this i32) (param $rtId i32)
+  local.get $this
+  local.get $rtId
+  i32.store offset=12
+ )
+ (func $~lib/rt/itcms/Object#set:rtSize (param $this i32) (param $rtSize i32)
+  local.get $this
+  local.get $rtSize
+  i32.store offset=16
+ )
+ (func $~lib/rt/itcms/__new (param $size i32) (param $id i32) (result i32)
+  (local $obj i32)
+  (local $ptr i32)
+  local.get $size
+  i32.const 1073741804
+  i32.ge_u
+  if
+   i32.const 32
+   i32.const 96
+   i32.const 261
+   i32.const 31
+   call $~lib/builtins/abort
+   unreachable
+  end
+  global.get $~lib/rt/itcms/total
+  global.get $~lib/rt/itcms/threshold
+  i32.ge_u
+  if
+   call $~lib/rt/itcms/interrupt
+  end
+  i32.const 16
+  local.get $size
+  i32.add
+  call $~lib/rt/tlsf/__alloc
+  i32.const 4
+  i32.sub
+  local.set $obj
+  local.get $obj
+  local.get $id
+  call $~lib/rt/itcms/Object#set:rtId
+  local.get $obj
+  local.get $size
+  call $~lib/rt/itcms/Object#set:rtSize
+  local.get $obj
+  global.get $~lib/rt/itcms/fromSpace
+  global.get $~lib/rt/itcms/white
+  call $~lib/rt/itcms/Object#linkTo
+  global.get $~lib/rt/itcms/total
+  local.get $obj
+  call $~lib/rt/itcms/Object#get:size
+  i32.add
+  global.set $~lib/rt/itcms/total
+  local.get $obj
+  i32.const 20
+  i32.add
+  local.set $ptr
+  local.get $ptr
+  i32.const 0
+  local.get $size
+  memory.fill
+  local.get $ptr
+  return
+ )
+ (func $issues/2904/X#doSomething (param $this i32) (result i32)
+  local.get $this
+  return
+ )
+ (func $issues/2904/Y#doSomethingElse (param $this i32) (result i32)
+  i32.const 432
+  return
+ )
+ (func $~lib/rt/common/OBJECT#get:rtSize (param $this i32) (result i32)
+  local.get $this
+  i32.load offset=16
+ )
+ (func $~lib/string/String#get:length (param $this i32) (result i32)
+  local.get $this
+  i32.const 20
+  i32.sub
+  call $~lib/rt/common/OBJECT#get:rtSize
+  i32.const 1
+  i32.shr_u
+  return
+ )
+ (func $~lib/util/string/compareImpl (param $str1 i32) (param $index1 i32) (param $str2 i32) (param $index2 i32) (param $len i32) (result i32)
+  (local $ptr1 i32)
+  (local $ptr2 i32)
+  (local $7 i32)
+  (local $a i32)
+  (local $b i32)
+  local.get $str1
+  local.get $index1
+  i32.const 1
+  i32.shl
+  i32.add
+  local.set $ptr1
+  local.get $str2
+  local.get $index2
+  i32.const 1
+  i32.shl
+  i32.add
+  local.set $ptr2
+  i32.const 0
+  i32.const 2
+  i32.lt_s
+  drop
+  local.get $len
+  i32.const 4
+  i32.ge_u
+  if (result i32)
+   local.get $ptr1
+   i32.const 7
+   i32.and
+   local.get $ptr2
+   i32.const 7
+   i32.and
+   i32.or
+   i32.eqz
+  else
+   i32.const 0
+  end
+  if
+   block $do-break|0
+    loop $do-loop|0
+     local.get $ptr1
+     i64.load
+     local.get $ptr2
+     i64.load
+     i64.ne
+     if
+      br $do-break|0
+     end
+     local.get $ptr1
+     i32.const 8
+     i32.add
+     local.set $ptr1
+     local.get $ptr2
+     i32.const 8
+     i32.add
+     local.set $ptr2
+     local.get $len
+     i32.const 4
+     i32.sub
+     local.set $len
+     local.get $len
+     i32.const 4
+     i32.ge_u
+     br_if $do-loop|0
+    end
+   end
+  end
+  loop $while-continue|1
+   local.get $len
+   local.tee $7
+   i32.const 1
+   i32.sub
+   local.set $len
+   local.get $7
+   if
+    local.get $ptr1
+    i32.load16_u
+    local.set $a
+    local.get $ptr2
+    i32.load16_u
+    local.set $b
+    local.get $a
+    local.get $b
+    i32.ne
+    if
+     local.get $a
+     local.get $b
+     i32.sub
+     return
+    end
+    local.get $ptr1
+    i32.const 2
+    i32.add
+    local.set $ptr1
+    local.get $ptr2
+    i32.const 2
+    i32.add
+    local.set $ptr2
+    br $while-continue|1
+   end
+  end
+  i32.const 0
+  return
+ )
+ (func $issues/2904/X#get:self (param $this i32) (result i32)
+  local.get $this
+  return
+ )
+ (func $~lib/rt/__visit_globals (param $0 i32)
+  (local $1 i32)
+  global.get $issues/2904/y1
+  local.tee $1
+  if
+   local.get $1
+   local.get $0
+   call $~lib/rt/itcms/__visit
+  end
+  global.get $issues/2904/y2
+  local.tee $1
+  if
+   local.get $1
+   local.get $0
+   call $~lib/rt/itcms/__visit
+  end
+  i32.const 224
+  local.get $0
+  call $~lib/rt/itcms/__visit
+  i32.const 32
+  local.get $0
+  call $~lib/rt/itcms/__visit
+ )
+ (func $~lib/arraybuffer/ArrayBufferView~visit (param $0 i32) (param $1 i32)
+  (local $2 i32)
+  local.get $0
+  local.get $1
+  call $~lib/object/Object~visit
+  local.get $0
+  i32.load
+  local.tee $2
+  if
+   local.get $2
+   local.get $1
+   call $~lib/rt/itcms/__visit
+  end
+ )
+ (func $~lib/object/Object~visit (param $0 i32) (param $1 i32)
+ )
+ (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32)
+  block $invalid
+   block $issues/2904/X
+    block $issues/2904/Y
+     block $~lib/arraybuffer/ArrayBufferView
+      block $~lib/string/String
+       block $~lib/arraybuffer/ArrayBuffer
+        block $~lib/object/Object
+         local.get $0
+         i32.const 8
+         i32.sub
+         i32.load
+         br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $issues/2904/Y $issues/2904/X $invalid
+        end
+        return
+       end
+       return
+      end
+      return
+     end
+     local.get $0
+     local.get $1
+     call $~lib/arraybuffer/ArrayBufferView~visit
+     return
+    end
+    return
+   end
+   return
+  end
+  unreachable
+ )
+ (func $~start
+  call $start:issues/2904
+ )
+ (func $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  global.get $~lib/memory/__data_end
+  i32.lt_s
+  if
+   i32.const 33328
+   i32.const 33376
+   i32.const 1
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+ )
+ (func $issues/2904/X#constructor (param $this i32) (result i32)
+  (local $1 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  call $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  i64.const 0
+  i64.store
+  local.get $this
+  i32.eqz
+  if
+   global.get $~lib/memory/__stack_pointer
+   i32.const 0
+   i32.const 5
+   call $~lib/rt/itcms/__new
+   local.tee $this
+   i32.store
+  end
+  global.get $~lib/memory/__stack_pointer
+  local.get $this
+  local.set $1
+  global.get $~lib/memory/__stack_pointer
+  local.get $1
+  i32.store offset=4
+  local.get $1
+  call $~lib/object/Object#constructor
+  local.tee $this
+  i32.store
+  local.get $this
+  local.set $1
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+  local.get $1
+ )
+ (func $issues/2904/Y#constructor (param $this i32) (result i32)
+  (local $1 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  call $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  i64.const 0
+  i64.store
+  local.get $this
+  i32.eqz
+  if
+   global.get $~lib/memory/__stack_pointer
+   i32.const 0
+   i32.const 4
+   call $~lib/rt/itcms/__new
+   local.tee $this
+   i32.store
+  end
+  global.get $~lib/memory/__stack_pointer
+  local.get $this
+  local.set $1
+  global.get $~lib/memory/__stack_pointer
+  local.get $1
+  i32.store offset=4
+  local.get $1
+  call $issues/2904/X#constructor
+  local.tee $this
+  i32.store
+  local.get $this
+  local.set $1
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+  local.get $1
+ )
+ (func $~lib/string/String.__eq (param $left i32) (param $right i32) (result i32)
+  (local $leftLength i32)
+  (local $3 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  call $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  i64.const 0
+  i64.store
+  local.get $left
+  local.get $right
+  i32.eq
+  if
+   i32.const 1
+   local.set $3
+   global.get $~lib/memory/__stack_pointer
+   i32.const 8
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   local.get $3
+   return
+  end
+  local.get $left
+  i32.const 0
+  i32.eq
+  if (result i32)
+   i32.const 1
+  else
+   local.get $right
+   i32.const 0
+   i32.eq
+  end
+  if
+   i32.const 0
+   local.set $3
+   global.get $~lib/memory/__stack_pointer
+   i32.const 8
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   local.get $3
+   return
+  end
+  local.get $left
+  local.set $3
+  global.get $~lib/memory/__stack_pointer
+  local.get $3
+  i32.store
+  local.get $3
+  call $~lib/string/String#get:length
+  local.set $leftLength
+  local.get $leftLength
+  local.get $right
+  local.set $3
+  global.get $~lib/memory/__stack_pointer
+  local.get $3
+  i32.store
+  local.get $3
+  call $~lib/string/String#get:length
+  i32.ne
+  if
+   i32.const 0
+   local.set $3
+   global.get $~lib/memory/__stack_pointer
+   i32.const 8
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   local.get $3
+   return
+  end
+  local.get $left
+  local.set $3
+  global.get $~lib/memory/__stack_pointer
+  local.get $3
+  i32.store
+  local.get $3
+  i32.const 0
+  local.get $right
+  local.set $3
+  global.get $~lib/memory/__stack_pointer
+  local.get $3
+  i32.store offset=4
+  local.get $3
+  i32.const 0
+  local.get $leftLength
+  call $~lib/util/string/compareImpl
+  i32.eqz
+  local.set $3
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+  local.get $3
+  return
+ )
+ (func $start:issues/2904
+  (local $0 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 12
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  call $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  i64.const 0
+  i64.store
+  global.get $~lib/memory/__stack_pointer
+  i32.const 0
+  i32.store offset=8
+  memory.size
+  i32.const 16
+  i32.shl
+  global.get $~lib/memory/__heap_base
+  i32.sub
+  i32.const 1
+  i32.shr_u
+  global.set $~lib/rt/itcms/threshold
+  i32.const 144
+  call $~lib/rt/itcms/initLazy
+  global.set $~lib/rt/itcms/pinSpace
+  i32.const 176
+  call $~lib/rt/itcms/initLazy
+  global.set $~lib/rt/itcms/toSpace
+  i32.const 320
+  call $~lib/rt/itcms/initLazy
+  global.set $~lib/rt/itcms/fromSpace
+  i32.const 0
+  call $issues/2904/Y#constructor
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store
+  local.get $0
+  call $issues/2904/X#doSomething
+  global.set $issues/2904/y1
+  global.get $issues/2904/y1
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store offset=8
+  local.get $0
+  call $issues/2904/Y#doSomethingElse
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store
+  local.get $0
+  i32.const 432
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store offset=4
+  local.get $0
+  call $~lib/string/String.__eq
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 480
+   i32.const 31
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+  i32.const 0
+  call $issues/2904/Y#constructor
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store
+  local.get $0
+  call $issues/2904/X#get:self
+  global.set $issues/2904/y2
+  global.get $issues/2904/y2
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store offset=8
+  local.get $0
+  call $issues/2904/Y#doSomethingElse
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store
+  local.get $0
+  i32.const 432
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store offset=4
+  local.get $0
+  call $~lib/string/String.__eq
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 480
+   i32.const 34
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+  global.get $~lib/memory/__stack_pointer
+  i32.const 12
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+ )
+ (func $~lib/object/Object#constructor (param $this i32) (result i32)
+  (local $1 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 4
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  call $~stack_check
+  global.get $~lib/memory/__stack_pointer
+  i32.const 0
+  i32.store
+  local.get $this
+  i32.eqz
+  if
+   global.get $~lib/memory/__stack_pointer
+   i32.const 0
+   i32.const 0
+   call $~lib/rt/itcms/__new
+   local.tee $this
+   i32.store
+  end
+  local.get $this
+  local.set $1
+  global.get $~lib/memory/__stack_pointer
+  i32.const 4
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+  local.get $1
+ )
+)
diff --git a/tests/compiler/issues/2904.json b/tests/compiler/issues/2904.json
new file mode 100644
index 0000000000..ea57a955e1
--- /dev/null
+++ b/tests/compiler/issues/2904.json
@@ -0,0 +1,3 @@
+{
+  "asc_flags": []
+}
diff --git a/tests/compiler/issues/2904.release.wat b/tests/compiler/issues/2904.release.wat
new file mode 100644
index 0000000000..bc59393592
--- /dev/null
+++ b/tests/compiler/issues/2904.release.wat
@@ -0,0 +1,1659 @@
+(module
+ (type $0 (func))
+ (type $1 (func (result i32)))
+ (type $2 (func (param i32)))
+ (type $3 (func (param i32 i32)))
+ (type $4 (func (param i32) (result i32)))
+ (type $5 (func (param i32 i32 i32 i32)))
+ (type $6 (func (param i32 i32 i64)))
+ (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
+ (global $~lib/rt/itcms/total (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/threshold (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/state (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/visitCount (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/pinSpace (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/iter (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/toSpace (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/white (mut i32) (i32.const 0))
+ (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0))
+ (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0))
+ (global $issues/2904/y1 (mut i32) (i32.const 0))
+ (global $issues/2904/y2 (mut i32) (i32.const 0))
+ (global $~lib/memory/__stack_pointer (mut i32) (i32.const 34332))
+ (memory $0 1)
+ (data $0 (i32.const 1036) "<")
+ (data $0.1 (i32.const 1048) "\02\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e")
+ (data $1 (i32.const 1100) "<")
+ (data $1.1 (i32.const 1112) "\02\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s")
+ (data $4 (i32.const 1228) "<")
+ (data $4.1 (i32.const 1240) "\02\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e")
+ (data $5 (i32.const 1292) ",")
+ (data $5.1 (i32.const 1304) "\02\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s")
+ (data $7 (i32.const 1372) "<")
+ (data $7.1 (i32.const 1384) "\02\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s")
+ (data $8 (i32.const 1436) ",")
+ (data $8.1 (i32.const 1448) "\02\00\00\00\18\00\00\00Y\00o\00u\00 \00m\00a\00d\00e\00 \00i\00t\00!")
+ (data $9 (i32.const 1484) ",")
+ (data $9.1 (i32.const 1496) "\02\00\00\00\1c\00\00\00i\00s\00s\00u\00e\00s\00/\002\009\000\004\00.\00t\00s")
+ (data $10 (i32.const 1536) "\06\00\00\00 \00\00\00 \00\00\00 \00\00\00\00\00\00\00 \00\00\00 ")
+ (export "memory" (memory $0))
+ (start $~start)
+ (func $~lib/rt/itcms/visitRoots
+  (local $0 i32)
+  (local $1 i32)
+  global.get $issues/2904/y1
+  local.tee $0
+  if
+   local.get $0
+   call $~lib/rt/itcms/__visit
+  end
+  global.get $issues/2904/y2
+  local.tee $0
+  if
+   local.get $0
+   call $~lib/rt/itcms/__visit
+  end
+  i32.const 1248
+  call $~lib/rt/itcms/__visit
+  i32.const 1056
+  call $~lib/rt/itcms/__visit
+  global.get $~lib/rt/itcms/pinSpace
+  local.tee $1
+  i32.load offset=4
+  i32.const -4
+  i32.and
+  local.set $0
+  loop $while-continue|0
+   local.get $0
+   local.get $1
+   i32.ne
+   if
+    local.get $0
+    i32.load offset=4
+    i32.const 3
+    i32.and
+    i32.const 3
+    i32.ne
+    if
+     i32.const 0
+     i32.const 1120
+     i32.const 160
+     i32.const 16
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $0
+    i32.const 20
+    i32.add
+    call $~lib/rt/__visit_members
+    local.get $0
+    i32.load offset=4
+    i32.const -4
+    i32.and
+    local.set $0
+    br $while-continue|0
+   end
+  end
+ )
+ (func $~lib/rt/itcms/__visit (param $0 i32)
+  (local $1 i32)
+  (local $2 i32)
+  (local $3 i32)
+  local.get $0
+  i32.eqz
+  if
+   return
+  end
+  global.get $~lib/rt/itcms/white
+  local.get $0
+  i32.const 20
+  i32.sub
+  local.tee $1
+  i32.load offset=4
+  i32.const 3
+  i32.and
+  i32.eq
+  if
+   local.get $1
+   global.get $~lib/rt/itcms/iter
+   i32.eq
+   if
+    local.get $1
+    i32.load offset=8
+    local.tee $0
+    i32.eqz
+    if
+     i32.const 0
+     i32.const 1120
+     i32.const 148
+     i32.const 30
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $0
+    global.set $~lib/rt/itcms/iter
+   end
+   block $__inlined_func$~lib/rt/itcms/Object#unlink$121
+    local.get $1
+    i32.load offset=4
+    i32.const -4
+    i32.and
+    local.tee $0
+    i32.eqz
+    if
+     local.get $1
+     i32.load offset=8
+     i32.eqz
+     local.get $1
+     i32.const 34332
+     i32.lt_u
+     i32.and
+     i32.eqz
+     if
+      i32.const 0
+      i32.const 1120
+      i32.const 128
+      i32.const 18
+      call $~lib/builtins/abort
+      unreachable
+     end
+     br $__inlined_func$~lib/rt/itcms/Object#unlink$121
+    end
+    local.get $1
+    i32.load offset=8
+    local.tee $2
+    i32.eqz
+    if
+     i32.const 0
+     i32.const 1120
+     i32.const 132
+     i32.const 16
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $0
+    local.get $2
+    i32.store offset=8
+    local.get $2
+    local.get $0
+    local.get $2
+    i32.load offset=4
+    i32.const 3
+    i32.and
+    i32.or
+    i32.store offset=4
+   end
+   global.get $~lib/rt/itcms/toSpace
+   local.set $2
+   local.get $1
+   i32.load offset=12
+   local.tee $0
+   i32.const 2
+   i32.le_u
+   if (result i32)
+    i32.const 1
+   else
+    local.get $0
+    i32.const 1536
+    i32.load
+    i32.gt_u
+    if
+     i32.const 1248
+     i32.const 1312
+     i32.const 21
+     i32.const 28
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $0
+    i32.const 2
+    i32.shl
+    i32.const 1540
+    i32.add
+    i32.load
+    i32.const 32
+    i32.and
+   end
+   local.set $3
+   local.get $2
+   i32.load offset=8
+   local.set $0
+   local.get $1
+   global.get $~lib/rt/itcms/white
+   i32.eqz
+   i32.const 2
+   local.get $3
+   select
+   local.get $2
+   i32.or
+   i32.store offset=4
+   local.get $1
+   local.get $0
+   i32.store offset=8
+   local.get $0
+   local.get $1
+   local.get $0
+   i32.load offset=4
+   i32.const 3
+   i32.and
+   i32.or
+   i32.store offset=4
+   local.get $2
+   local.get $1
+   i32.store offset=8
+   global.get $~lib/rt/itcms/visitCount
+   i32.const 1
+   i32.add
+   global.set $~lib/rt/itcms/visitCount
+  end
+ )
+ (func $~lib/rt/tlsf/removeBlock (param $0 i32) (param $1 i32)
+  (local $2 i32)
+  (local $3 i32)
+  (local $4 i32)
+  (local $5 i32)
+  local.get $1
+  i32.load
+  local.tee $3
+  i32.const 1
+  i32.and
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 268
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $3
+  i32.const -4
+  i32.and
+  local.tee $3
+  i32.const 12
+  i32.lt_u
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 270
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $3
+  i32.const 256
+  i32.lt_u
+  if (result i32)
+   local.get $3
+   i32.const 4
+   i32.shr_u
+  else
+   i32.const 31
+   i32.const 1073741820
+   local.get $3
+   local.get $3
+   i32.const 1073741820
+   i32.ge_u
+   select
+   local.tee $3
+   i32.clz
+   i32.sub
+   local.tee $4
+   i32.const 7
+   i32.sub
+   local.set $2
+   local.get $3
+   local.get $4
+   i32.const 4
+   i32.sub
+   i32.shr_u
+   i32.const 16
+   i32.xor
+  end
+  local.tee $3
+  i32.const 16
+  i32.lt_u
+  local.get $2
+  i32.const 23
+  i32.lt_u
+  i32.and
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 284
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $1
+  i32.load offset=8
+  local.set $5
+  local.get $1
+  i32.load offset=4
+  local.tee $4
+  if
+   local.get $4
+   local.get $5
+   i32.store offset=8
+  end
+  local.get $5
+  if
+   local.get $5
+   local.get $4
+   i32.store offset=4
+  end
+  local.get $1
+  local.get $0
+  local.get $2
+  i32.const 4
+  i32.shl
+  local.get $3
+  i32.add
+  i32.const 2
+  i32.shl
+  i32.add
+  local.tee $1
+  i32.load offset=96
+  i32.eq
+  if
+   local.get $1
+   local.get $5
+   i32.store offset=96
+   local.get $5
+   i32.eqz
+   if
+    local.get $0
+    local.get $2
+    i32.const 2
+    i32.shl
+    i32.add
+    local.tee $1
+    i32.load offset=4
+    i32.const -2
+    local.get $3
+    i32.rotl
+    i32.and
+    local.set $3
+    local.get $1
+    local.get $3
+    i32.store offset=4
+    local.get $3
+    i32.eqz
+    if
+     local.get $0
+     local.get $0
+     i32.load
+     i32.const -2
+     local.get $2
+     i32.rotl
+     i32.and
+     i32.store
+    end
+   end
+  end
+ )
+ (func $~lib/rt/tlsf/insertBlock (param $0 i32) (param $1 i32)
+  (local $2 i32)
+  (local $3 i32)
+  (local $4 i32)
+  (local $5 i32)
+  (local $6 i32)
+  local.get $1
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 201
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $1
+  i32.load
+  local.tee $3
+  i32.const 1
+  i32.and
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 203
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $1
+  i32.const 4
+  i32.add
+  local.get $1
+  i32.load
+  i32.const -4
+  i32.and
+  i32.add
+  local.tee $4
+  i32.load
+  local.tee $2
+  i32.const 1
+  i32.and
+  if
+   local.get $0
+   local.get $4
+   call $~lib/rt/tlsf/removeBlock
+   local.get $1
+   local.get $3
+   i32.const 4
+   i32.add
+   local.get $2
+   i32.const -4
+   i32.and
+   i32.add
+   local.tee $3
+   i32.store
+   local.get $1
+   i32.const 4
+   i32.add
+   local.get $1
+   i32.load
+   i32.const -4
+   i32.and
+   i32.add
+   local.tee $4
+   i32.load
+   local.set $2
+  end
+  local.get $3
+  i32.const 2
+  i32.and
+  if
+   local.get $1
+   i32.const 4
+   i32.sub
+   i32.load
+   local.tee $1
+   i32.load
+   local.tee $6
+   i32.const 1
+   i32.and
+   i32.eqz
+   if
+    i32.const 0
+    i32.const 1392
+    i32.const 221
+    i32.const 16
+    call $~lib/builtins/abort
+    unreachable
+   end
+   local.get $0
+   local.get $1
+   call $~lib/rt/tlsf/removeBlock
+   local.get $1
+   local.get $6
+   i32.const 4
+   i32.add
+   local.get $3
+   i32.const -4
+   i32.and
+   i32.add
+   local.tee $3
+   i32.store
+  end
+  local.get $4
+  local.get $2
+  i32.const 2
+  i32.or
+  i32.store
+  local.get $3
+  i32.const -4
+  i32.and
+  local.tee $2
+  i32.const 12
+  i32.lt_u
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 233
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $4
+  local.get $1
+  i32.const 4
+  i32.add
+  local.get $2
+  i32.add
+  i32.ne
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 234
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $4
+  i32.const 4
+  i32.sub
+  local.get $1
+  i32.store
+  local.get $2
+  i32.const 256
+  i32.lt_u
+  if (result i32)
+   local.get $2
+   i32.const 4
+   i32.shr_u
+  else
+   i32.const 31
+   i32.const 1073741820
+   local.get $2
+   local.get $2
+   i32.const 1073741820
+   i32.ge_u
+   select
+   local.tee $2
+   i32.clz
+   i32.sub
+   local.tee $3
+   i32.const 7
+   i32.sub
+   local.set $5
+   local.get $2
+   local.get $3
+   i32.const 4
+   i32.sub
+   i32.shr_u
+   i32.const 16
+   i32.xor
+  end
+  local.tee $2
+  i32.const 16
+  i32.lt_u
+  local.get $5
+  i32.const 23
+  i32.lt_u
+  i32.and
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 251
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $0
+  local.get $5
+  i32.const 4
+  i32.shl
+  local.get $2
+  i32.add
+  i32.const 2
+  i32.shl
+  i32.add
+  i32.load offset=96
+  local.set $3
+  local.get $1
+  i32.const 0
+  i32.store offset=4
+  local.get $1
+  local.get $3
+  i32.store offset=8
+  local.get $3
+  if
+   local.get $3
+   local.get $1
+   i32.store offset=4
+  end
+  local.get $0
+  local.get $5
+  i32.const 4
+  i32.shl
+  local.get $2
+  i32.add
+  i32.const 2
+  i32.shl
+  i32.add
+  local.get $1
+  i32.store offset=96
+  local.get $0
+  local.get $0
+  i32.load
+  i32.const 1
+  local.get $5
+  i32.shl
+  i32.or
+  i32.store
+  local.get $0
+  local.get $5
+  i32.const 2
+  i32.shl
+  i32.add
+  local.tee $0
+  local.get $0
+  i32.load offset=4
+  i32.const 1
+  local.get $2
+  i32.shl
+  i32.or
+  i32.store offset=4
+ )
+ (func $~lib/rt/tlsf/addMemory (param $0 i32) (param $1 i32) (param $2 i64)
+  (local $3 i32)
+  (local $4 i32)
+  (local $5 i32)
+  local.get $2
+  local.get $1
+  i64.extend_i32_u
+  i64.lt_u
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 382
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $1
+  i32.const 19
+  i32.add
+  i32.const -16
+  i32.and
+  i32.const 4
+  i32.sub
+  local.set $1
+  local.get $0
+  i32.load offset=1568
+  local.tee $3
+  if
+   local.get $3
+   i32.const 4
+   i32.add
+   local.get $1
+   i32.gt_u
+   if
+    i32.const 0
+    i32.const 1392
+    i32.const 389
+    i32.const 16
+    call $~lib/builtins/abort
+    unreachable
+   end
+   local.get $3
+   local.get $1
+   i32.const 16
+   i32.sub
+   local.tee $5
+   i32.eq
+   if
+    local.get $3
+    i32.load
+    local.set $4
+    local.get $5
+    local.set $1
+   end
+  else
+   local.get $0
+   i32.const 1572
+   i32.add
+   local.get $1
+   i32.gt_u
+   if
+    i32.const 0
+    i32.const 1392
+    i32.const 402
+    i32.const 5
+    call $~lib/builtins/abort
+    unreachable
+   end
+  end
+  local.get $2
+  i32.wrap_i64
+  i32.const -16
+  i32.and
+  local.get $1
+  i32.sub
+  local.tee $3
+  i32.const 20
+  i32.lt_u
+  if
+   return
+  end
+  local.get $1
+  local.get $4
+  i32.const 2
+  i32.and
+  local.get $3
+  i32.const 8
+  i32.sub
+  local.tee $3
+  i32.const 1
+  i32.or
+  i32.or
+  i32.store
+  local.get $1
+  i32.const 0
+  i32.store offset=4
+  local.get $1
+  i32.const 0
+  i32.store offset=8
+  local.get $1
+  i32.const 4
+  i32.add
+  local.get $3
+  i32.add
+  local.tee $3
+  i32.const 2
+  i32.store
+  local.get $0
+  local.get $3
+  i32.store offset=1568
+  local.get $0
+  local.get $1
+  call $~lib/rt/tlsf/insertBlock
+ )
+ (func $~lib/rt/tlsf/initialize
+  (local $0 i32)
+  (local $1 i32)
+  memory.size
+  local.tee $1
+  i32.const 0
+  i32.le_s
+  if (result i32)
+   i32.const 1
+   local.get $1
+   i32.sub
+   memory.grow
+   i32.const 0
+   i32.lt_s
+  else
+   i32.const 0
+  end
+  if
+   unreachable
+  end
+  i32.const 34336
+  i32.const 0
+  i32.store
+  i32.const 35904
+  i32.const 0
+  i32.store
+  loop $for-loop|0
+   local.get $0
+   i32.const 23
+   i32.lt_u
+   if
+    local.get $0
+    i32.const 2
+    i32.shl
+    i32.const 34336
+    i32.add
+    i32.const 0
+    i32.store offset=4
+    i32.const 0
+    local.set $1
+    loop $for-loop|1
+     local.get $1
+     i32.const 16
+     i32.lt_u
+     if
+      local.get $0
+      i32.const 4
+      i32.shl
+      local.get $1
+      i32.add
+      i32.const 2
+      i32.shl
+      i32.const 34336
+      i32.add
+      i32.const 0
+      i32.store offset=96
+      local.get $1
+      i32.const 1
+      i32.add
+      local.set $1
+      br $for-loop|1
+     end
+    end
+    local.get $0
+    i32.const 1
+    i32.add
+    local.set $0
+    br $for-loop|0
+   end
+  end
+  i32.const 34336
+  i32.const 35908
+  memory.size
+  i64.extend_i32_s
+  i64.const 16
+  i64.shl
+  call $~lib/rt/tlsf/addMemory
+  i32.const 34336
+  global.set $~lib/rt/tlsf/ROOT
+ )
+ (func $~lib/rt/itcms/step (result i32)
+  (local $0 i32)
+  (local $1 i32)
+  (local $2 i32)
+  block $break|0
+   block $case2|0
+    block $case1|0
+     block $case0|0
+      global.get $~lib/rt/itcms/state
+      br_table $case0|0 $case1|0 $case2|0 $break|0
+     end
+     i32.const 1
+     global.set $~lib/rt/itcms/state
+     i32.const 0
+     global.set $~lib/rt/itcms/visitCount
+     call $~lib/rt/itcms/visitRoots
+     global.get $~lib/rt/itcms/toSpace
+     global.set $~lib/rt/itcms/iter
+     global.get $~lib/rt/itcms/visitCount
+     return
+    end
+    global.get $~lib/rt/itcms/white
+    i32.eqz
+    local.set $1
+    global.get $~lib/rt/itcms/iter
+    i32.load offset=4
+    i32.const -4
+    i32.and
+    local.set $0
+    loop $while-continue|1
+     local.get $0
+     global.get $~lib/rt/itcms/toSpace
+     i32.ne
+     if
+      local.get $0
+      global.set $~lib/rt/itcms/iter
+      local.get $1
+      local.get $0
+      i32.load offset=4
+      local.tee $2
+      i32.const 3
+      i32.and
+      i32.ne
+      if
+       local.get $0
+       local.get $2
+       i32.const -4
+       i32.and
+       local.get $1
+       i32.or
+       i32.store offset=4
+       i32.const 0
+       global.set $~lib/rt/itcms/visitCount
+       local.get $0
+       i32.const 20
+       i32.add
+       call $~lib/rt/__visit_members
+       global.get $~lib/rt/itcms/visitCount
+       return
+      end
+      local.get $0
+      i32.load offset=4
+      i32.const -4
+      i32.and
+      local.set $0
+      br $while-continue|1
+     end
+    end
+    i32.const 0
+    global.set $~lib/rt/itcms/visitCount
+    call $~lib/rt/itcms/visitRoots
+    global.get $~lib/rt/itcms/toSpace
+    global.get $~lib/rt/itcms/iter
+    i32.load offset=4
+    i32.const -4
+    i32.and
+    i32.eq
+    if
+     global.get $~lib/memory/__stack_pointer
+     local.set $0
+     loop $while-continue|0
+      local.get $0
+      i32.const 34332
+      i32.lt_u
+      if
+       local.get $0
+       i32.load
+       call $~lib/rt/itcms/__visit
+       local.get $0
+       i32.const 4
+       i32.add
+       local.set $0
+       br $while-continue|0
+      end
+     end
+     global.get $~lib/rt/itcms/iter
+     i32.load offset=4
+     i32.const -4
+     i32.and
+     local.set $0
+     loop $while-continue|2
+      local.get $0
+      global.get $~lib/rt/itcms/toSpace
+      i32.ne
+      if
+       local.get $1
+       local.get $0
+       i32.load offset=4
+       local.tee $2
+       i32.const 3
+       i32.and
+       i32.ne
+       if
+        local.get $0
+        local.get $2
+        i32.const -4
+        i32.and
+        local.get $1
+        i32.or
+        i32.store offset=4
+        local.get $0
+        i32.const 20
+        i32.add
+        call $~lib/rt/__visit_members
+       end
+       local.get $0
+       i32.load offset=4
+       i32.const -4
+       i32.and
+       local.set $0
+       br $while-continue|2
+      end
+     end
+     global.get $~lib/rt/itcms/fromSpace
+     local.set $0
+     global.get $~lib/rt/itcms/toSpace
+     global.set $~lib/rt/itcms/fromSpace
+     local.get $0
+     global.set $~lib/rt/itcms/toSpace
+     local.get $1
+     global.set $~lib/rt/itcms/white
+     local.get $0
+     i32.load offset=4
+     i32.const -4
+     i32.and
+     global.set $~lib/rt/itcms/iter
+     i32.const 2
+     global.set $~lib/rt/itcms/state
+    end
+    global.get $~lib/rt/itcms/visitCount
+    return
+   end
+   global.get $~lib/rt/itcms/iter
+   local.tee $0
+   global.get $~lib/rt/itcms/toSpace
+   i32.ne
+   if
+    local.get $0
+    i32.load offset=4
+    local.tee $1
+    i32.const -4
+    i32.and
+    global.set $~lib/rt/itcms/iter
+    global.get $~lib/rt/itcms/white
+    i32.eqz
+    local.get $1
+    i32.const 3
+    i32.and
+    i32.ne
+    if
+     i32.const 0
+     i32.const 1120
+     i32.const 229
+     i32.const 20
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $0
+    i32.const 34332
+    i32.lt_u
+    if
+     local.get $0
+     i32.const 0
+     i32.store offset=4
+     local.get $0
+     i32.const 0
+     i32.store offset=8
+    else
+     global.get $~lib/rt/itcms/total
+     local.get $0
+     i32.load
+     i32.const -4
+     i32.and
+     i32.const 4
+     i32.add
+     i32.sub
+     global.set $~lib/rt/itcms/total
+     local.get $0
+     i32.const 4
+     i32.add
+     local.tee $0
+     i32.const 34332
+     i32.ge_u
+     if
+      global.get $~lib/rt/tlsf/ROOT
+      i32.eqz
+      if
+       call $~lib/rt/tlsf/initialize
+      end
+      global.get $~lib/rt/tlsf/ROOT
+      local.set $1
+      local.get $0
+      i32.const 4
+      i32.sub
+      local.set $2
+      local.get $0
+      i32.const 15
+      i32.and
+      i32.const 1
+      local.get $0
+      select
+      if (result i32)
+       i32.const 1
+      else
+       local.get $2
+       i32.load
+       i32.const 1
+       i32.and
+      end
+      if
+       i32.const 0
+       i32.const 1392
+       i32.const 562
+       i32.const 3
+       call $~lib/builtins/abort
+       unreachable
+      end
+      local.get $2
+      local.get $2
+      i32.load
+      i32.const 1
+      i32.or
+      i32.store
+      local.get $1
+      local.get $2
+      call $~lib/rt/tlsf/insertBlock
+     end
+    end
+    i32.const 10
+    return
+   end
+   global.get $~lib/rt/itcms/toSpace
+   global.get $~lib/rt/itcms/toSpace
+   i32.store offset=4
+   global.get $~lib/rt/itcms/toSpace
+   global.get $~lib/rt/itcms/toSpace
+   i32.store offset=8
+   i32.const 0
+   global.set $~lib/rt/itcms/state
+  end
+  i32.const 0
+ )
+ (func $~lib/rt/tlsf/searchBlock (param $0 i32) (result i32)
+  (local $1 i32)
+  (local $2 i32)
+  local.get $0
+  i32.load offset=4
+  i32.const -2
+  i32.and
+  local.tee $1
+  if (result i32)
+   local.get $0
+   local.get $1
+   i32.ctz
+   i32.const 2
+   i32.shl
+   i32.add
+   i32.load offset=96
+  else
+   local.get $0
+   i32.load
+   i32.const -2
+   i32.and
+   local.tee $1
+   if (result i32)
+    local.get $0
+    local.get $1
+    i32.ctz
+    local.tee $2
+    i32.const 2
+    i32.shl
+    i32.add
+    i32.load offset=4
+    local.tee $1
+    i32.eqz
+    if
+     i32.const 0
+     i32.const 1392
+     i32.const 347
+     i32.const 18
+     call $~lib/builtins/abort
+     unreachable
+    end
+    local.get $0
+    local.get $1
+    i32.ctz
+    local.get $2
+    i32.const 4
+    i32.shl
+    i32.add
+    i32.const 2
+    i32.shl
+    i32.add
+    i32.load offset=96
+   else
+    i32.const 0
+   end
+  end
+ )
+ (func $~lib/rt/itcms/__new (param $0 i32) (result i32)
+  (local $1 i32)
+  (local $2 i32)
+  (local $3 i32)
+  (local $4 i32)
+  global.get $~lib/rt/itcms/total
+  global.get $~lib/rt/itcms/threshold
+  i32.ge_u
+  if
+   block $__inlined_func$~lib/rt/itcms/interrupt$69
+    i32.const 2048
+    local.set $1
+    loop $do-loop|0
+     local.get $1
+     call $~lib/rt/itcms/step
+     i32.sub
+     local.set $1
+     global.get $~lib/rt/itcms/state
+     i32.eqz
+     if
+      global.get $~lib/rt/itcms/total
+      i64.extend_i32_u
+      i64.const 200
+      i64.mul
+      i64.const 100
+      i64.div_u
+      i32.wrap_i64
+      i32.const 1024
+      i32.add
+      global.set $~lib/rt/itcms/threshold
+      br $__inlined_func$~lib/rt/itcms/interrupt$69
+     end
+     local.get $1
+     i32.const 0
+     i32.gt_s
+     br_if $do-loop|0
+    end
+    global.get $~lib/rt/itcms/total
+    global.get $~lib/rt/itcms/total
+    global.get $~lib/rt/itcms/threshold
+    i32.sub
+    i32.const 1024
+    i32.lt_u
+    i32.const 10
+    i32.shl
+    i32.add
+    global.set $~lib/rt/itcms/threshold
+   end
+  end
+  global.get $~lib/rt/tlsf/ROOT
+  i32.eqz
+  if
+   call $~lib/rt/tlsf/initialize
+  end
+  global.get $~lib/rt/tlsf/ROOT
+  local.tee $2
+  call $~lib/rt/tlsf/searchBlock
+  local.tee $1
+  i32.eqz
+  if
+   memory.size
+   local.tee $1
+   i32.const 4
+   local.get $2
+   i32.load offset=1568
+   local.get $1
+   i32.const 16
+   i32.shl
+   i32.const 4
+   i32.sub
+   i32.ne
+   i32.shl
+   i32.const 65563
+   i32.add
+   i32.const -65536
+   i32.and
+   i32.const 16
+   i32.shr_u
+   local.tee $3
+   local.get $1
+   local.get $3
+   i32.gt_s
+   select
+   memory.grow
+   i32.const 0
+   i32.lt_s
+   if
+    local.get $3
+    memory.grow
+    i32.const 0
+    i32.lt_s
+    if
+     unreachable
+    end
+   end
+   local.get $2
+   local.get $1
+   i32.const 16
+   i32.shl
+   memory.size
+   i64.extend_i32_s
+   i64.const 16
+   i64.shl
+   call $~lib/rt/tlsf/addMemory
+   local.get $2
+   call $~lib/rt/tlsf/searchBlock
+   local.tee $1
+   i32.eqz
+   if
+    i32.const 0
+    i32.const 1392
+    i32.const 499
+    i32.const 16
+    call $~lib/builtins/abort
+    unreachable
+   end
+  end
+  local.get $1
+  i32.load
+  i32.const -4
+  i32.and
+  i32.const 28
+  i32.lt_u
+  if
+   i32.const 0
+   i32.const 1392
+   i32.const 501
+   i32.const 14
+   call $~lib/builtins/abort
+   unreachable
+  end
+  local.get $2
+  local.get $1
+  call $~lib/rt/tlsf/removeBlock
+  local.get $1
+  i32.load
+  local.tee $3
+  i32.const -4
+  i32.and
+  i32.const 28
+  i32.sub
+  local.tee $4
+  i32.const 16
+  i32.ge_u
+  if
+   local.get $1
+   local.get $3
+   i32.const 2
+   i32.and
+   i32.const 28
+   i32.or
+   i32.store
+   local.get $1
+   i32.const 32
+   i32.add
+   local.tee $3
+   local.get $4
+   i32.const 4
+   i32.sub
+   i32.const 1
+   i32.or
+   i32.store
+   local.get $2
+   local.get $3
+   call $~lib/rt/tlsf/insertBlock
+  else
+   local.get $1
+   local.get $3
+   i32.const -2
+   i32.and
+   i32.store
+   local.get $1
+   i32.const 4
+   i32.add
+   local.get $1
+   i32.load
+   i32.const -4
+   i32.and
+   i32.add
+   local.tee $2
+   local.get $2
+   i32.load
+   i32.const -3
+   i32.and
+   i32.store
+  end
+  local.get $1
+  local.get $0
+  i32.store offset=12
+  local.get $1
+  i32.const 0
+  i32.store offset=16
+  global.get $~lib/rt/itcms/fromSpace
+  local.tee $0
+  i32.load offset=8
+  local.set $2
+  local.get $1
+  local.get $0
+  global.get $~lib/rt/itcms/white
+  i32.or
+  i32.store offset=4
+  local.get $1
+  local.get $2
+  i32.store offset=8
+  local.get $2
+  local.get $1
+  local.get $2
+  i32.load offset=4
+  i32.const 3
+  i32.and
+  i32.or
+  i32.store offset=4
+  local.get $0
+  local.get $1
+  i32.store offset=8
+  global.get $~lib/rt/itcms/total
+  local.get $1
+  i32.load
+  i32.const -4
+  i32.and
+  i32.const 4
+  i32.add
+  i32.add
+  global.set $~lib/rt/itcms/total
+  local.get $1
+  i32.const 20
+  i32.add
+  local.tee $0
+  i32.const 0
+  i32.const 0
+  memory.fill
+  local.get $0
+ )
+ (func $~lib/rt/__visit_members (param $0 i32)
+  block $invalid
+   block $issues/2904/X
+    block $issues/2904/Y
+     block $~lib/arraybuffer/ArrayBufferView
+      block $~lib/string/String
+       block $~lib/arraybuffer/ArrayBuffer
+        block $~lib/object/Object
+         local.get $0
+         i32.const 8
+         i32.sub
+         i32.load
+         br_table $~lib/object/Object $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $issues/2904/Y $issues/2904/X $invalid
+        end
+        return
+       end
+       return
+      end
+      return
+     end
+     local.get $0
+     i32.load
+     local.tee $0
+     if
+      local.get $0
+      call $~lib/rt/itcms/__visit
+     end
+     return
+    end
+    return
+   end
+   return
+  end
+  unreachable
+ )
+ (func $~start
+  (local $0 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 12
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  global.get $~lib/memory/__stack_pointer
+  i32.const 1564
+  i32.lt_s
+  if
+   i32.const 34352
+   i32.const 34400
+   i32.const 1
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+  global.get $~lib/memory/__stack_pointer
+  i64.const 0
+  i64.store
+  global.get $~lib/memory/__stack_pointer
+  i32.const 0
+  i32.store offset=8
+  memory.size
+  i32.const 16
+  i32.shl
+  i32.const 34332
+  i32.sub
+  i32.const 1
+  i32.shr_u
+  global.set $~lib/rt/itcms/threshold
+  i32.const 1172
+  i32.const 1168
+  i32.store
+  i32.const 1176
+  i32.const 1168
+  i32.store
+  i32.const 1168
+  global.set $~lib/rt/itcms/pinSpace
+  i32.const 1204
+  i32.const 1200
+  i32.store
+  i32.const 1208
+  i32.const 1200
+  i32.store
+  i32.const 1200
+  global.set $~lib/rt/itcms/toSpace
+  i32.const 1348
+  i32.const 1344
+  i32.store
+  i32.const 1352
+  i32.const 1344
+  i32.store
+  i32.const 1344
+  global.set $~lib/rt/itcms/fromSpace
+  call $issues/2904/Y#constructor
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store
+  local.get $0
+  global.set $issues/2904/y1
+  global.get $~lib/memory/__stack_pointer
+  global.get $issues/2904/y1
+  i32.store offset=8
+  global.get $~lib/memory/__stack_pointer
+  i32.const 1456
+  i32.store
+  global.get $~lib/memory/__stack_pointer
+  i32.const 1456
+  i32.store offset=4
+  call $~lib/string/String.__eq
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 1504
+   i32.const 31
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+  call $issues/2904/Y#constructor
+  local.set $0
+  global.get $~lib/memory/__stack_pointer
+  local.get $0
+  i32.store
+  local.get $0
+  global.set $issues/2904/y2
+  global.get $~lib/memory/__stack_pointer
+  global.get $issues/2904/y2
+  i32.store offset=8
+  global.get $~lib/memory/__stack_pointer
+  i32.const 1456
+  i32.store
+  global.get $~lib/memory/__stack_pointer
+  i32.const 1456
+  i32.store offset=4
+  call $~lib/string/String.__eq
+  i32.eqz
+  if
+   i32.const 0
+   i32.const 1504
+   i32.const 34
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+  global.get $~lib/memory/__stack_pointer
+  i32.const 12
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+ )
+ (func $issues/2904/Y#constructor (result i32)
+  (local $0 i32)
+  (local $1 i32)
+  (local $2 i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  block $folding-inner0
+   global.get $~lib/memory/__stack_pointer
+   i32.const 1564
+   i32.lt_s
+   br_if $folding-inner0
+   global.get $~lib/memory/__stack_pointer
+   i64.const 0
+   i64.store
+   global.get $~lib/memory/__stack_pointer
+   i32.const 4
+   call $~lib/rt/itcms/__new
+   local.tee $0
+   i32.store
+   global.get $~lib/memory/__stack_pointer
+   local.set $1
+   global.get $~lib/memory/__stack_pointer
+   local.get $0
+   i32.store offset=4
+   global.get $~lib/memory/__stack_pointer
+   i32.const 8
+   i32.sub
+   global.set $~lib/memory/__stack_pointer
+   global.get $~lib/memory/__stack_pointer
+   i32.const 1564
+   i32.lt_s
+   br_if $folding-inner0
+   global.get $~lib/memory/__stack_pointer
+   i64.const 0
+   i64.store
+   local.get $0
+   i32.eqz
+   if
+    global.get $~lib/memory/__stack_pointer
+    i32.const 5
+    call $~lib/rt/itcms/__new
+    local.tee $0
+    i32.store
+   end
+   global.get $~lib/memory/__stack_pointer
+   local.set $2
+   global.get $~lib/memory/__stack_pointer
+   local.get $0
+   i32.store offset=4
+   global.get $~lib/memory/__stack_pointer
+   i32.const 4
+   i32.sub
+   global.set $~lib/memory/__stack_pointer
+   global.get $~lib/memory/__stack_pointer
+   i32.const 1564
+   i32.lt_s
+   br_if $folding-inner0
+   global.get $~lib/memory/__stack_pointer
+   i32.const 0
+   i32.store
+   local.get $0
+   i32.eqz
+   if
+    global.get $~lib/memory/__stack_pointer
+    i32.const 0
+    call $~lib/rt/itcms/__new
+    local.tee $0
+    i32.store
+   end
+   global.get $~lib/memory/__stack_pointer
+   i32.const 4
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   local.get $2
+   local.get $0
+   i32.store
+   global.get $~lib/memory/__stack_pointer
+   i32.const 8
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   local.get $1
+   local.get $0
+   i32.store
+   global.get $~lib/memory/__stack_pointer
+   i32.const 8
+   i32.add
+   global.set $~lib/memory/__stack_pointer
+   local.get $0
+   return
+  end
+  i32.const 34352
+  i32.const 34400
+  i32.const 1
+  i32.const 1
+  call $~lib/builtins/abort
+  unreachable
+ )
+ (func $~lib/string/String.__eq (result i32)
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.sub
+  global.set $~lib/memory/__stack_pointer
+  global.get $~lib/memory/__stack_pointer
+  i32.const 1564
+  i32.lt_s
+  if
+   i32.const 34352
+   i32.const 34400
+   i32.const 1
+   i32.const 1
+   call $~lib/builtins/abort
+   unreachable
+  end
+  global.get $~lib/memory/__stack_pointer
+  i64.const 0
+  i64.store
+  global.get $~lib/memory/__stack_pointer
+  i32.const 8
+  i32.add
+  global.set $~lib/memory/__stack_pointer
+  i32.const 1
+ )
+)
diff --git a/tests/compiler/issues/2904.ts b/tests/compiler/issues/2904.ts
new file mode 100644
index 0000000000..b3708c7a16
--- /dev/null
+++ b/tests/compiler/issues/2904.ts
@@ -0,0 +1,37 @@
+class X {
+
+  // polymorphic 'this' type can be returned from methods
+  doSomething(): this {
+    return this;
+  }
+
+  // polymorphic 'this' type can be used by properties
+  get self(): this {
+    return this;
+  }
+
+  // TODO: implement polymorphic 'this' type for fields to make the following code work:
+  // _foo: this = this;
+  // get foo(): this {
+  //   return this._foo;
+  // }
+  // set foo(value: this) {
+  //   this._foo = value;
+  // }
+
+}
+
+class Y extends X {
+  doSomethingElse(): string {
+    return "You made it!";
+  }
+}
+
+const y1 = new Y().doSomething();
+assert(y1.doSomethingElse() == "You made it!");
+
+const y2 = new Y().self;
+assert(y2.doSomethingElse() == "You made it!");
+
+// const y3 = new Y().foo;
+// assert(y3.doSomethingElse() == "You made it!");

From d1d0cbf2cc70b3ba205eb9adb93d1f3878e3ae0b Mon Sep 17 00:00:00 2001
From: Matt Johnson-Pint <mjp@hypermode.com>
Date: Wed, 5 Feb 2025 15:41:41 -0800
Subject: [PATCH 2/5] add some comments

---
 src/resolver.ts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/resolver.ts b/src/resolver.ts
index 10c6725ec7..21a04b7fcc 100644
--- a/src/resolver.ts
+++ b/src/resolver.ts
@@ -2864,12 +2864,16 @@ export class Resolver extends DiagnosticEmitter {
     // Instance method prototypes are pre-bound to their concrete class as their parent
     if (prototype.is(CommonFlags.Instance)) {
 
+      // The actual class instance may be a subclass of the bound class in the case of
+      // a function or property that uses the polymorphic `this` type
       if (this.currentThisExpression && ctxFlow) {
         let element = this.lookupExpression(this.currentThisExpression, ctxFlow);
         if (element?.kind == ElementKind.Class) {
           classInstance = <Class>element;
         }
       }
+
+      // Otherwise, the bound class is the actual class instance
       if (!classInstance) {
         classInstance = assert(prototype.getBoundClassOrInterface());
       }
@@ -3013,6 +3017,7 @@ export class Resolver extends DiagnosticEmitter {
 
     // check against overridden base member
     if (prototype.is(CommonFlags.Instance)) {
+      // always take the prototype's bound class here - which may differ from the previous classInstance
       let classInstance = assert(prototype.getBoundClassOrInterface());
       let methodOrPropertyName = instance.declaration.name.text;
       let baseClass = classInstance.base;

From 2ef67882a99eac4e65e95e3fdd8a5b6f46e5ee96 Mon Sep 17 00:00:00 2001
From: Matt Johnson-Pint <mjp@hypermode.com>
Date: Wed, 5 Feb 2025 16:35:46 -0800
Subject: [PATCH 3/5] fix bootstrap issues

---
 src/resolver.ts | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/src/resolver.ts b/src/resolver.ts
index 21a04b7fcc..85dd5c0ff9 100644
--- a/src/resolver.ts
+++ b/src/resolver.ts
@@ -2856,7 +2856,7 @@ export class Resolver extends DiagnosticEmitter {
     /** How to proceed with eventual diagnostics. */
     reportMode: ReportMode = ReportMode.Report,
     /** Contextual flow. */
-    ctxFlow?: Flow,
+    ctxFlow: Flow | null = null
   ): Function | null {
     let classInstance: Class | null = null; // if an instance method
     let instanceKey = typeArguments ? typesToString(typeArguments) : "";
@@ -2864,12 +2864,18 @@ export class Resolver extends DiagnosticEmitter {
     // Instance method prototypes are pre-bound to their concrete class as their parent
     if (prototype.is(CommonFlags.Instance)) {
 
-      // The actual class instance may be a subclass of the bound class in the case of
-      // a function or property that uses the polymorphic `this` type
+      // The actual class instance may be a subclass of the bound class
       if (this.currentThisExpression && ctxFlow) {
-        let element = this.lookupExpression(this.currentThisExpression, ctxFlow);
-        if (element?.kind == ElementKind.Class) {
-          classInstance = <Class>element;
+        // In the case of a function or property that uses the polymorphic `this` type,
+        // this is important, so the return is typed as the actual class instance.
+        // Note: It should work without this outer type check, and does when testing, but fails the "bootstrap" build.
+        // TODO: Figure out why and remove the extra check.
+        let type = prototype.functionTypeNode.returnType;
+        if (type.kind == NodeKind.NamedType && (<NamedTypeNode>type).name.identifier.text == CommonNames.this_) {
+          let element = this.lookupExpression(this.currentThisExpression!, ctxFlow);
+          if (element && element.kind == ElementKind.Class && element.is(CommonFlags.Resolved)) {
+            classInstance = <Class>element;
+          }
         }
       }
 
@@ -3512,7 +3518,7 @@ export class Resolver extends DiagnosticEmitter {
                   );
                   break;
                 }
-                if (boundPrototype.typeNode?.kind == NodeKind.NamedType && (<NamedTypeNode>boundPrototype.typeNode).name.identifier.text == CommonNames.this_) {
+                if (assert(boundPrototype.typeNode).kind == NodeKind.NamedType && (<NamedTypeNode>boundPrototype.typeNode).name.identifier.text == CommonNames.this_) {
                   this.error(
                     DiagnosticCode.Not_implemented_0,
                     assert(boundPrototype.typeNode).range,
@@ -3779,7 +3785,7 @@ export class Resolver extends DiagnosticEmitter {
     /** How to proceed with eventual diagnostics. */
     reportMode: ReportMode = ReportMode.Report,
     /** Contextual flow. */
-    ctxFlow?: Flow,
+    ctxFlow: Flow | null = null
   ): Property | null {
     let instance = prototype.instance;
     if (instance) return instance;

From 22e525476a47c2eb272b8d529d5fdd76956fea9c Mon Sep 17 00:00:00 2001
From: Matt Johnson-Pint <mjp@hypermode.com>
Date: Wed, 5 Feb 2025 17:21:29 -0800
Subject: [PATCH 4/5] .

---
 src/resolver.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/resolver.ts b/src/resolver.ts
index 85dd5c0ff9..75e93c3e3c 100644
--- a/src/resolver.ts
+++ b/src/resolver.ts
@@ -2873,7 +2873,7 @@ export class Resolver extends DiagnosticEmitter {
         let type = prototype.functionTypeNode.returnType;
         if (type.kind == NodeKind.NamedType && (<NamedTypeNode>type).name.identifier.text == CommonNames.this_) {
           let element = this.lookupExpression(this.currentThisExpression!, ctxFlow);
-          if (element && element.kind == ElementKind.Class && element.is(CommonFlags.Resolved)) {
+          if (element && element.kind == ElementKind.Class) {
             classInstance = <Class>element;
           }
         }

From b4355c3a9e0a33759374170af7ca00e2c0264725 Mon Sep 17 00:00:00 2001
From: Matt Johnson-Pint <mjp@hypermode.com>
Date: Thu, 6 Feb 2025 14:47:16 -0800
Subject: [PATCH 5/5] .

---
 src/resolver.ts | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/resolver.ts b/src/resolver.ts
index 75e93c3e3c..86d47e5bfc 100644
--- a/src/resolver.ts
+++ b/src/resolver.ts
@@ -3023,10 +3023,8 @@ export class Resolver extends DiagnosticEmitter {
 
     // check against overridden base member
     if (prototype.is(CommonFlags.Instance)) {
-      // always take the prototype's bound class here - which may differ from the previous classInstance
-      let classInstance = assert(prototype.getBoundClassOrInterface());
       let methodOrPropertyName = instance.declaration.name.text;
-      let baseClass = classInstance.base;
+      let baseClass = (assert(prototype.getBoundClassOrInterface())).base;
       if (baseClass) {
         let baseMember = baseClass.getMember(methodOrPropertyName);
         if (baseMember) {