Remove CRTP from ModuleRunnerBase
diff --git a/src/shell-interface.h b/src/shell-interface.h
index 03b4c81..65885e5 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -94,15 +94,15 @@
 
   std::map<Name, Memory> memories;
   std::unordered_map<Name, std::vector<Literal>> tables;
-  std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances;
+  std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances;
 
   ShellExternalInterface(
-    std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances_ = {}) {
+    std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances_ = {}) {
     linkedInstances.swap(linkedInstances_);
   }
   virtual ~ShellExternalInterface() = default;
 
-  ModuleRunner* getImportInstanceOrNull(Importable* import) {
+  ModuleRunnerBase* getImportInstanceOrNull(Importable* import) {
     auto it = linkedInstances.find(import->module);
     if (it == linkedInstances.end()) {
       return nullptr;
@@ -110,7 +110,7 @@
     return it->second.get();
   }
 
-  ModuleRunner* getImportInstance(Importable* import) {
+  ModuleRunnerBase* getImportInstance(Importable* import) {
     auto* ret = getImportInstanceOrNull(import);
     if (!ret) {
       Fatal() << "getImportInstance: unknown import: " << import->module.str
@@ -119,7 +119,7 @@
     return ret;
   }
 
-  void init(Module& wasm, ModuleRunner& instance) override {
+  void init(Module& wasm, ModuleRunnerBase& instance) override {
     ModuleUtils::iterDefinedMemories(wasm, [&](wasm::Memory* memory) {
       auto shellMemory = Memory();
       shellMemory.resize(memory->initial * wasm::Memory::kPageSize);
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h
index 83f861a..a969520 100644
--- a/src/tools/execution-results.h
+++ b/src/tools/execution-results.h
@@ -50,13 +50,13 @@
 
   // The ModuleRunner and this ExternalInterface end up needing links both ways,
   // so we cannot init this in the constructor.
-  ModuleRunner* instance = nullptr;
+  ModuleRunnerBase* instance = nullptr;
 
 public:
   LoggingExternalInterface(
     Loggings& loggings,
     Module& wasm,
-    std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances_ = {})
+    std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances_ = {})
     : ShellExternalInterface(linkedInstances_), loggings(loggings), wasm(wasm) {
     for (auto& exp : wasm.exports) {
       if (exp->kind == ExternalKind::Table && exp->name == "table") {
@@ -294,7 +294,7 @@
     return flow.values;
   }
 
-  void setModuleRunner(ModuleRunner* instance_) { instance = instance_; }
+  void setModuleRunner(ModuleRunnerBase* instance_) { instance = instance_; }
 };
 
 // gets execution results from a wasm module. this is useful for fuzzing
@@ -322,7 +322,7 @@
 
       // Instantiate the second, if there is one (we instantiate both before
       // running anything, so that we match the behavior of fuzz_shell.js).
-      std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances;
+      std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances;
       std::unique_ptr<LoggingExternalInterface> secondInterface;
       std::shared_ptr<ModuleRunner> secondInstance;
       if (second) {
@@ -351,16 +351,16 @@
     }
   }
 
-  void instantiate(ModuleRunner& instance,
+  void instantiate(ModuleRunnerBase& instance,
                    LoggingExternalInterface& interface) {
     // This is not an optimization: we want to execute anything, even relaxed
     // SIMD instructions.
-    instance.setRelaxedBehavior(ModuleRunner::RelaxedBehavior::Execute);
+    instance.setRelaxedBehavior(ModuleRunnerBase::RelaxedBehavior::Execute);
     instance.instantiate();
     interface.setModuleRunner(&instance);
   }
 
-  void callExports(Module& wasm, ModuleRunner& instance) {
+  void callExports(Module& wasm, ModuleRunnerBase& instance) {
     // execute all exported methods (that are therefore preserved through
     // opts)
     for (auto& exp : wasm.exports) {
@@ -507,11 +507,11 @@
 
   bool operator!=(ExecutionResults& other) { return !((*this) == other); }
 
-  FunctionResult run(Function* func, Module& wasm, ModuleRunner& instance) {
+  FunctionResult run(Function* func, Module& wasm, ModuleRunnerBase& instance) {
     // Clear the continuation state after each run of an export.
     struct CleanUp {
-      ModuleRunner& instance;
-      CleanUp(ModuleRunner& instance) : instance(instance) {}
+      ModuleRunnerBase& instance;
+      CleanUp(ModuleRunnerBase& instance) : instance(instance) {}
       ~CleanUp() { instance.clearContinuationStore(); }
     } cleanUp(instance);
 
diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp
index 455c247..7f89651 100644
--- a/src/tools/wasm-ctor-eval.cpp
+++ b/src/tools/wasm-ctor-eval.cpp
@@ -67,12 +67,12 @@
 // the output.
 #define RECOMMENDATION "\n       recommendation: "
 
-class EvallingModuleRunner : public ModuleRunnerBase<EvallingModuleRunner> {
+class EvallingModuleRunner : public ModuleRunnerBase {
 public:
   EvallingModuleRunner(
     Module& wasm,
     ExternalInterface* externalInterface,
-    std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances_ = {})
+    std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances_ = {})
     : ModuleRunnerBase(wasm, externalInterface, linkedInstances_) {}
 
   Flow visitGlobalGet(GlobalGet* curr) {
@@ -84,7 +84,7 @@
                                 global->base.toString());
     }
 
-    return ModuleRunnerBase<EvallingModuleRunner>::visitGlobalGet(curr);
+    return ModuleRunnerBase::visitGlobalGet(curr);
   }
 
   Flow visitTableGet(TableGet* curr) {
@@ -99,7 +99,7 @@
     if (!allowContNew) {
       throw FailToEvalException("cont.new disallowed");
     }
-    return ModuleRunnerBase<EvallingModuleRunner>::visitContNew(curr);
+    return ModuleRunnerBase::visitContNew(curr);
   }
 
   // This needs to be duplicated from ModuleRunner, unfortunately.
@@ -182,7 +182,7 @@
 struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
   Module* wasm;
   EvallingModuleRunner* instance;
-  std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances;
+  std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances;
 
   // A representation of the contents of wasm memory as we execute.
   std::unordered_map<Name, std::vector<char>> memories;
@@ -198,8 +198,7 @@
   bool instanceInitialized = false;
 
   CtorEvalExternalInterface(
-    std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances_ =
-      {}) {
+    std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances_ = {}) {
     linkedInstances.swap(linkedInstances_);
   }
 
@@ -216,9 +215,9 @@
     applyGlobalsToModule();
   }
 
-  void init(Module& wasm_, EvallingModuleRunner& instance_) override {
+  void init(Module& wasm_, ModuleRunnerBase& instance_) override {
     wasm = &wasm_;
-    instance = &instance_;
+    instance = static_cast<EvallingModuleRunner*>(&instance_);
     for (auto& memory : wasm->memories) {
       if (!memory->imported()) {
         std::vector<char> data;
@@ -1354,7 +1353,7 @@
   std::unordered_set<std::string> keptExportsSet(keptExports.begin(),
                                                  keptExports.end());
 
-  std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances;
+  std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances;
 
   // build and link the env module
   auto envModule = buildEnvModule(wasm);
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index 154c045..b050707 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -44,9 +44,9 @@
 
   // Keyed by instance name.
   std::map<Name, std::shared_ptr<ShellExternalInterface>> interfaces;
-  std::map<Name, std::shared_ptr<ModuleRunner>> instances;
+  std::map<Name, std::shared_ptr<ModuleRunnerBase>> instances;
   // Used for imports, keyed by instance name.
-  std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances;
+  std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances;
 
   Name lastInstance;
   std::optional<Name> lastModuleDefinition;
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index d46dde4..8260b5f 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -2956,8 +2956,7 @@
 // To call into the interpreter, use callExport.
 //
 
-template<typename SubType>
-class ModuleRunnerBase : public ExpressionRunner<SubType> {
+class ModuleRunnerBase : public ExpressionRunner<ModuleRunnerBase> {
 public:
   //
   // You need to implement one of these to create a concrete interpreter. The
@@ -2966,9 +2965,9 @@
   //
   struct ExternalInterface {
     ExternalInterface(
-      std::map<Name, std::shared_ptr<SubType>> linkedInstances = {}) {}
+      std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances = {}) {}
     virtual ~ExternalInterface() = default;
-    virtual void init(Module& wasm, SubType& instance) {}
+    virtual void init(Module& wasm, ModuleRunnerBase& instance) {}
     virtual void importGlobals(GlobalValueSet& globals, Module& wasm) = 0;
     virtual Literal getImportedFunction(Function* import) = 0;
     virtual bool growMemory(Name name, Address oldSize, Address newSize) = 0;
@@ -3169,7 +3168,7 @@
     }
   };
 
-  SubType* self() { return static_cast<SubType*>(this); }
+  ModuleRunnerBase* self() { return this; }
 
   // TODO: this duplicates module in ExpressionRunner, and can be removed
   Module& wasm;
@@ -3183,8 +3182,8 @@
   ModuleRunnerBase(
     Module& wasm,
     ExternalInterface* externalInterface,
-    std::map<Name, std::shared_ptr<SubType>> linkedInstances_ = {})
-    : ExpressionRunner<SubType>(&wasm), wasm(wasm),
+    std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances_ = {})
+    : ExpressionRunner<ModuleRunnerBase>(&wasm), wasm(wasm),
       externalInterface(externalInterface), linkedInstances(linkedInstances_) {
     // Set up a single shared CurrContinuations for all these linked instances,
     // reusing one if it exists.
@@ -3302,7 +3301,7 @@
 
   struct TableInstanceInfo {
     // The ModuleRunner instance in which the memory is defined.
-    SubType* instance;
+    ModuleRunnerBase* instance;
     // The external interface in which the table is defined
     ExternalInterface* interface() { return instance->externalInterface; }
     // The name the table has in that interface.
@@ -3358,7 +3357,7 @@
 
   struct MemoryInstanceInfo {
     // The ModuleRunner instance in which the memory is defined.
-    SubType* instance;
+    ModuleRunnerBase* instance;
     // The external interface in which the memory is defined
     ExternalInterface* interface() { return instance->externalInterface; }
     // The name the memory has in that interface.
@@ -3445,13 +3444,13 @@
   public:
     std::vector<Literals> locals;
     Function* function;
-    SubType& parent;
+    ModuleRunnerBase& parent;
 
     FunctionScope* oldScope;
 
     FunctionScope(Function* function,
                   const Literals& arguments,
-                  SubType& parent)
+                  ModuleRunnerBase& parent)
       : function(function), parent(parent) {
       oldScope = parent.scope;
       parent.scope = this;
@@ -3683,7 +3682,7 @@
     return ret;
   }
 
-  Flow visitTableGet(TableGet* curr) {
+  virtual Flow visitTableGet(TableGet* curr) {
     VISIT(index, curr->index)
     auto info = getTableInstanceInfo(curr->table);
     auto address = index.getSingleValue().getUnsigned();
@@ -3844,7 +3843,7 @@
     return curr->isTee() ? flow : Flow();
   }
 
-  Flow visitGlobalGet(GlobalGet* curr) {
+  virtual Flow visitGlobalGet(GlobalGet* curr) {
     auto name = curr->name;
     return getGlobal(name);
   }
@@ -4534,7 +4533,7 @@
     multiValues.pop_back();
     return ret;
   }
-  Flow visitContNew(ContNew* curr) {
+  virtual Flow visitContNew(ContNew* curr) {
     VISIT(funcFlow, curr->func)
     // Create a new continuation for the target function.
     auto funcValue = funcFlow.getSingleValue();
@@ -5053,16 +5052,20 @@
     return externalInterface->store(&store, addr, toStore, memoryName);
   }
 
+  virtual Literal makeFuncData(Name name, Type type) {
+    return ExpressionRunner<ModuleRunnerBase>::makeFuncData(name, type);
+  }
+
   ExternalInterface* externalInterface;
-  std::map<Name, std::shared_ptr<SubType>> linkedInstances;
+  std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances;
 };
 
-class ModuleRunner : public ModuleRunnerBase<ModuleRunner> {
+class ModuleRunner : public ModuleRunnerBase {
 public:
   ModuleRunner(
     Module& wasm,
     ExternalInterface* externalInterface,
-    std::map<Name, std::shared_ptr<ModuleRunner>> linkedInstances = {})
+    std::map<Name, std::shared_ptr<ModuleRunnerBase>> linkedInstances = {})
     : ModuleRunnerBase(wasm, externalInterface, linkedInstances) {}
 
   Literal makeFuncData(Name name, Type type) {