From eb7706d08b143d0b4f2754ce2234493cdab5d6c5 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Fri, 15 May 2026 07:05:16 +0000 Subject: [PATCH] Implement resource support Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- src/parser/WASMComponentParser.cpp | 10 +- src/runtime/Component.cpp | 2 +- src/runtime/Component.h | 81 +++++- src/runtime/ComponentInstance.cpp | 160 +++++++++--- src/runtime/ComponentInstance.h | 177 +++++++++---- src/runtime/Function.h | 2 + src/runtime/Store.cpp | 15 ++ src/runtime/Store.h | 44 ++++ src/shell/Shell.cpp | 19 +- src/wasi/WASI02.cpp | 388 ++++++++++++++++++++++++----- src/wasi/WASI02.h | 10 +- 11 files changed, 744 insertions(+), 164 deletions(-) diff --git a/src/parser/WASMComponentParser.cpp b/src/parser/WASMComponentParser.cpp index 4aaf86e5b..39dc73066 100644 --- a/src/parser/WASMComponentParser.cpp +++ b/src/parser/WASMComponentParser.cpp @@ -262,7 +262,7 @@ class WASMComponentBinaryReader : public wabt::WASMComponentBinaryReaderDelegate break; case ComponentSort::Type: if (externalInfo.external == ComponentExternalDesc::TypeSubRes) { - type = new Walrus::ComponentTypeResource(Walrus::ComponentRefCounted::ResourceKind, false); + type = new Walrus::ComponentTypeSubResource(); } else { type = m_current->getType(externalInfo.index.index); type->addRef(); @@ -672,14 +672,14 @@ class WASMComponentBinaryReader : public wabt::WASMComponentBinaryReaderDelegate void OnOwnType(Index index) { - Walrus::ComponentTypeResource* ref = m_current->getType(index)->asTypeResource(); + Walrus::ComponentTypeSubResource* ref = m_current->getType(index)->asTypeSubResource(); ref->addRef(); m_current->pushType(new Walrus::ComponentTypeResourceRef(Walrus::ComponentRefCounted::OwnKind, ref)); } void OnBorrowType(Index index) { - Walrus::ComponentTypeResource* ref = m_current->getType(index)->asTypeResource(); + Walrus::ComponentTypeSubResource* ref = m_current->getType(index)->asTypeSubResource(); ref->addRef(); m_current->pushType(new Walrus::ComponentTypeResourceRef(Walrus::ComponentRefCounted::BorrowKind, ref)); } @@ -723,14 +723,14 @@ class WASMComponentBinaryReader : public wabt::WASMComponentBinaryReaderDelegate void OnResourceType(ComponentResourceRep rep, Index dtor) { - m_current->pushType(new Walrus::ComponentTypeResource(Walrus::ComponentRefCounted::ResourceKind, rep == ComponentResourceRep::I64)); + m_current->pushType(new Walrus::ComponentTypeResource(rep == ComponentResourceRep::I64, dtor)); } void OnResourceAsyncType(ComponentResourceRep rep, Index dtor, Index callback) { - m_current->pushType(new Walrus::ComponentTypeResource(Walrus::ComponentRefCounted::ResourceAsyncKind, rep == ComponentResourceRep::I64)); + m_current->pushType(new Walrus::ComponentTypeResourceAsync(rep == ComponentResourceRep::I64, dtor, callback)); } void BeginInstanceType(uint32_t count) diff --git a/src/runtime/Component.cpp b/src/runtime/Component.cpp index 857493b60..a6ab348b8 100644 --- a/src/runtime/Component.cpp +++ b/src/runtime/Component.cpp @@ -74,7 +74,7 @@ void ComponentRefCounted::releaseAllRefs(ComponentRefCounted* ref) break; } default: - ASSERT(ref->isValueType() || ref->isTypeLabels() || ref->isTypeResource()); + ASSERT(ref->isValueType() || ref->isTypeLabels() || ref->isTypeSubResource()); break; } diff --git a/src/runtime/Component.h b/src/runtime/Component.h index da24e1c0d..94271f6fb 100644 --- a/src/runtime/Component.h +++ b/src/runtime/Component.h @@ -50,7 +50,9 @@ class ComponentTypeTuple; class ComponentTypeLabels; class ComponentTypeResult; class ComponentTypeFunc; +class ComponentTypeSubResource; class ComponentTypeResource; +class ComponentTypeResourceAsync; class ComponentTypeResourceRef; class ComponentType; @@ -76,6 +78,7 @@ class ComponentRefCounted { InstanceTypeKind, ComponentTypeKind, InstanceKind, + SubResourceKind, ResourceKind, ResourceAsyncKind, }; @@ -193,6 +196,17 @@ class ComponentRefCounted { return reinterpret_cast(this); } + bool isTypeSubResource() const + { + return kind() == SubResourceKind || kind() == ResourceKind || kind() == ResourceAsyncKind; + } + + ComponentTypeSubResource* asTypeSubResource() + { + ASSERT(isTypeSubResource()); + return reinterpret_cast(this); + } + bool isTypeResource() const { return kind() == ResourceKind || kind() == ResourceAsyncKind; @@ -204,6 +218,12 @@ class ComponentRefCounted { return reinterpret_cast(this); } + ComponentTypeResourceAsync* asTypeResourceAsync() + { + ASSERT(kind() == ResourceAsyncKind); + return reinterpret_cast(this); + } + bool isComponentType() const { return kind() == InstanceTypeKind || kind() == ComponentTypeKind; @@ -453,13 +473,30 @@ class ComponentTypeFunc : public ComponentRefCounted { ComponentTypeRef m_result; }; -class ComponentTypeResource : public ComponentRefCounted { +class ComponentTypeSubResource : public ComponentRefCounted { public: - ComponentTypeResource(Kind kind, bool i64) + ComponentTypeSubResource() + : ComponentRefCounted(SubResourceKind) + { + } + +protected: + ComponentTypeSubResource(Kind kind) : ComponentRefCounted(kind) + { + ASSERT(kind == ResourceKind || kind == ResourceAsyncKind); + } +}; + +class ComponentTypeResource : public ComponentTypeSubResource { +public: + static constexpr uint32_t NotDefined = ~static_cast(0); + + ComponentTypeResource(bool i64, uint32_t dtorIndex) + : ComponentTypeSubResource(ResourceKind) , m_i64(i64) + , m_dtorIndex(dtorIndex) { - ASSERT(isTypeResource()); } bool i64() const @@ -467,26 +504,58 @@ class ComponentTypeResource : public ComponentRefCounted { return m_i64; } + uint32_t dtorIndex() const + { + return m_dtorIndex; + } + +protected: + ComponentTypeResource(Kind kind, bool i64, uint32_t dtorIndex) + : ComponentTypeSubResource(kind) + , m_i64(i64) + , m_dtorIndex(dtorIndex) + { + ASSERT(kind == ResourceAsyncKind); + } + private: bool m_i64; + uint32_t m_dtorIndex; +}; + +class ComponentTypeResourceAsync : public ComponentTypeResource { +public: + ComponentTypeResourceAsync(bool i64, uint32_t dtorIndex, uint32_t callbackIndex) + : ComponentTypeResource(ResourceAsyncKind, i64, dtorIndex) + , m_callbackIndex(callbackIndex) + { + } + + uint32_t callbackIndex() const + { + return m_callbackIndex; + } + +private: + uint32_t m_callbackIndex; }; class ComponentTypeResourceRef : public ComponentRefCounted { public: - ComponentTypeResourceRef(Kind kind, ComponentTypeResource* ref) + ComponentTypeResourceRef(Kind kind, ComponentTypeSubResource* ref) : ComponentRefCounted(kind) , m_ref(ref) { ASSERT(isTypeResourceRef()); } - ComponentTypeResource* ref() const + ComponentTypeSubResource* ref() const { return m_ref; } private: - ComponentTypeResource* m_ref; + ComponentTypeSubResource* m_ref; }; // Immutable type declarations for components, instances diff --git a/src/runtime/ComponentInstance.cpp b/src/runtime/ComponentInstance.cpp index e09bc8d6f..d1494a2e0 100644 --- a/src/runtime/ComponentInstance.cpp +++ b/src/runtime/ComponentInstance.cpp @@ -38,19 +38,139 @@ LoweredFunction* LoweredFunction::createLoweredFunction(Store* store, const Func void LoweredFunction::call(ExecutionState& state, Value* argv, Value* result) { +#ifdef ENABLE_WASI + if (m_liftedFunction->kind() == LiftedFunction::WasiFunctionKind) { + callWasiFunction(state, argv, result, m_liftedFunction->asLiftedWasiFunction(), m_options); + return; + } +#endif + ASSERT(0); } CanonFunction* CanonFunction::createCanonFunction(Store* store, const FunctionType* functionType, Type type) { - CanonFunction* func = new CanonFunction(functionType, type); + CanonFunction* func = new CanonFunction(functionType, type, store); store->appendExtern(func); return func; } void CanonFunction::call(ExecutionState& state, Value* argv, Value* result) { - ASSERT(0); + ComponentInstance* instance = store()->context()->instance(); + + switch (type()) { + case ResourceDrop: { + uint32_t index = argv[0].asI32(); + ComponentHandle* handle = instance->getHandle(state, index); +#ifdef ENABLE_WASI + if (dropWasiResource(state, handle)) { + delete handle; + instance->removeHandle(index); + break; + } +#endif /* ENABLE_WASI */ + + if (handle->kind() != ComponentHandle::ResourceRepKind) { + ComponentInstance::throwInvalidHandle(state, index); + } + + delete handle; + instance->removeHandle(index); + break; + } + default: + ASSERT(0); + break; + } +} + +ComponentInstance::ComponentInstance(ComponentType* type) + : Object(GET_GLOBAL_TYPE_INFO(componentInstanceTypeInfo)) + , m_type(type) + , m_freeResourceHandle(LastHandle) +{ + type->addRef(); +} + +ComponentInstance::~ComponentInstance() +{ + for (auto it : m_handles) { + if ((it & (UnusedSlotMask | BorrowedHandleMask)) == 0) { + delete reinterpret_cast(it); + } + } + for (auto it : m_funcs) { + it->releaseRef(); + } + for (auto it : m_canonOptions) { + delete it; + } + m_type->releaseRef(); +} + +uint32_t ComponentInstance::appendHandle(ExecutionState& state, ComponentHandle* handle) +{ + uintptr_t handleValue = reinterpret_cast(handle); + ASSERT((handleValue & (UnusedSlotMask | BorrowedHandleMask)) == 0); + + if (m_freeResourceHandle != LastHandle) { + uint32_t result = static_cast(m_freeResourceHandle); + ASSERT((reinterpret_cast(m_handles[result]) & UnusedSlotMask) != 0); + m_freeResourceHandle = m_handles[result]; + if (m_freeResourceHandle != LastHandle) { + m_freeResourceHandle >>= UnusedSlotShift; + } + m_handles[result] = handleValue; + return result + FirstHandleIndex; + } + + if (m_handles.size() >= ~static_cast(0) - FirstHandleIndex) { + std::string message = "too many handles allocated"; + Trap::throwException(state, message); + return 0; + } + + uint32_t result = static_cast(m_handles.size()); + m_handles.push_back(handleValue); + return result + FirstHandleIndex; +} + +ComponentHandle* ComponentInstance::getHandle(ExecutionState& state, uint32_t index) +{ + if (index >= FirstHandleIndex && (index - FirstHandleIndex) < m_handles.size()) { + uintptr_t handleValue = m_handles[index - FirstHandleIndex]; + if ((handleValue & UnusedSlotMask) == 0) { + return reinterpret_cast(handleValue & ~BorrowedHandleMask); + } + } + + throwInvalidHandle(state, index); + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; +} + +void ComponentInstance::removeHandle(uint32_t index) +{ + ASSERT(index >= FirstHandleIndex && (index - FirstHandleIndex) < m_handles.size() + && (m_handles[index - FirstHandleIndex] & UnusedSlotMask) == 0); + index -= FirstHandleIndex; + m_handles[index] = (m_freeResourceHandle << UnusedSlotShift) | UnusedSlotMask; + m_freeResourceHandle = index; +} + +void ComponentInstance::throwInvalidHandle(ExecutionState& state, uint32_t index) +{ + std::string message = "invalid resource handle: "; + message.append(std::to_string(index)); + Trap::throwException(state, message); +} + +ComponentInstance* ComponentInstance::createInstance(Store* store, ComponentType* type) +{ + ComponentInstance* instance = new ComponentInstance(type); + store->appendComponentInstance(instance); + return instance; } void ComponentInstance::coreInstantiate(ExecutionState& state, Store* store, Component* component, ComponentCoreInstantiate* instantiate) @@ -86,7 +206,7 @@ void ComponentInstance::coreInstantiate(ExecutionState& state, Store* store, Com } if (exportType == nullptr) { - std::string message = "Cannot import field \""; + std::string message = "cannot import field \""; message.append(import->fieldName()); message.append("\" from module: "); message.append(import->moduleName()); @@ -143,7 +263,7 @@ void ComponentInstance::coreInstantiate(ExecutionState& state, Store* store, Com } } else { if (inlineIndex == 0) { - std::string message = "Cannot import module: "; + std::string message = "cannot import module: "; message.append(import->moduleName()); Trap::throwException(state, message); } @@ -163,7 +283,7 @@ void ComponentInstance::coreInstantiate(ExecutionState& state, Store* store, Com } if (sort == ComponentSort::Invalid) { - std::string message = "Cannot import field \""; + std::string message = "cannot import field \""; message.append(import->fieldName()); message.append("\" from module: "); message.append(import->moduleName()); @@ -221,7 +341,7 @@ void ComponentInstance::coreInstantiate(ExecutionState& state, Store* store, Com } if (value == nullptr) { - std::string message = "Type mismatch for field \""; + std::string message = "type mismatch for field \""; message.append(import->fieldName()); message.append("\" form module: "); message.append(import->moduleName()); @@ -335,7 +455,7 @@ void ComponentInstance::lowerFunction(Store* store, std::vector& #ifdef ENABLE_WASI if (func->kind() == LiftedFunction::WasiFunctionKind) { - m_coreFuncs.push_back(LoweredFunction::createLoweredFunction(store, func->asLiftedWasiFunction()->functionType(), func, options)); + m_coreFuncs.push_back(LoweredFunction::createLoweredFunction(store, getWasiFunctionType(func->asLiftedWasiFunction()), func, options)); return; } #endif /* ENABLE_WASI */ @@ -344,8 +464,7 @@ void ComponentInstance::lowerFunction(Store* store, std::vector& ComponentInstance* ComponentInstance::InstantiateContext::instantiate(Component* component, ComponentInstance* parent, ComponentInstantiate* arg) { - ComponentInstance* instance = new ComponentInstance(component->type()); - m_store->appendComponentInstance(instance); + ComponentInstance* instance = createInstance(m_store, component->type()); for (auto it : component->declarations()) { switch (it->kind()) { @@ -421,9 +540,8 @@ ComponentInstance* ComponentInstance::InstantiateContext::instantiate(Component* #ifdef ENABLE_WASI if (!success && external.sort == ComponentSort::Instance) { - ComponentInstance* importedInstance = wasi02LoadInstance(external, m_functionTypes); + ComponentInstance* importedInstance = wasi02LoadInstance(m_store, m_functionTypes, external.name); if (importedInstance != nullptr) { - m_store->appendComponentInstance(importedInstance); instance->m_instances.push_back(importedInstance); success = true; break; @@ -432,7 +550,7 @@ ComponentInstance* ComponentInstance::InstantiateContext::instantiate(Component* #endif /* ENABLE_WASI */ if (!success) { - std::string message = "Cannot import: "; + std::string message = "cannot import: "; message.append(external.name); Trap::throwException(m_state, message); } @@ -453,22 +571,4 @@ ComponentInstance* ComponentInstance::instantiate(ExecutionState& state, Store* return context.instantiate(component, nullptr, nullptr); } -ComponentInstance::ComponentInstance(ComponentType* type) - : Object(GET_GLOBAL_TYPE_INFO(componentInstanceTypeInfo)) - , m_type(type) -{ - type->addRef(); -} - -ComponentInstance::~ComponentInstance() -{ - m_type->releaseRef(); - for (auto it : m_funcs) { - it->releaseRef(); - } - for (auto it : m_canonOptions) { - delete it; - } -} - } // namespace Walrus diff --git a/src/runtime/ComponentInstance.h b/src/runtime/ComponentInstance.h index 6129d9582..867851a1a 100644 --- a/src/runtime/ComponentInstance.h +++ b/src/runtime/ComponentInstance.h @@ -167,50 +167,6 @@ class LiftedCoreFunction : public LiftedFunction { CanonOptions* m_options; }; -#ifdef ENABLE_WASI - -class LiftedWasiFunction : public LiftedFunction { -public: - enum Type { - cliExit026, - ioPollableBlock026, - ioOutputStreamCheckWrite026, - ioOutputStreamWrite026, - ioOutputStreamBlockingWriteAndFlush026, - ioOutputStreamBlockingFlush026, - ioOutputStreamSubscribe026, - cliGetStdin026, - cliGetStdout026, - cliGetStderr026, - cliGetTerminalStdin026, - cliGetTerminalStdout026, - cliGetTerminalStderr026, - }; - - LiftedWasiFunction(Type type, FunctionType* functionType) - : LiftedFunction() - , m_type(type) - , m_functionType(functionType) - { - } - - virtual Kind kind() const override - { - return WasiFunctionKind; - } - - FunctionType* functionType() const - { - return m_functionType; - } - -private: - Type m_type; - FunctionType* m_functionType; -}; - -#endif /* ENABLE_WASI */ - class LoweredFunction : public NativeFunction { public: static LoweredFunction* createLoweredFunction(Store* store, const FunctionType* functionType, LiftedFunction* liftedFunction, CanonOptions* options); @@ -249,26 +205,130 @@ class CanonFunction : public NativeFunction { static CanonFunction* createCanonFunction(Store* store, const FunctionType* functionType, Type type); + virtual Kind kind() const override + { + return CanonFunctionKind; + } + Type type() const { return m_type; } - virtual Kind kind() const override + Store* store() const { - return CanonFunctionKind; + return m_store; } virtual void call(ExecutionState& state, Value* argv, Value* result) override; private: - CanonFunction(const FunctionType* functionType, Type type) + CanonFunction(const FunctionType* functionType, Type type, Store* store) : NativeFunction(functionType) , m_type(type) + , m_store(store) { } Type m_type; + Store* m_store; +}; + +class ComponentResourceRep; + +class ComponentHandle { +public: + enum Kind { + ResourceRepKind, +#ifdef ENABLE_WASI + ResourceWasiStreamKind, + ResourceWasiPollableKind, + ResourceWasiTerminalKind, +#endif /* ENABLE_WASI */ + }; + + ~ComponentHandle() {} + + Kind kind() + { + return m_kind; + } + + ComponentResourceRep* asResourceRep() + { + ASSERT(kind() == ResourceRepKind); + return reinterpret_cast(this); + } + +protected: + ComponentHandle(Kind kind) + : m_kind(kind) + { + } + +private: + Kind m_kind; +}; + +class ComponentResource : public ComponentHandle { +public: + ComponentTypeResource* type() const + { + return m_type; + } + +protected: + ComponentResource(Kind kind, ComponentTypeResource* type) + : ComponentHandle(kind) + , m_type(type) + { + } + +private: + ComponentTypeResource* m_type; +}; + +class ComponentResourceRep : public ComponentResource { +public: + ComponentResourceRep(ComponentTypeResource* type, ComponentInstance* instance, uint32_t rep) + : ComponentResource(ResourceRepKind, type) + , m_instance(instance) + , m_rep32(rep) + { + ASSERT(!type->i64()); + } + + ComponentResourceRep(ComponentTypeResource* type, ComponentInstance* instance, uint64_t rep) + : ComponentResource(ResourceRepKind, type) + , m_instance(instance) + , m_rep64(rep) + { + ASSERT(type->i64()); + } + + ComponentInstance* instance() const + { + return m_instance; + } + + uint32_t rep32() const + { + ASSERT(!type()->i64()); + return m_rep32; + } + + uint64_t rep64() const + { + ASSERT(type()->i64()); + return m_rep64; + } + +private: + ComponentInstance* m_instance; + union { + uint32_t m_rep32; + uint64_t m_rep64; + }; }; class DefinedFunctionTypes; @@ -298,6 +358,19 @@ class ComponentInstance : public Object { return m_instances[index]; } + // Handle 0 cannot be used. + uint32_t appendHandle(ExecutionState& state, ComponentHandle* handle); + ComponentHandle* getHandle(ExecutionState& state, uint32_t index); + void removeHandle(uint32_t index); + static void throwInvalidHandle(ExecutionState& state, uint32_t index); + + bool isBorrowedHandle(uint32_t index) + { + ASSERT(index >= FirstHandleIndex && (index - FirstHandleIndex) < m_handles.size() + && (m_handles[index - FirstHandleIndex] & UnusedSlotMask) == 0); + return (m_handles[index - FirstHandleIndex] & BorrowedHandleMask) != 0; + } + private: ComponentInstance(ComponentType* type); @@ -318,6 +391,14 @@ class ComponentInstance : public Object { DefinedFunctionTypes& m_functionTypes; }; + static constexpr uint32_t FirstHandleIndex = 1; + static constexpr uintptr_t LastHandle = ~static_cast(0); + static constexpr uintptr_t UnusedSlotMask = 0x1; + static constexpr uintptr_t BorrowedHandleMask = 0x2; + static constexpr int UnusedSlotShift = 1; + + static ComponentInstance* createInstance(Store* store, ComponentType* type); + void coreInstantiate(ExecutionState& state, Store* store, Component* component, ComponentCoreInstantiate* instantiate); void aliasExport(ComponentAliasExport* alias); void aliasCoreExport(ComponentAliasExport* alias); @@ -326,6 +407,7 @@ class ComponentInstance : public Object { void lowerFunction(Store* store, std::vector& canonOptions, ComponentCanonLower* lower); ComponentType* m_type; + uintptr_t m_freeResourceHandle; std::vector m_coreFuncs; std::vector m_coreTables; std::vector m_coreMemories; @@ -335,6 +417,7 @@ class ComponentInstance : public Object { std::vector m_funcs; std::vector m_instances; std::vector m_canonOptions; + std::vector m_handles; }; } // namespace Walrus diff --git a/src/runtime/Function.h b/src/runtime/Function.h index ac34fb491..a06afb9bd 100644 --- a/src/runtime/Function.h +++ b/src/runtime/Function.h @@ -52,8 +52,10 @@ class Function : public Extern { DefinedFunctionKind, ImportedFunctionKind, WasiFunctionKind, + // Function types used by component support. LoweredFunctionKind, CanonFunctionKind, + WasiResourceReleaseKind, }; const FunctionType* functionType() const { return m_functionType; } diff --git a/src/runtime/Store.cpp b/src/runtime/Store.cpp index bdd9750f2..55a91123c 100644 --- a/src/runtime/Store.cpp +++ b/src/runtime/Store.cpp @@ -108,4 +108,19 @@ Waiter* Store::getWaiter(void* address) m_waiterList.push_back(waiter); return waiter; } + +void Store::registerComponentInstance(std::string& name, ComponentInstance* instance) +{ + m_namedComponentInstances[name] = instance; +} + +ComponentInstance* Store::findComponentInstance(std::string& name) +{ + auto it = m_namedComponentInstances.find(name); + if (it == m_namedComponentInstances.end()) { + return nullptr; + } + return it->second; +} + } // namespace Walrus diff --git a/src/runtime/Store.h b/src/runtime/Store.h index f8ccef400..ceb56831e 100644 --- a/src/runtime/Store.h +++ b/src/runtime/Store.h @@ -56,6 +56,39 @@ struct Waiter { class Store { public: + class ComponentContext { + MAKE_STACK_ALLOCATED() + + public: + ComponentContext(Store* store, ComponentInstance* instance) + : m_store(store) + , m_prevContext(store->m_context) + , m_instance(instance) + { + m_store->m_context = this; + } + + ~ComponentContext() + { + m_store->m_context = m_prevContext; + } + + ComponentContext* prevContext() const + { + return m_prevContext; + } + + ComponentInstance* instance() const + { + return m_instance; + } + + private: + Store* m_store; + ComponentContext* m_prevContext; + ComponentInstance* m_instance; + }; + Store(Engine* engine); ~Store(); @@ -101,6 +134,14 @@ class Store { Waiter* getWaiter(void* address); + ComponentContext* context() const + { + return m_context; + } + + void registerComponentInstance(std::string& name, ComponentInstance* instance); + ComponentInstance* findComponentInstance(std::string& name); + private: Engine* m_engine; TypeStore m_typeStore; @@ -113,6 +154,9 @@ class Store { std::mutex m_waiterListLock; std::vector m_waiterList; + + ComponentContext* m_context; + std::map m_namedComponentInstances; }; } // namespace Walrus diff --git a/src/shell/Shell.cpp b/src/shell/Shell.cpp index ff6b31faa..fa181f81c 100644 --- a/src/shell/Shell.cpp +++ b/src/shell/Shell.cpp @@ -396,13 +396,13 @@ static Trap::TrapResult executeWASMComponent(Store* store, DefinedFunctionTypes& } } - Function* run = nullptr; + LiftedCoreFunction* run = nullptr; if (instance != nullptr) { for (auto& it : instance->type()->exports()) { if (it.sort == ComponentSort::Func && it.name == "run") { LiftedFunction* func = instance->getFunction(it.exportIndex); if (func->kind() == LiftedFunction::CoreFunctionKind) { - run = func->asLiftedCoreFunction()->function(); + run = func->asLiftedCoreFunction(); } break; } @@ -411,7 +411,8 @@ static Trap::TrapResult executeWASMComponent(Store* store, DefinedFunctionTypes& if (run != nullptr) { Value resultValue[1]; - run->call(state, nullptr, resultValue); + Store::ComponentContext context(data->store, run->options()->instance()); + run->function()->call(state, nullptr, resultValue); } else { printf("Note: Cannot execute component, missing run()\n"); } @@ -1348,6 +1349,7 @@ int main(int argc, const char* argv[]) WASI::initialize(&uvwasi); #endif + int result = 0; for (const auto& filePath : options.fileNames) { FILE* fp = fopen(filePath.data(), "r"); if (fp) { @@ -1372,13 +1374,15 @@ int main(int argc, const char* argv[]) auto trapResult = executeWASMComponent(store, functionTypes, filePath, buf); if (trapResult.exception) { fprintf(stderr, "Uncaught Exception: %s\n", trapResult.exception->message().data()); - return -1; + result = -1; + break; } } else { auto trapResult = executeWASM(store, filePath, buf, functionTypes); if (trapResult.exception) { fprintf(stderr, "Uncaught Exception: %s\n", trapResult.exception->message().data()); - return -1; + result = -1; + break; } } } else if (endsWith(filePath, "wat") || endsWith(filePath, "wast")) { @@ -1386,7 +1390,8 @@ int main(int argc, const char* argv[]) } } else { printf("Cannot open file %s\n", filePath.data()); - return -1; + result = -1; + break; } } @@ -1404,5 +1409,5 @@ int main(int argc, const char* argv[]) ProfilerStop(); #endif - return 0; + return result; } diff --git a/src/wasi/WASI02.cpp b/src/wasi/WASI02.cpp index 2e6106f9b..c92b4ef91 100644 --- a/src/wasi/WASI02.cpp +++ b/src/wasi/WASI02.cpp @@ -19,114 +19,370 @@ #include "wasi/WASI02.h" #include "runtime/ComponentInstance.h" #include "runtime/DefinedFunctionTypes.h" +#include "runtime/Memory.h" +#include "runtime/Store.h" namespace Walrus { +class ComponentResourceWasiStream : public ComponentResource { + friend class ComponentResourceWasiPollable; + +public: + ComponentResourceWasiStream(ComponentTypeResource* type, int fileNo) + : ComponentResource(ResourceWasiStreamKind, type) + , m_fileNo(fileNo) + , m_pollableCount(0) + { + } + + int fileNo() const + { + return m_fileNo; + } + + size_t pollableCount() const + { + return m_pollableCount; + } + +private: + int m_fileNo; + 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 { + cliExit026, + ioPollableBlock026, + ioOutputStreamCheckWrite026, + ioOutputStreamWrite026, + ioOutputStreamBlockingWriteAndFlush026, + ioOutputStreamBlockingFlush026, + ioOutputStreamSubscribe026, + cliGetStdin026, + cliGetStdout026, + cliGetStderr026, + cliGetTerminalStdin026, + cliGetTerminalStdout026, + cliGetTerminalStderr026, + }; + + 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, DefinedFunctionTypes& functionTypes) + : m_store(store) + , m_functionTypes(functionTypes) + , m_type(nullptr) + { + } + + ~ComponentInstanceWasi02() + { + if (m_type != nullptr) { + m_type->releaseRef(); + } + } + + ComponentInstance* loadInstance(std::string& name); + +private: + static void aliasExport(ComponentInstance* instance, const char* name, ComponentRefCounted* type); static void addExport(ComponentInstance* instance, const char* name, LiftedWasiFunction::Type type, FunctionType* functionType); - static ComponentInstance* loadInstance(ComponentType::External& external, DefinedFunctionTypes& functionTypes); + ComponentInstance* getInstance(const char* name); + + Store* m_store; + DefinedFunctionTypes& m_functionTypes; + ComponentType* m_type; }; +void ComponentInstanceWasi02::aliasExport(ComponentInstance* instance, const char* name, ComponentRefCounted* type) +{ + ComponentType* componentType = instance->type(); + type->addRef(); + componentType->pushType(type); + type->addRef(); + instance->m_type->exports().push_back(ComponentType::External{ name, type, ComponentSort::Type, static_cast(componentType->types().size()) }); +} + void ComponentInstanceWasi02::addExport(ComponentInstance* instance, const char* name, LiftedWasiFunction::Type type, FunctionType* functionType) { instance->m_type->exports().push_back(ComponentType::External{ name, nullptr, ComponentSort::Func, static_cast(instance->m_funcs.size()) }); - instance->m_funcs.push_back(new LiftedWasiFunction(type, functionType)); + instance->m_funcs.push_back(new LiftedWasiFunction(type, instance, functionType)); } -ComponentInstance* ComponentInstanceWasi02::loadInstance(ComponentType::External& external, DefinedFunctionTypes& functionTypes) +ComponentInstance* ComponentInstanceWasi02::getInstance(const char* name) { - if (external.name == "wasi:cli/exit@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "exit", LiftedWasiFunction::cliExit026, functionTypes[DefinedFunctionTypes::I32R]); - type->releaseRef(); + std::string str(name); + return wasi02LoadInstance(m_store, m_functionTypes, str); +} + +ComponentInstance* ComponentInstanceWasi02::loadInstance(std::string& name) +{ + if (name == "wasi:cli/exit@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + addExport(instance, "exit", LiftedWasiFunction::cliExit026, m_functionTypes[DefinedFunctionTypes::I32R]); return instance; } - if (external.name == "wasi:io/error@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - type->releaseRef(); + if (name == "wasi:io/error@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + m_type->pushType(new ComponentTypeResource(false, ComponentTypeResource::NotDefined)); /* 0 */ return instance; } - if (external.name == "wasi:io/poll@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "[method]pollable.block", LiftedWasiFunction::ioPollableBlock026, functionTypes[DefinedFunctionTypes::I32R]); - type->releaseRef(); + if (name == "wasi:io/poll@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + m_type->pushType(new ComponentTypeResource(false, ComponentTypeResource::NotDefined)); /* 0 */ + addExport(instance, "[method]pollable.block", LiftedWasiFunction::ioPollableBlock026, m_functionTypes[DefinedFunctionTypes::I32R]); return instance; } - if (external.name == "wasi:io/streams@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "[method]output-stream.check-write", LiftedWasiFunction::ioOutputStreamCheckWrite026, functionTypes[DefinedFunctionTypes::I32I32R]); - addExport(instance, "[method]output-stream.write", LiftedWasiFunction::ioOutputStreamWrite026, functionTypes[DefinedFunctionTypes::I32I32I32I32R]); - addExport(instance, "[method]output-stream.blocking-write-and-flush", LiftedWasiFunction::ioOutputStreamBlockingWriteAndFlush026, functionTypes[DefinedFunctionTypes::I32I32I32I32R]); - addExport(instance, "[method]output-stream.blocking-flush", LiftedWasiFunction::ioOutputStreamBlockingFlush026, functionTypes[DefinedFunctionTypes::I32I32R]); - addExport(instance, "[method]output-stream.subscribe", LiftedWasiFunction::ioOutputStreamSubscribe026, functionTypes[DefinedFunctionTypes::I32_RI32]); - type->releaseRef(); + if (name == "wasi:io/streams@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + m_type->pushType(new ComponentTypeResource(false, ComponentTypeResource::NotDefined)); /* 0 */ + m_type->pushType(new ComponentTypeResource(false, ComponentTypeResource::NotDefined)); /* 1 */ + + ComponentInstance* errorIntance = getInstance("wasi:io/error@0.2.6"); + instance->m_instances.push_back(errorIntance); + aliasExport(instance, "error", errorIntance->type()->getType(0)); /* 2 */ + ComponentInstance* pollIntance = getInstance("wasi:io/poll@0.2.6"); + instance->m_instances.push_back(pollIntance); + aliasExport(instance, "pollable", pollIntance->type()->getType(0)); /* 3 */ + addExport(instance, "[method]output-stream.check-write", LiftedWasiFunction::ioOutputStreamCheckWrite026, m_functionTypes[DefinedFunctionTypes::I32I32R]); + addExport(instance, "[method]output-stream.write", LiftedWasiFunction::ioOutputStreamWrite026, m_functionTypes[DefinedFunctionTypes::I32I32I32I32R]); + addExport(instance, "[method]output-stream.blocking-write-and-flush", LiftedWasiFunction::ioOutputStreamBlockingWriteAndFlush026, m_functionTypes[DefinedFunctionTypes::I32I32I32I32R]); + addExport(instance, "[method]output-stream.blocking-flush", LiftedWasiFunction::ioOutputStreamBlockingFlush026, m_functionTypes[DefinedFunctionTypes::I32I32R]); + addExport(instance, "[method]output-stream.subscribe", LiftedWasiFunction::ioOutputStreamSubscribe026, m_functionTypes[DefinedFunctionTypes::I32_RI32]); return instance; } - if (external.name == "wasi:cli/stdin@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "get-stdin", LiftedWasiFunction::cliGetStdin026, functionTypes[DefinedFunctionTypes::RI32]); - type->releaseRef(); + if (name == "wasi:cli/stdin@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + + ComponentInstance* streamsIntance = getInstance("wasi:io/streams@0.2.6"); + instance->m_instances.push_back(streamsIntance); + aliasExport(instance, "input-stream", streamsIntance->type()->getType(0)); /* 0 */ + addExport(instance, "get-stdin", LiftedWasiFunction::cliGetStdin026, m_functionTypes[DefinedFunctionTypes::RI32]); return instance; } - if (external.name == "wasi:cli/stdout@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "get-stdout", LiftedWasiFunction::cliGetStdout026, functionTypes[DefinedFunctionTypes::RI32]); - type->releaseRef(); + if (name == "wasi:cli/stdout@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + + ComponentInstance* streamsIntance = getInstance("wasi:io/streams@0.2.6"); + instance->m_instances.push_back(streamsIntance); + aliasExport(instance, "output-stream", streamsIntance->type()->getType(1)); /* 0 */ + addExport(instance, "get-stdout", LiftedWasiFunction::cliGetStdout026, m_functionTypes[DefinedFunctionTypes::RI32]); return instance; } - if (external.name == "wasi:cli/stderr@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "get-stderr", LiftedWasiFunction::cliGetStderr026, functionTypes[DefinedFunctionTypes::RI32]); - type->releaseRef(); + if (name == "wasi:cli/stderr@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + + ComponentInstance* streamsIntance = getInstance("wasi:io/streams@0.2.6"); + instance->m_instances.push_back(streamsIntance); + aliasExport(instance, "output-stream", streamsIntance->type()->getType(1)); /* 0 */ + addExport(instance, "get-stderr", LiftedWasiFunction::cliGetStderr026, m_functionTypes[DefinedFunctionTypes::RI32]); return instance; } - if (external.name == "wasi:cli/terminal-input@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - type->releaseRef(); + if (name == "wasi:cli/terminal-input@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + m_type->pushType(new ComponentTypeResource(false, ComponentTypeResource::NotDefined)); /* 0 */ return instance; } - if (external.name == "wasi:cli/terminal-output@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - type->releaseRef(); + if (name == "wasi:cli/terminal-output@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + m_type->pushType(new ComponentTypeResource(false, ComponentTypeResource::NotDefined)); /* 0 */ return instance; } - if (external.name == "wasi:cli/terminal-stdin@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "get-terminal-stdin", LiftedWasiFunction::cliGetTerminalStdin026, functionTypes[DefinedFunctionTypes::I32R]); - type->releaseRef(); + if (name == "wasi:cli/terminal-stdin@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + + ComponentInstance* inputIntance = getInstance("wasi:cli/terminal-input@0.2.6"); + instance->m_instances.push_back(inputIntance); + aliasExport(instance, "terminal-input", inputIntance->type()->getType(0)); /* 0 */ + addExport(instance, "get-terminal-stdin", LiftedWasiFunction::cliGetTerminalStdin026, m_functionTypes[DefinedFunctionTypes::I32R]); return instance; } - if (external.name == "wasi:cli/terminal-stdout@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "get-terminal-stdout", LiftedWasiFunction::cliGetTerminalStdout026, functionTypes[DefinedFunctionTypes::I32R]); - type->releaseRef(); + if (name == "wasi:cli/terminal-stdout@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + + ComponentInstance* outputIntance = getInstance("wasi:cli/terminal-output@0.2.6"); + instance->m_instances.push_back(outputIntance); + aliasExport(instance, "terminal-output", outputIntance->type()->getType(0)); /* 0 */ + addExport(instance, "get-terminal-stdout", LiftedWasiFunction::cliGetTerminalStdout026, m_functionTypes[DefinedFunctionTypes::I32R]); return instance; } - if (external.name == "wasi:cli/terminal-stderr@0.2.6") { - ComponentType* type = new ComponentType(ComponentType::ComponentTypeKind); - ComponentInstance* instance = new ComponentInstance(type); - addExport(instance, "get-terminal-stderr", LiftedWasiFunction::cliGetTerminalStderr026, functionTypes[DefinedFunctionTypes::I32R]); - type->releaseRef(); + if (name == "wasi:cli/terminal-stderr@0.2.6") { + m_type = new ComponentType(ComponentType::ComponentTypeKind); + ComponentInstance* instance = ComponentInstance::createInstance(m_store, m_type); + + ComponentInstance* outputIntance = getInstance("wasi:cli/terminal-output@0.2.6"); + instance->m_instances.push_back(outputIntance); + aliasExport(instance, "terminal-output", outputIntance->type()->getType(0)); /* 0 */ + addExport(instance, "get-terminal-stderr", LiftedWasiFunction::cliGetTerminalStderr026, m_functionTypes[DefinedFunctionTypes::I32R]); return instance; } return nullptr; } -ComponentInstance* wasi02LoadInstance(ComponentType::External& external, DefinedFunctionTypes& functionTypes) +ComponentInstance* wasi02LoadInstance(Store* store, DefinedFunctionTypes& functionTypes, std::string& name) +{ + ComponentInstance* instance = store->findComponentInstance(name); + if (instance != nullptr) { + return instance; + } + + ComponentInstanceWasi02 instanceCreator(store, functionTypes); + instance = instanceCreator.loadInstance(name); + store->registerComponentInstance(name, instance); + return instance; +} + +const FunctionType* getWasiFunctionType(LiftedWasiFunction* function) +{ + return function->functionType(); +} + +void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedWasiFunction* function, CanonOptions* options) { - return ComponentInstanceWasi02::loadInstance(external, functionTypes); + ComponentInstance* instance = function->instance(); + + switch (function->type()) { + case LiftedWasiFunction::ioOutputStreamSubscribe026: { + 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::cliGetStdout026: { + ComponentResource* resource = new ComponentResourceWasiStream(instance->type()->getType(0)->asTypeResource(), 1); + result[0] = Value(static_cast(options->instance()->appendHandle(state, resource))); + break; + } + case LiftedWasiFunction::cliGetTerminalStdout026: { + 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 diff --git a/src/wasi/WASI02.h b/src/wasi/WASI02.h index a63ce8b12..76510d2d1 100644 --- a/src/wasi/WASI02.h +++ b/src/wasi/WASI02.h @@ -26,8 +26,14 @@ namespace Walrus { class ComponentInstance; class DefinedFunctionTypes; - -ComponentInstance* wasi02LoadInstance(ComponentType::External& external, DefinedFunctionTypes& functionTypes); +class CanonOptions; +class ComponentHandle; +class LiftedWasiFunction; + +ComponentInstance* wasi02LoadInstance(Store* store, DefinedFunctionTypes& functionTypes, std::string& name); +const FunctionType* getWasiFunctionType(LiftedWasiFunction* function); +void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedWasiFunction* function, CanonOptions* options); +bool dropWasiResource(ExecutionState& state, ComponentHandle* handle); } // namespace Walrus