Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
39 changes: 39 additions & 0 deletions src/support/nullability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.
*/

// Trivial wrappers to annotate the nullability of pointers.

#ifndef _wasm_support_nullability
#define _wasm_support_nullability

#include <memory>
#include <type_traits>

namespace wasm::nullability {

template<typename T, typename = void> struct is_pointer : std::false_type {};

template<typename T>
struct is_pointer<T, std::void_t<typename std::pointer_traits<T>::element_type>>
: std::true_type {};

template<typename T> using Nullable = std::enable_if_t<is_pointer<T>::value, T>;

template<typename T> using NonNull = std::enable_if_t<is_pointer<T>::value, T>;

} // namespace wasm::nullability

#endif // _wasm_support_nullability
70 changes: 31 additions & 39 deletions src/tools/wasm-ctor-eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "support/colors.h"
#include "support/file.h"
#include "support/insert_ordered.h"
#include "support/nullability.h"
#include "support/small_set.h"
#include "support/string.h"
#include "support/topological_sort.h"
Expand Down Expand Up @@ -67,13 +68,37 @@ bool isNullableAndMutable(Expression* ref, Index fieldIndex) {
// the output.
#define RECOMMENDATION "\n recommendation: "

class EvallingModuleRunner;

class EvallingImportResolver : public ImportResolver {
public:
EvallingImportResolver(
std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances,
ModuleRunnerBase<EvallingModuleRunner>::ExternalInterface*
externalInterface)
: externalInterface(externalInterface) {}

nullability::Nullable<Literals*> getGlobal(QualifiedName name,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually we just say in a comment whether a pointer can be null. I'm not sure the extra type-level wrapper is worth it. Is this useful in your experience?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought is just to make it harder to miss by accident than a comment, and also to give us an easy way to migrate to 'real' nullability annotations in the future (we can just grep for the symbol instead of trying to find comments). Google uses nullability annotations and clang can apparently catch some bugs this way (TOTW 230 but this isn't accessible publicly).

I don't have a strong preference though, I agree that it's noisy to read.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kripken wdyt?

Type type) const override {
externalInterface->trap("Accessed imported global");
return nullptr;
}

private:
ModuleRunnerBase<EvallingModuleRunner>::ExternalInterface* externalInterface;
};

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_, externalInterface),
linkedInstances_) {}

Flow visitGlobalGet(GlobalGet* curr) {
// Error on reads of imported globals.
Expand Down Expand Up @@ -147,19 +172,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 +243,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 +550,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 +582,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