Skip to content
Merged
33 changes: 33 additions & 0 deletions src/ir/import-name.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2026 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef wasm_ir_import_name_h
#define wasm_ir_import_name_h

#include <ostream>

#include "support/name.h"

namespace wasm {

struct ImportNames {
Name module;
Name name;
};

} // namespace wasm

#endif // wasm_ir_import_name_h
32 changes: 32 additions & 0 deletions src/ir/import-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#ifndef wasm_ir_import_h
#define wasm_ir_import_h

#include "ir/import-name.h"
#include "literal.h"
#include "wasm.h"

Expand Down Expand Up @@ -123,6 +124,37 @@ struct ImportInfo {
Index getNumDefinedTags() { return wasm.tags.size() - getNumImportedTags(); }
};

class ImportResolver {
public:
virtual ~ImportResolver() = default;

// Returns null if the `name` wasn't found. The returned Literals* lives as
// long as the ImportResolver instance.
virtual Literals* getGlobalOrNull(ImportNames name, Type type) const = 0;
};

// Looks up imports from the given `linkedInstances`.
template<typename ModuleRunnerType>
class LinkedInstancesImportResolver : public ImportResolver {
public:
LinkedInstancesImportResolver(
std::map<Name, std::shared_ptr<ModuleRunnerType>> linkedInstances)
: linkedInstances(std::move(linkedInstances)) {}

Literals* getGlobalOrNull(ImportNames name, Type type) const override {
auto it = linkedInstances.find(name.module);
if (it == linkedInstances.end()) {
return nullptr;
}

ModuleRunnerType* instance = it->second.get();
return instance->getExportedGlobalOrNull(name.name);
}

private:
const std::map<Name, std::shared_ptr<ModuleRunnerType>> linkedInstances;
};

} // namespace wasm

#endif // wasm_ir_import_h
5 changes: 5 additions & 0 deletions src/passes/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3944,4 +3944,9 @@ std::ostream& operator<<(std::ostream& o, wasm::ModuleHeapType pair) {
return o << "(unnamed)";
}

std::ostream& operator<<(std::ostream& o,
const wasm::ImportNames& importNames) {
return o << importNames.module << "." << importNames.name;
}

} // namespace std
14 changes: 0 additions & 14 deletions src/shell-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,20 +129,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
wasm, [&](Table* table) { tables[table->name].resize(table->initial); });
}

void importGlobals(std::map<Name, Literals>& globals, Module& wasm) override {
ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) {
auto inst = getImportInstance(import);
auto* exportedGlobal = inst->wasm.getExportOrNull(import->base);
if (!exportedGlobal || exportedGlobal->kind != ExternalKind::Global) {
trap((std::stringstream()
<< "importGlobals: unknown import: " << import->module.str << "."
<< import->name.str)
.str());
}
globals[import->name] = inst->globals[*exportedGlobal->getInternalName()];
});
}

Literal getImportedFunction(Function* import) override {
// TODO: We should perhaps restrict the types with which the well-known
// functions can be imported.
Expand Down
59 changes: 20 additions & 39 deletions src/tools/wasm-ctor-eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,27 @@ bool isNullableAndMutable(Expression* ref, Index fieldIndex) {
// the output.
#define RECOMMENDATION "\n recommendation: "

class EvallingModuleRunner;

class EvallingImportResolver : public ImportResolver {
public:
EvallingImportResolver() = default;

Literals* getGlobalOrNull(ImportNames name, Type type) const override {
throw FailToEvalException("Accessed imported global");
}
};

class EvallingModuleRunner : public ModuleRunnerBase<EvallingModuleRunner> {
public:
EvallingModuleRunner(
Module& wasm,
ExternalInterface* externalInterface,
std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances_ = {})
: ModuleRunnerBase(wasm, externalInterface, linkedInstances_) {}
: ModuleRunnerBase(wasm,
externalInterface,
std::make_shared<EvallingImportResolver>(),
linkedInstances_) {}

Flow visitGlobalGet(GlobalGet* curr) {
// Error on reads of imported globals.
Expand Down Expand Up @@ -147,19 +161,6 @@ std::unique_ptr<Module> buildEnvModule(Module& wasm) {
}
});

ModuleUtils::iterImportedGlobals(wasm, [&](Global* global) {
if (global->module == env->name) {
auto* copied = ModuleUtils::copyGlobal(global, *env);
copied->module = Name();
copied->base = Name();

Builder builder(*env);
copied->init = builder.makeConst(Literal::makeZero(global->type));
env->addExport(
builder.makeExport(global->base, copied->name, ExternalKind::Global));
}
});

// create an exported memory with the same initial and max size
ModuleUtils::iterImportedMemories(wasm, [&](Memory* memory) {
if (memory->module == env->name) {
Expand Down Expand Up @@ -231,26 +232,6 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
}
}

void importGlobals(GlobalValueSet& globals, Module& wasm_) override {
ModuleUtils::iterImportedGlobals(wasm_, [&](Global* global) {
auto it = linkedInstances.find(global->module);
if (it != linkedInstances.end()) {
auto* inst = it->second.get();
auto* globalExport = inst->wasm.getExportOrNull(global->base);
if (!globalExport || globalExport->kind != ExternalKind::Global) {
throw FailToEvalException(std::string("importGlobals: ") +
global->module.toString() + "." +
global->base.toString());
}
globals[global->name] = inst->globals[*globalExport->getInternalName()];
} else {
throw FailToEvalException(std::string("importGlobals: ") +
global->module.toString() + "." +
global->base.toString());
}
});
}

Literal getImportedFunction(Function* import) override {
auto f = [import, this](const Literals& arguments) -> Flow {
Name WASI("wasi_snapshot_preview1");
Expand Down Expand Up @@ -558,8 +539,8 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
void applyGlobalsToModule() {
if (!wasm->features.hasGC()) {
// Without GC, we can simply serialize the globals in place as they are.
for (const auto& [name, values] : instance->globals) {
wasm->getGlobal(name)->init = getSerialization(values);
for (const auto& [name, values] : instance->allGlobals) {
wasm->getGlobal(name)->init = getSerialization(*values);
}
return;
}
Expand Down Expand Up @@ -590,9 +571,9 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
// for it. (If there is no value, then this is a new global we've added
// during execution, for whom we've already set up a proper serialized
// value when we created it.)
auto iter = instance->globals.find(oldGlobal->name);
if (iter != instance->globals.end()) {
oldGlobal->init = getSerialization(iter->second, name);
auto iter = instance->allGlobals.find(oldGlobal->name);
if (iter != instance->allGlobals.end()) {
oldGlobal->init = getSerialization(*iter->second, name);
}

// Add the global back to the module.
Expand Down
2 changes: 1 addition & 1 deletion src/tools/wasm-shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ struct Shell {
}
auto& instance = it->second;
try {
return instance->getExportedGlobal(get->name);
return instance->getExportedGlobalOrTrap(get->name);
} catch (TrapException&) {
return TrapResult{};
} catch (...) {
Expand Down
Loading
Loading