From 0960513213522dbfcb071afb086f0e3747cfdf27 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Thu, 28 May 2026 07:49:34 +0000 Subject: [PATCH] Implement wasi functions Move implementation code into a separate file, and reorganize code Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- src/runtime/Component.h | 2 + src/runtime/ComponentInstance.cpp | 188 ++++++++++++++++++-- src/runtime/ComponentInstance.h | 25 ++- src/runtime/Memory.h | 2 + src/runtime/Store.cpp | 19 +-- src/runtime/Store.h | 18 +- src/shell/Shell.cpp | 14 +- src/wasi/WASI02.cpp | 274 +++++------------------------- src/wasi/WASI02.h | 2 + src/wasi/WASI02Impl.cpp | 179 +++++++++++++++++++ src/wasi/WASI02Impl.h | 191 +++++++++++++++++++++ 11 files changed, 643 insertions(+), 271 deletions(-) create mode 100644 src/wasi/WASI02Impl.cpp create mode 100644 src/wasi/WASI02Impl.h diff --git a/src/runtime/Component.h b/src/runtime/Component.h index d206041f9..d85b4439f 100644 --- a/src/runtime/Component.h +++ b/src/runtime/Component.h @@ -1025,6 +1025,8 @@ class Component { friend class wabt::WASMComponentBinaryReader; public: + static constexpr uint32_t MaxStringByteLength = 0x0fffffff; + struct InlineExport { std::string name; ComponentSort sort; diff --git a/src/runtime/ComponentInstance.cpp b/src/runtime/ComponentInstance.cpp index bc930c2cb..c4ad342a7 100644 --- a/src/runtime/ComponentInstance.cpp +++ b/src/runtime/ComponentInstance.cpp @@ -28,10 +28,159 @@ namespace Walrus { DEFINE_GLOBAL_TYPE_INFO(componentInstanceTypeInfo, ComponentInstanceKind); -LoweredFunction* LoweredFunction::createLoweredFunction(Store* store, const FunctionType* functionType, LiftedFunction* liftedFunction, CanonOptions* options) +static void throwException(ExecutionState& state, const char* message) +{ + std::string stringMessage = message; + Trap::throwException(state, stringMessage); +} + +void CanonOptions::memoryCheckRange32(ExecutionState& state, uint32_t align, uint32_t start, uint32_t size) +{ + ASSERT(!memory()->is64() && align <= 8 && (align & (align - 1)) == 0); + + align = start & (align - 1); + if (align == 0 && memory()->sizeInByte() >= size && memory()->sizeInByte() - size >= start) { + return; + } + + throwException(state, align != 0 ? "incorrectly aligned memory area" : "out of bounds memory area"); +} + +void CanonOptions::memoryCheckRange64(ExecutionState& state, uint64_t align, uint64_t start, uint64_t size) +{ + ASSERT(memory()->is64() && align <= 8 && (align & (align - 1)) == 0); + + align = start & (align - 1); + if (align == 0 && memory()->sizeInByte() >= size && memory()->sizeInByte() - size >= start) { + return; + } + + throwException(state, align != 0 ? "incorrectly aligned memory area" : "out of bounds memory area"); +} + +uint32_t CanonOptions::memoryMalloc32(ExecutionState& state, uint32_t align, uint32_t size) +{ + ASSERT(!memory()->is64() && realloc() != nullptr && align <= 8 && (align & (align - 1)) == 0); + + Value argv[4]; + Value result; + argv[0] = Value(static_cast(0)); + argv[1] = Value(static_cast(0)); + argv[2] = Value(static_cast(align)); + argv[3] = Value(static_cast(size)); + + // Should trap on an error (unreachable). + realloc()->call(state, argv, &result); + uint32_t start = static_cast(result.asI32()); + memoryCheckRange32(state, align, start, size); + return start; +} + +uint64_t CanonOptions::memoryMalloc64(ExecutionState& state, uint64_t align, uint64_t size) +{ + ASSERT(memory()->is64() && realloc() != nullptr && align <= 8 && (align & (align - 1)) == 0); + + Value argv[4]; + Value result; + argv[0] = Value(static_cast(0)); + argv[1] = Value(static_cast(0)); + argv[2] = Value(static_cast(align)); + argv[3] = Value(static_cast(size)); + + // Should trap on an error (unreachable). + realloc()->call(state, argv, &result); + uint64_t start = static_cast(result.asI64()); + memoryCheckRange64(state, align, start, size); + return start; +} + +enum Utf8Consts : uint8_t { + Utf8Len1Mask = 0x7f, + Utf8Len2 = 0xc0, + Utf8Len2Mask = 0x1f, + Utf8Cont = 0x80, + Utf8ContMask = 0x3f, + Utf8ContShift1 = 6, + Utf8ContShift2 = 12, + Utf8ContShift3 = 18, +}; + +uint64_t CanonOptions::storeLatin1String(ExecutionState& state, const uint8_t* src, uint32_t* length) +{ + uint32_t codeUnitLength = *length; + uint32_t byteLength = codeUnitLength; + uint32_t align = 2; + const uint8_t* end = src + codeUnitLength; + + switch (encoding()) { + case ComponentCanonOptions::Utf8: + align = 1; + for (const uint8_t* ptr = src; ptr < end; ptr++) { + if ((*ptr & ~Utf8Len1Mask) != 0) { + byteLength++; + } + } + *length = byteLength; + break; + case ComponentCanonOptions::Utf16: + byteLength <<= 1; + break; + default: + ASSERT(encoding() == ComponentCanonOptions::Latin1Utf16); + break; + } + + if (byteLength > Component::MaxStringByteLength) { + throwException(state, "result string too large"); + } + + uint64_t start; + uint8_t* ptr; + if (memory()->is64()) { + start = memoryMalloc64(state, align, byteLength); + ptr = reinterpret_cast(memory()->buffer() + start); + } else { + uint32_t start32 = memoryMalloc32(state, align, byteLength); + ptr = reinterpret_cast(memory()->buffer() + start32); + start = start32; + } + + switch (encoding()) { + case ComponentCanonOptions::Utf8: + if (byteLength == codeUnitLength) { + break; + } + + while (src < end) { + if ((*src & ~Utf8Len1Mask) == 0) { + *ptr++ = *src; + } else { + ptr[0] = static_cast(Utf8Len2 | (*src >> Utf8ContShift1)); + ptr[1] = static_cast(Utf8Cont | (*src & Utf8ContMask)); + ptr += 2; + } + src++; + } + return start; + case ComponentCanonOptions::Utf16: + while (src < end) { + ptr[0] = *src++; + ptr[1] = 0; + ptr += 2; + } + return start; + default: + ASSERT(byteLength == codeUnitLength); + break; + } + memcpy(ptr, src, byteLength); + return start; +} + +LoweredFunction* LoweredFunction::createLoweredFunction(const FunctionType* functionType, LiftedFunction* liftedFunction, CanonOptions* options) { LoweredFunction* func = new LoweredFunction(functionType, liftedFunction, options); - store->appendExtern(func); + options->instance()->store()->appendExtern(func); return func; } @@ -84,9 +233,10 @@ void CanonFunction::call(ExecutionState& state, Value* argv, Value* result) } } -ComponentInstance::ComponentInstance(ComponentType* type) +ComponentInstance::ComponentInstance(Store* store, ComponentType* type) : Object(GET_GLOBAL_TYPE_INFO(componentInstanceTypeInfo)) , m_type(type) + , m_store(store) , m_freeResourceHandle(LastHandle) { type->addRef(); @@ -167,7 +317,7 @@ void ComponentInstance::throwInvalidHandle(ExecutionState& state, uint32_t index ComponentInstance* ComponentInstance::createInstance(Store* store, ComponentType* type) { - ComponentInstance* instance = new ComponentInstance(type); + ComponentInstance* instance = new ComponentInstance(store, type); store->appendComponentInstance(instance); return instance; } @@ -218,7 +368,7 @@ bool ComponentInstance::compareTypes(ComponentRefCounted* expected, ComponentRef return true; } -void ComponentInstance::coreInstantiate(ExecutionState& state, Store* store, Component* component, ComponentCoreInstantiate* instantiate) +void ComponentInstance::coreInstantiate(ExecutionState& state, Component* component, ComponentCoreInstantiate* instantiate) { ExternVector imports; Module* module = component->modules()[instantiate->moduleIndex()]; @@ -487,24 +637,24 @@ void ComponentInstance::aliasInline(ComponentAliasInline* alias) } } -void ComponentInstance::liftFunction(Store* store, std::vector& canonOptions, ComponentCanonLift* lift) +void ComponentInstance::liftFunction(std::vector& canonOptions, ComponentCanonLift* lift) { CanonOptions* options = canonOptions[lift->options()]; m_funcs.push_back(new LiftedCoreFunction(m_coreFuncs[lift->coreFuncIndex()], options)); } -void ComponentInstance::lowerFunction(Store* store, std::vector& canonOptions, ComponentCanonLower* lower) +void ComponentInstance::lowerFunction(std::vector& canonOptions, ComponentCanonLower* lower) { LiftedFunction* func = m_funcs[lower->funcIndex()]; CanonOptions* options = canonOptions[lower->options()]; #ifdef ENABLE_WASI if (func->kind() == LiftedFunction::WasiFunctionKind) { - m_coreFuncs.push_back(LoweredFunction::createLoweredFunction(store, getWasiFunctionType(func->asLiftedWasiFunction()), func, options)); + m_coreFuncs.push_back(LoweredFunction::createLoweredFunction(getWasiFunctionType(func->asLiftedWasiFunction()), func, options)); return; } #endif /* ENABLE_WASI */ - m_coreFuncs.push_back(LoweredFunction::createLoweredFunction(store, func->asLiftedCoreFunction()->function()->functionType(), func, options)); + m_coreFuncs.push_back(LoweredFunction::createLoweredFunction(func->asLiftedCoreFunction()->function()->functionType(), func, options)); } ComponentInstance* ComponentInstance::InstantiateContext::instantiate(Component* component, ComponentInstance* parent, ComponentInstantiate* arg) @@ -516,7 +666,7 @@ ComponentInstance* ComponentInstance::InstantiateContext::instantiate(Component* for (auto it : component->declarations()) { switch (it->kind()) { case ComponentDeclaration::CoreInstantiateKind: { - instance->coreInstantiate(m_state, m_store, component, it->asCoreInstantiate()); + instance->coreInstantiate(m_state, component, it->asCoreInstantiate()); break; } case ComponentDeclaration::InstantiateKind: { @@ -542,15 +692,29 @@ ComponentInstance* ComponentInstance::InstantiateContext::instantiate(Component* Function* realloc = options->reallocIndex() == ComponentCanonOptions::NotDefined ? nullptr : instance->m_coreFuncs[options->reallocIndex()]; Function* postReturn = options->postReturnIndex() == ComponentCanonOptions::NotDefined ? nullptr : instance->m_coreFuncs[options->postReturnIndex()]; Function* callback = options->callbackIndex() == ComponentCanonOptions::NotDefined ? nullptr : instance->m_coreFuncs[options->callbackIndex()]; + + if (realloc != nullptr) { + ASSERT(memory != nullptr); + Value::Type type = memory->is64() ? Value::I64 : Value::I32; + + const TypeVector::Types& param = realloc->functionType()->param().types(); + const TypeVector::Types& result = realloc->functionType()->result().types(); + if (param.size() != 4 || param[0] != type || param[1] != type || param[2] != type || param[3] != type + || result.size() != 1 || result[0] != type) { + std::string message = "invalid realloc function"; + Trap::throwException(m_state, message); + } + } + instance->m_canonOptions.push_back(new CanonOptions(instance, options->encoding(), options->isAsync(), memory, realloc, postReturn, callback)); break; } case ComponentDeclaration::CanonLiftKind: { - instance->liftFunction(m_store, instance->m_canonOptions, it->asCanonLift()); + instance->liftFunction(instance->m_canonOptions, it->asCanonLift()); break; } case ComponentDeclaration::CanonLowerKind: { - instance->lowerFunction(m_store, instance->m_canonOptions, it->asCanonLower()); + instance->lowerFunction(instance->m_canonOptions, it->asCanonLower()); break; } case ComponentDeclaration::CanonResourceDrop: { diff --git a/src/runtime/ComponentInstance.h b/src/runtime/ComponentInstance.h index 49552acb7..a27cd90ec 100644 --- a/src/runtime/ComponentInstance.h +++ b/src/runtime/ComponentInstance.h @@ -26,6 +26,8 @@ class ComponentInstance; class CanonOptions { public: + static constexpr uint32_t Utf16Tag = static_cast(1) << 31; + CanonOptions(ComponentInstance* instance, ComponentCanonOptions::StringEncoding encoding, bool isAsync, Memory* memory, Function* realloc, Function* postReturn, Function* callback) : m_instance(instance) @@ -73,6 +75,13 @@ class CanonOptions { return m_callback; } + void memoryCheckRange32(ExecutionState& state, uint32_t align, uint32_t start, uint32_t size); + void memoryCheckRange64(ExecutionState& state, uint64_t align, uint64_t start, uint64_t size); + uint32_t memoryMalloc32(ExecutionState& state, uint32_t align, uint32_t size); + uint64_t memoryMalloc64(ExecutionState& state, uint64_t align, uint64_t size); + + uint64_t storeLatin1String(ExecutionState& state, const uint8_t* src, uint32_t* length); + private: ComponentInstance* m_instance; ComponentCanonOptions::StringEncoding m_encoding; @@ -169,7 +178,7 @@ class LiftedCoreFunction : public LiftedFunction { class LoweredFunction : public NativeFunction { public: - static LoweredFunction* createLoweredFunction(Store* store, const FunctionType* functionType, LiftedFunction* liftedFunction, CanonOptions* options); + static LoweredFunction* createLoweredFunction(const FunctionType* functionType, LiftedFunction* liftedFunction, CanonOptions* options); virtual Kind kind() const override { @@ -346,6 +355,11 @@ class ComponentInstance : public Object { return m_type; } + Store* store() + { + return m_store; + } + LiftedFunction* getFunction(uint32_t index) { return m_funcs[index]; @@ -370,7 +384,7 @@ class ComponentInstance : public Object { } private: - ComponentInstance(ComponentType* type); + ComponentInstance(Store* store, ComponentType* type); class InstantiateContext { public: @@ -396,14 +410,15 @@ class ComponentInstance : public Object { static ComponentInstance* createInstance(Store* store, ComponentType* type); static bool compareTypes(ComponentRefCounted* expected, ComponentRefCounted* provided, std::vector& resources); - void coreInstantiate(ExecutionState& state, Store* store, Component* component, ComponentCoreInstantiate* instantiate); + void coreInstantiate(ExecutionState& state, Component* component, ComponentCoreInstantiate* instantiate); void aliasExport(ComponentAliasExport* alias); void aliasCoreExport(ComponentAliasExport* alias); void aliasInline(ComponentAliasInline* alias); - void liftFunction(Store* store, std::vector& canonOptions, ComponentCanonLift* lift); - void lowerFunction(Store* store, std::vector& canonOptions, ComponentCanonLower* lower); + void liftFunction(std::vector& canonOptions, ComponentCanonLift* lift); + void lowerFunction(std::vector& canonOptions, ComponentCanonLower* lower); ComponentType* m_type; + Store* m_store; uintptr_t m_freeResourceHandle; std::vector m_coreFuncs; std::vector m_coreTables; diff --git a/src/runtime/Memory.h b/src/runtime/Memory.h index ec90daee6..28d0f534a 100644 --- a/src/runtime/Memory.h +++ b/src/runtime/Memory.h @@ -34,6 +34,8 @@ class Memory : public Extern { public: static const uint32_t s_memoryPageSize = 1024 * 64; static const uint64_t s_maxMemory64Grow = ~static_cast(0) / s_memoryPageSize; + static const uint64_t s_maxMemory64 = ~static_cast(0); + static const uint32_t s_maxMemory32 = ~static_cast(0); // Caching memory target for fast access. struct TargetBuffer { diff --git a/src/runtime/Store.cpp b/src/runtime/Store.cpp index 18729fb3f..9980de0c6 100644 --- a/src/runtime/Store.cpp +++ b/src/runtime/Store.cpp @@ -40,6 +40,9 @@ static const FunctionType g_defaultFunctionTypes[] = { Store::Store(Engine* engine) : m_engine(engine) +#ifdef ENABLE_WASI + , m_wasiData(nullptr) +#endif { memset(m_definedFuncTypes, 0, sizeof(m_definedFuncTypes)); #ifdef ENABLE_GC @@ -117,22 +120,6 @@ Waiter* Store::getWaiter(void* address) return waiter; } -#ifdef ENABLE_WASI -void Store::registerWasiComponentInstance(size_t instanceId, ComponentInstance* instance) -{ - m_wasiInstances[instanceId] = instance; -} - -ComponentInstance* Store::findWasiComponentInstance(size_t instanceId) -{ - auto it = m_wasiInstances.find(instanceId); - if (it == m_wasiInstances.end()) { - return nullptr; - } - return it->second; -} -#endif - FunctionType* Store::createDefinedFunctionType(DefinedFunctionType type) { const CompositeType** noIndex = reinterpret_cast(TypeStore::NoIndex); diff --git a/src/runtime/Store.h b/src/runtime/Store.h index 1ebb564b9..03d0c6d79 100644 --- a/src/runtime/Store.h +++ b/src/runtime/Store.h @@ -33,6 +33,10 @@ class ComponentInstance; class Extern; class FunctionType; +#ifdef ENABLE_WASI +class WasiStoreData; +#endif + struct Waiter { struct WaiterItem { WaiterItem(Waiter* waiter) @@ -181,8 +185,16 @@ class Store { } #ifdef ENABLE_WASI - void registerWasiComponentInstance(size_t instanceId, ComponentInstance* instance); - ComponentInstance* findWasiComponentInstance(size_t instanceId); + WasiStoreData* wasiData() const + { + return m_wasiData; + } + + void initWasiData(WasiStoreData* data) + { + ASSERT(m_wasiData == nullptr); + m_wasiData = data; + } #endif private: @@ -204,7 +216,7 @@ class Store { ComponentContext* m_context; #ifdef ENABLE_WASI - std::map m_wasiInstances; + WasiStoreData* m_wasiData; #endif }; diff --git a/src/shell/Shell.cpp b/src/shell/Shell.cpp index 82a492735..955d288bd 100644 --- a/src/shell/Shell.cpp +++ b/src/shell/Shell.cpp @@ -31,6 +31,7 @@ #ifdef ENABLE_WASI #include "wasi/WASI.h" +#include "wasi/WASI02.h" #endif struct spectestseps : std::numpunct { @@ -43,7 +44,7 @@ struct ParseOptions { std::vector fileNames; // WASI options - std::vector wasi_envs; + std::vector wasi_envs; std::vector> wasi_dirs; int argsIndex = -1; }; @@ -1315,12 +1316,7 @@ int main(int argc, const char* argv[]) #ifdef ENABLE_WASI // initialize WASI uvwasi_t uvwasi; - - std::vector envp; - for (auto& s : options.wasi_envs) { - envp.push_back(s.c_str()); - } - envp.push_back(nullptr); + options.wasi_envs.push_back(nullptr); std::vector dirs; for (auto& dir : options.wasi_dirs) { @@ -1334,7 +1330,7 @@ int main(int argc, const char* argv[]) init_options.fd_table_size = 3; init_options.argc = (options.argsIndex == -1 ? 0 : argc - options.argsIndex); init_options.argv = (options.argsIndex == -1 ? nullptr : argv + options.argsIndex); - init_options.envp = envp.data(); + init_options.envp = options.wasi_envs.data(); init_options.preopenc = dirs.size(); init_options.preopens = dirs.data(); init_options.preopen_socketc = 0; @@ -1344,6 +1340,8 @@ int main(int argc, const char* argv[]) assert(err == UVWASI_ESUCCESS); WASI::initialize(&uvwasi); + // Wasi 0.2 + store->initWasiData(wasi02InitData(init_options.argc, init_options.argv, init_options.envp)); #endif int result = 0; diff --git a/src/wasi/WASI02.cpp b/src/wasi/WASI02.cpp index 6bf88555a..3249a6617 100644 --- a/src/wasi/WASI02.cpp +++ b/src/wasi/WASI02.cpp @@ -17,12 +17,53 @@ #ifdef ENABLE_WASI #include "wasi/WASI02.h" -#include "runtime/ComponentInstance.h" -#include "runtime/Memory.h" +#include "wasi/WASI02Impl.h" #include "runtime/Store.h" namespace Walrus { +WasiStoreData::WasiStoreData(int argc, const char** argv, const char** envp) +{ + m_arguments.reserve(static_cast(argc)); + while (argc-- > 0) { + m_arguments.push_back(*argv++); + } + + if (envp != nullptr) { + while (*envp != nullptr) { + const char* name = *envp; + const char* value = name; + + while (true) { + if (*value == '\0') { + m_environment.push_back(std::pair(std::string(name, value - name), "")); + break; + } else if (*value == '=') { + value++; + m_environment.push_back(std::pair(std::string(name, value - name - 1), value)); + break; + } + value++; + } + envp++; + } + } +} + +WasiStoreData* wasi02InitData(int argc, const char** argv, const char** envp) +{ + return new WasiStoreData(argc, argv, envp); +} + +static ComponentInstance* findWasiComponentInstance(Store* store, size_t instanceId) +{ + auto it = store->wasiData()->wasiInstances().find(instanceId); + if (it == store->wasiData()->wasiInstances().end()) { + return nullptr; + } + return it->second; +} + enum WasiNamedInstances : size_t { InstanceUnknown, InstanceIoError02, @@ -44,146 +85,6 @@ enum WasiNamedInstances : size_t { InstanceFileSystemPreOpens02, }; -class ComponentResourceWasiStream : public ComponentResource { - friend class ComponentResourceWasiPollable; - -public: - ComponentResourceWasiStream(ComponentTypeResource* type, FILE* file) - : ComponentResource(ResourceWasiStreamKind, type) - , m_file(file) - , m_pollableCount(0) - { - } - - FILE* file() const - { - return m_file; - } - - size_t pollableCount() const - { - return m_pollableCount; - } - -private: - FILE* m_file; - size_t m_pollableCount; -}; - -class ComponentResourceWasiPollable : public ComponentResource { -public: - ComponentResourceWasiPollable(ComponentTypeResource* type, ComponentResourceWasiStream* stream) - : ComponentResource(ResourceWasiPollableKind, type) - , m_stream(stream) - { - stream->m_pollableCount++; - } - - ~ComponentResourceWasiPollable() - { - m_stream->m_pollableCount--; - } - - ComponentResourceWasiStream* stream() const - { - return m_stream; - } - -private: - ComponentResourceWasiStream* m_stream; -}; - -class ComponentResourceWasiTerminal : public ComponentResource { -public: - ComponentResourceWasiTerminal(ComponentTypeResource* type, int fileNo) - : ComponentResource(ResourceWasiTerminalKind, type) - , m_fileNo(fileNo) - { - } - - int fileNo() const - { - return m_fileNo; - } - -private: - int m_fileNo; -}; - -static ComponentResourceWasiStream* asStream(ComponentHandle* handle) -{ - ASSERT(handle->kind() == ComponentHandle::ResourceWasiStreamKind); - return reinterpret_cast(handle); -} - -class LiftedWasiFunction : public LiftedFunction { -public: - enum Type { - cliExit02, - ioPollableBlock02, - ioPoll02, - ioInputStreamRead02, - ioInputStreamSubscribe02, - ioOutputStreamCheckWrite02, - ioOutputStreamWrite02, - ioOutputStreamBlockingWriteAndFlush02, - ioOutputStreamBlockingFlush02, - ioOutputStreamSubscribe02, - cliGetEnvironment02, - cliGetArguments02, - cliGetStdin02, - cliGetStdout02, - cliGetStderr02, - cliGetTerminalStdin02, - cliGetTerminalStdout02, - cliGetTerminalStderr02, - clockMonotonicNow02, - clockSubscribeDuration02, - clockWallNow02, - fileSystemDescriptorReadViaStream02, - fileSystemDescriptorWriteViaStream02, - fileSystemDescriptorAppendViaStream02, - fileSystemDescriptorGetFlags02, - fileSystemDescriptorStat02, - fileSystemDescriptorOpenAt02, - fileSystemDescriptorMetadataHash02, - fileSystemGetDirectories02, - }; - - LiftedWasiFunction(Type type, ComponentInstance* instance, FunctionType* functionType) - : LiftedFunction() - , m_type(type) - , m_instance(instance) - , m_functionType(functionType) - { - } - - virtual Kind kind() const override - { - return WasiFunctionKind; - } - - Type type() const - { - return m_type; - } - - ComponentInstance* instance() const - { - return m_instance; - } - - FunctionType* functionType() const - { - return m_functionType; - } - -private: - Type m_type; - ComponentInstance* m_instance; - FunctionType* m_functionType; -}; - class ComponentInstanceWasi02 { public: ComponentInstanceWasi02(Store* store) @@ -251,7 +152,7 @@ static bool compareName(const char* name, size_t length, const char* expected) ComponentInstance* ComponentInstanceWasi02::loadInstance(size_t instanceId, bool useCache) { if (useCache) { - ComponentInstance* instance = m_store->findWasiComponentInstance(instanceId); + ComponentInstance* instance = findWasiComponentInstance(m_store, instanceId); if (instance != nullptr) { return instance; } @@ -539,6 +440,7 @@ ComponentInstance* ComponentInstanceWasi02::loadInstance(size_t instanceId, bool ComponentInstance* wasi02LoadInstance(Store* store, std::string& name) { + ASSERT(store->wasiData() != nullptr); size_t length = name.length(); const char* charData = name.data(); @@ -628,14 +530,14 @@ ComponentInstance* wasi02LoadInstance(Store* store, std::string& name) return nullptr; } - ComponentInstance* instance = store->findWasiComponentInstance(instanceId); + ComponentInstance* instance = findWasiComponentInstance(store, instanceId); if (instance != nullptr) { return instance; } ComponentInstanceWasi02 instanceCreator(store); instance = instanceCreator.loadInstance(instanceId, false); - store->registerWasiComponentInstance(instanceId, instance); + store->wasiData()->wasiInstances()[instanceId] = instance; return instance; } @@ -644,88 +546,6 @@ const FunctionType* getWasiFunctionType(LiftedWasiFunction* function) return function->functionType(); } -void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedWasiFunction* function, CanonOptions* options) -{ - ComponentInstance* instance = function->instance(); - - switch (function->type()) { - case LiftedWasiFunction::ioOutputStreamCheckWrite02: { - uint32_t offset = argv[1].asI32(); - uint64_t value = 0xffffffff; - options->memory()->store(state, offset, 8, value); - options->memory()->buffer()[offset] = 0; - break; - } - case LiftedWasiFunction::ioOutputStreamWrite02: { - uint32_t index = argv[0].asI32(); - ComponentHandle* handle = options->instance()->getHandle(state, index); - if (handle->kind() != ComponentHandle::ResourceWasiStreamKind) { - ComponentInstance::throwInvalidHandle(state, index); - } - - uint32_t bufferStart = argv[1].asI32(); - uint32_t bufferLength = argv[2].asI32(); - fwrite(options->memory()->buffer() + bufferStart, bufferLength, 1, asStream(handle)->file()); - - uint32_t offset = argv[3].asI32(); - options->memory()->buffer()[offset] = 0; - break; - } - case LiftedWasiFunction::ioOutputStreamBlockingFlush02: { - uint32_t offset = argv[1].asI32(); - options->memory()->buffer()[offset] = 0; - break; - } - case LiftedWasiFunction::ioOutputStreamSubscribe02: { - uint32_t index = argv[0].asI32(); - ComponentHandle* handle = options->instance()->getHandle(state, index); - if (handle->kind() != ComponentHandle::ResourceWasiStreamKind) { - ComponentInstance::throwInvalidHandle(state, index); - } - - ComponentResource* resource = new ComponentResourceWasiPollable(instance->type()->getType(0)->asTypeResource(), asStream(handle)); - result[0] = Value(static_cast(options->instance()->appendHandle(state, resource))); - break; - } - case LiftedWasiFunction::cliGetStdout02: { - ComponentResource* resource = new ComponentResourceWasiStream(instance->type()->getType(0)->asTypeResource(), stdout); - result[0] = Value(static_cast(options->instance()->appendHandle(state, resource))); - break; - } - case LiftedWasiFunction::cliGetTerminalStdout02: { - ComponentResource* resource = new ComponentResourceWasiTerminal(instance->type()->getType(0)->asTypeResource(), 1); - uint32_t offset = argv[0].asI32(); - uint32_t value = options->instance()->appendHandle(state, resource); - options->memory()->store(state, offset, 4, value); - options->memory()->buffer()[offset] = 1; - break; - } - default: - std::string message = "unimplemented wasi function"; - Trap::throwException(state, message); - break; - } -} - -bool dropWasiResource(ExecutionState& state, ComponentHandle* handle) -{ - switch (handle->kind()) { - case ComponentHandle::ResourceWasiStreamKind: - if (asStream(handle)->pollableCount() != 0) { - std::string message = "stream cannot be destroyed (has assigned pollable)"; - Trap::throwException(state, message); - } - break; - case ComponentHandle::ResourceWasiPollableKind: - break; - case ComponentHandle::ResourceWasiTerminalKind: - break; - default: - return false; - } - return true; -} - } // namespace Walrus #endif diff --git a/src/wasi/WASI02.h b/src/wasi/WASI02.h index fa758aa05..d5e2be1bc 100644 --- a/src/wasi/WASI02.h +++ b/src/wasi/WASI02.h @@ -24,11 +24,13 @@ namespace Walrus { +class WasiStoreData; class ComponentInstance; class CanonOptions; class ComponentHandle; class LiftedWasiFunction; +WasiStoreData* wasi02InitData(int argc, const char** argv, const char** envp); ComponentInstance* wasi02LoadInstance(Store* store, std::string& name); const FunctionType* getWasiFunctionType(LiftedWasiFunction* function); void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedWasiFunction* function, CanonOptions* options); diff --git a/src/wasi/WASI02Impl.cpp b/src/wasi/WASI02Impl.cpp new file mode 100644 index 000000000..f4caed89f --- /dev/null +++ b/src/wasi/WASI02Impl.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2023-present Samsung Electronics Co., Ltd + * + * 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. + */ + +#ifdef ENABLE_WASI + +#include "wasi/WASI02.h" +#include "wasi/WASI02Impl.h" +#include "runtime/Memory.h" + +namespace Walrus { + +static void throwNoMemory(ExecutionState& state) +{ + std::string message = "out of memory"; + Trap::throwException(state, message); +} + +static ComponentResourceWasiStream* asStream(ComponentHandle* handle) +{ + ASSERT(handle->kind() == ComponentHandle::ResourceWasiStreamKind); + return reinterpret_cast(handle); +} + +void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedWasiFunction* function, CanonOptions* options) +{ + ComponentInstance* instance = function->instance(); + + switch (function->type()) { + case LiftedWasiFunction::ioOutputStreamCheckWrite02: { + uint32_t offset = argv[1].asI32(); + uint64_t value = 0xffffffff; + options->memory()->store(state, offset, 8, value); + options->memory()->buffer()[offset] = 0; + break; + } + case LiftedWasiFunction::ioOutputStreamWrite02: { + uint32_t index = argv[0].asI32(); + ComponentHandle* handle = options->instance()->getHandle(state, index); + if (handle->kind() != ComponentHandle::ResourceWasiStreamKind) { + ComponentInstance::throwInvalidHandle(state, index); + } + + ASSERT(!options->memory()->is64()); + uint32_t bufferStart = argv[1].asI32(); + uint32_t bufferSize = argv[2].asI32(); + options->memoryCheckRange32(state, 1, bufferStart, bufferSize); + fwrite(options->memory()->buffer() + bufferStart, bufferSize, 1, asStream(handle)->file()); + + uint32_t offset = argv[3].asI32(); + options->memory()->buffer()[offset] = 0; + break; + } + case LiftedWasiFunction::ioOutputStreamBlockingFlush02: { + uint32_t offset = argv[1].asI32(); + options->memory()->buffer()[offset] = 0; + break; + } + case LiftedWasiFunction::ioOutputStreamSubscribe02: { + uint32_t index = argv[0].asI32(); + ComponentHandle* handle = options->instance()->getHandle(state, index); + if (handle->kind() != ComponentHandle::ResourceWasiStreamKind) { + ComponentInstance::throwInvalidHandle(state, index); + } + + ComponentResource* resource = new ComponentResourceWasiPollable(instance->type()->getType(0)->asTypeResource(), asStream(handle)); + result[0] = Value(static_cast(options->instance()->appendHandle(state, resource))); + break; + } + case LiftedWasiFunction::cliGetEnvironment02: { + uint32_t offset = argv[0].asI32(); + const std::vector>& environment = instance->store()->wasiData()->environment(); + + ASSERT(!options->memory()->is64()); + if (environment.size() >= Memory::s_maxMemory32 >> 4) { + throwNoMemory(state); + } + + uint32_t length = static_cast(environment.size()); + uint32_t start = options->memoryMalloc32(state, 4, length << 4); + uint32_t* argBuffer = reinterpret_cast(options->memory()->buffer() + start); + options->memory()->store(state, offset, 0, start); + options->memory()->store(state, offset, 4, length); + + for (auto& it : environment) { + if (it.first.length() >= Component::MaxStringByteLength || it.second.length() >= Component::MaxStringByteLength) { + throwNoMemory(state); + } + length = static_cast(it.first.length()); + start = static_cast(options->storeLatin1String(state, reinterpret_cast(it.first.data()), &length)); + *argBuffer++ = start; + *argBuffer++ = length; + length = static_cast(it.second.length()); + start = static_cast(options->storeLatin1String(state, reinterpret_cast(it.second.data()), &length)); + *argBuffer++ = start; + *argBuffer++ = length; + } + break; + } + case LiftedWasiFunction::cliGetArguments02: { + uint32_t offset = argv[0].asI32(); + const std::vector& arguments = instance->store()->wasiData()->arguments(); + + ASSERT(!options->memory()->is64()); + if (arguments.size() >= Memory::s_maxMemory32 >> 3) { + throwNoMemory(state); + } + + uint32_t length = static_cast(arguments.size()); + uint32_t start = options->memoryMalloc32(state, 4, length << 3); + uint32_t* argBuffer = reinterpret_cast(options->memory()->buffer() + start); + options->memory()->store(state, offset, 0, start); + options->memory()->store(state, offset, 4, length); + + for (auto& it : arguments) { + if (it.length() >= Component::MaxStringByteLength) { + throwNoMemory(state); + } + length = static_cast(it.length()); + start = static_cast(options->storeLatin1String(state, reinterpret_cast(it.data()), &length)); + *argBuffer++ = start; + *argBuffer++ = length; + } + break; + } + case LiftedWasiFunction::cliGetStdout02: { + ComponentResource* resource = new ComponentResourceWasiStream(instance->type()->getType(0)->asTypeResource(), stdout); + result[0] = Value(static_cast(options->instance()->appendHandle(state, resource))); + break; + } + case LiftedWasiFunction::cliGetTerminalStdout02: { + ComponentResource* resource = new ComponentResourceWasiTerminal(instance->type()->getType(0)->asTypeResource(), 1); + uint32_t offset = argv[0].asI32(); + uint32_t value = options->instance()->appendHandle(state, resource); + options->memory()->store(state, offset, 4, value); + options->memory()->buffer()[offset] = 1; + break; + } + default: + std::string message = "unimplemented wasi function"; + Trap::throwException(state, message); + break; + } +} + +bool dropWasiResource(ExecutionState& state, ComponentHandle* handle) +{ + switch (handle->kind()) { + case ComponentHandle::ResourceWasiStreamKind: + if (asStream(handle)->pollableCount() != 0) { + std::string message = "stream cannot be destroyed (has assigned pollable)"; + Trap::throwException(state, message); + } + break; + case ComponentHandle::ResourceWasiPollableKind: + break; + case ComponentHandle::ResourceWasiTerminalKind: + break; + default: + return false; + } + return true; +} + +} // namespace Walrus + +#endif diff --git a/src/wasi/WASI02Impl.h b/src/wasi/WASI02Impl.h new file mode 100644 index 000000000..bbb02126a --- /dev/null +++ b/src/wasi/WASI02Impl.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2023-present Samsung Electronics Co., Ltd + * + * 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 __WalrusWASI02Impl__ +#define __WalrusWASI02Impl__ + +#ifdef ENABLE_WASI + +#include "Walrus.h" +#include "runtime/Component.h" +#include "runtime/ComponentInstance.h" + +namespace Walrus { + +class WasiStoreData { +public: + WasiStoreData(int argc, const char** argv, const char** envp); + + const std::vector& arguments() const + { + return m_arguments; + } + + const std::vector>& environment() const + { + return m_environment; + } + + std::map& wasiInstances() + { + return m_wasiInstances; + } + +private: + std::vector m_arguments; + std::vector> m_environment; + std::map m_wasiInstances; +}; + +class ComponentResourceWasiStream : public ComponentResource { + friend class ComponentResourceWasiPollable; + +public: + ComponentResourceWasiStream(ComponentTypeResource* type, FILE* file) + : ComponentResource(ResourceWasiStreamKind, type) + , m_file(file) + , m_pollableCount(0) + { + } + + FILE* file() const + { + return m_file; + } + + size_t pollableCount() const + { + return m_pollableCount; + } + +private: + FILE* m_file; + size_t m_pollableCount; +}; + +class ComponentResourceWasiPollable : public ComponentResource { +public: + ComponentResourceWasiPollable(ComponentTypeResource* type, ComponentResourceWasiStream* stream) + : ComponentResource(ResourceWasiPollableKind, type) + , m_stream(stream) + { + stream->m_pollableCount++; + } + + ~ComponentResourceWasiPollable() + { + m_stream->m_pollableCount--; + } + + ComponentResourceWasiStream* stream() const + { + return m_stream; + } + +private: + ComponentResourceWasiStream* m_stream; +}; + +class ComponentResourceWasiTerminal : public ComponentResource { +public: + ComponentResourceWasiTerminal(ComponentTypeResource* type, int fileNo) + : ComponentResource(ResourceWasiTerminalKind, type) + , m_fileNo(fileNo) + { + } + + int fileNo() const + { + return m_fileNo; + } + +private: + int m_fileNo; +}; + +class LiftedWasiFunction : public LiftedFunction { +public: + enum Type { + cliExit02, + ioPollableBlock02, + ioPoll02, + ioInputStreamRead02, + ioInputStreamSubscribe02, + ioOutputStreamCheckWrite02, + ioOutputStreamWrite02, + ioOutputStreamBlockingWriteAndFlush02, + ioOutputStreamBlockingFlush02, + ioOutputStreamSubscribe02, + cliGetEnvironment02, + cliGetArguments02, + cliGetStdin02, + cliGetStdout02, + cliGetStderr02, + cliGetTerminalStdin02, + cliGetTerminalStdout02, + cliGetTerminalStderr02, + clockMonotonicNow02, + clockSubscribeDuration02, + clockWallNow02, + fileSystemDescriptorReadViaStream02, + fileSystemDescriptorWriteViaStream02, + fileSystemDescriptorAppendViaStream02, + fileSystemDescriptorGetFlags02, + fileSystemDescriptorStat02, + fileSystemDescriptorOpenAt02, + fileSystemDescriptorMetadataHash02, + fileSystemGetDirectories02, + }; + + LiftedWasiFunction(Type type, ComponentInstance* instance, FunctionType* functionType) + : LiftedFunction() + , m_type(type) + , m_instance(instance) + , m_functionType(functionType) + { + } + + virtual Kind kind() const override + { + return WasiFunctionKind; + } + + Type type() const + { + return m_type; + } + + ComponentInstance* instance() const + { + return m_instance; + } + + FunctionType* functionType() const + { + return m_functionType; + } + +private: + Type m_type; + ComponentInstance* m_instance; + FunctionType* m_functionType; +}; + +} // namespace Walrus + +#endif + +#endif // __WalrusWASI02Impl__