diff --git a/src/runtime/ComponentInstance.h b/src/runtime/ComponentInstance.h index a27cd90ec..efc4a2d8f 100644 --- a/src/runtime/ComponentInstance.h +++ b/src/runtime/ComponentInstance.h @@ -250,9 +250,12 @@ class ComponentHandle { enum Kind { ResourceRepKind, #ifdef ENABLE_WASI - ResourceWasiStreamKind, + ResourceWasiInputStreamKind, + ResourceWasiOutputStreamKind, ResourceWasiPollableKind, ResourceWasiTerminalKind, + ResourceWasiFileKind, + ResourceWasiDirectoryKind, #endif /* ENABLE_WASI */ }; diff --git a/src/shell/Shell.cpp b/src/shell/Shell.cpp index 955d288bd..b6c437675 100644 --- a/src/shell/Shell.cpp +++ b/src/shell/Shell.cpp @@ -44,9 +44,11 @@ struct ParseOptions { std::vector fileNames; // WASI options +#ifdef ENABLE_WASI std::vector wasi_envs; - std::vector> wasi_dirs; + Walrus::Wasi02DirMap wasi_dirs; int argsIndex = -1; +#endif }; static uint32_t s_JITFlags = 0; @@ -1212,6 +1214,9 @@ static void parseArguments(int argc, const char* argv[], ParseOptions& options) ++i; options.exportToRun = argv[i]; continue; + } else if (strcmp(argv[i], "--enable-web-assembly3") == 0) { + s_FeatureFlags |= wabt::FeatureFlagValue::enableWebAssembly3; + continue; #if defined(WALRUS_ENABLE_JIT) } else if (strcmp(argv[i], "--jit") == 0) { s_JITFlags |= JITFlagValue::useJIT; @@ -1226,16 +1231,15 @@ static void parseArguments(int argc, const char* argv[], ParseOptions& options) s_JITFlags |= JITFlagValue::disableRegAlloc; continue; #endif - } else if (strcmp(argv[i], "--enable-web-assembly3") == 0) { - s_FeatureFlags |= wabt::FeatureFlagValue::enableWebAssembly3; - continue; } else if (strcmp(argv[i], "--env") == 0) { if (i + 1 == argc || argv[i + 1][0] == '-') { fprintf(stderr, "error: --env requires an argument\n"); exit(1); } ++i; +#ifdef ENABLE_WASI options.wasi_envs.emplace_back(argv[i]); +#endif continue; } else if (strcmp(argv[i], "--mapdirs") == 0) { if (i + 2 >= argc || argv[i + 1][0] == '-' || argv[i + 2][0] == '-') { @@ -1243,7 +1247,9 @@ static void parseArguments(int argc, const char* argv[], ParseOptions& options) exit(1); } // pair of (mapped path, real_path) - options.wasi_dirs.push_back(std::make_pair(argv[i + 2], argv[i + 1])); +#ifdef ENABLE_WASI + options.wasi_dirs.push_back(Wasi02DirMapEntry{ argv[i + 2], argv[i + 1] }); +#endif i += 2; continue; } else if (strcmp(argv[i], "--args") == 0) { @@ -1252,17 +1258,21 @@ static void parseArguments(int argc, const char* argv[], ParseOptions& options) exit(1); } ++i; +#ifdef ENABLE_WASI options.fileNames.emplace_back(argv[i]); options.argsIndex = i; +#endif break; } else if (strcmp(argv[i], "--help") == 0) { fprintf(stdout, "Usage: walrus [OPTIONS] \n\n"); fprintf(stdout, "OPTIONS:\n"); fprintf(stdout, "\t--help\n\t\tShow this message then exit.\n\n"); + fprintf(stdout, "\t--enable-web-assembly3\n\t\tEnable support for web assembly3 features.\n\n"); +#if defined(WALRUS_ENABLE_JIT) fprintf(stdout, "\t--jit\n\t\tEnable just-in-time interpretation.\n\n"); fprintf(stdout, "\t--jit-verbose\n\t\tEnable verbose output for just-in-time interpretation.\n\n"); fprintf(stdout, "\t--jit-verbose-color\n\t\tEnable colored verbose output for just-in-time interpretation.\n\n"); - fprintf(stdout, "\t--enable-web-assembly3\n\t\tEnable support for web assembly3 features.\n\n"); +#endif fprintf(stdout, "\t--mapdirs \n\t\tMap real directories to virtual ones for WASI functions to use.\n\t\tExample: ./walrus test.wasm --mapdirs this/real/directory/ this/virtual/directory\n\n"); fprintf(stdout, "\t--env\n\t\tShare host environment to walrus WASI.\n\n"); fprintf(stdout, "\t--args [ ... ]\n\t\tRun Webassembly module with arguments: must be followed by the name of the Webassembly module file, then optionally following arguments which are passed on to the module\n\t\tExample: ./walrus --args test.wasm 'hello' 'world' 42\n\n"); @@ -1320,7 +1330,7 @@ int main(int argc, const char* argv[]) std::vector dirs; for (auto& dir : options.wasi_dirs) { - dirs.push_back({ dir.first.c_str(), dir.second.c_str() }); + dirs.push_back({ dir.mappedPath, dir.realPath }); } uvwasi_options_t init_options; @@ -1341,7 +1351,7 @@ int main(int argc, const char* argv[]) WASI::initialize(&uvwasi); // Wasi 0.2 - store->initWasiData(wasi02InitData(init_options.argc, init_options.argv, init_options.envp)); + store->initWasiData(wasi02InitData(init_options.argc, init_options.argv, init_options.envp, options.wasi_dirs)); #endif int result = 0; diff --git a/src/wasi/WASI02.cpp b/src/wasi/WASI02.cpp index 3249a6617..0628be83c 100644 --- a/src/wasi/WASI02.cpp +++ b/src/wasi/WASI02.cpp @@ -22,7 +22,9 @@ namespace Walrus { -WasiStoreData::WasiStoreData(int argc, const char** argv, const char** envp) +WasiStoreData::WasiStoreData(int argc, const char** argv, const char** envp, Wasi02DirMap& preOpens) + : m_prevNow(0) + , m_prevClockNow(clock()) { m_arguments.reserve(static_cast(argc)); while (argc-- > 0) { @@ -48,11 +50,15 @@ WasiStoreData::WasiStoreData(int argc, const char** argv, const char** envp) envp++; } } + + for (auto& it : preOpens) { + m_preOpens.push_back(std::pair(it.mappedPath, it.realPath)); + } } -WasiStoreData* wasi02InitData(int argc, const char** argv, const char** envp) +WasiStoreData* wasi02InitData(int argc, const char** argv, const char** envp, Wasi02DirMap& preOpens) { - return new WasiStoreData(argc, argv, envp); + return new WasiStoreData(argc, argv, envp, preOpens); } static ComponentInstance* findWasiComponentInstance(Store* store, size_t instanceId) diff --git a/src/wasi/WASI02.h b/src/wasi/WASI02.h index d5e2be1bc..687e7a652 100644 --- a/src/wasi/WASI02.h +++ b/src/wasi/WASI02.h @@ -30,7 +30,14 @@ class CanonOptions; class ComponentHandle; class LiftedWasiFunction; -WasiStoreData* wasi02InitData(int argc, const char** argv, const char** envp); +struct Wasi02DirMapEntry { + const char* mappedPath; + const char* realPath; +}; + +typedef std::vector Wasi02DirMap; + +WasiStoreData* wasi02InitData(int argc, const char** argv, const char** envp, Wasi02DirMap& preOpens); 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 index f4caed89f..c2a5ce70c 100644 --- a/src/wasi/WASI02Impl.cpp +++ b/src/wasi/WASI02Impl.cpp @@ -22,23 +22,74 @@ namespace Walrus { +enum DescriptorFlags : uint32_t { + flagRead = 1 << 0, + flagWrite = 1 << 1, + flagFileIntegritySync = 1 << 2, + flagDataIntegritySync = 1 << 3, + flagRequestedWriteSync = 1 << 4, + flagMutateDirectory = 1 << 5, +}; + +enum OpenFlags : uint32_t { + openCreate = 1 << 0, + openDirectory = 1 << 1, + openExclusive = 1 << 2, + openTruncate = 1 << 3, +}; + static void throwNoMemory(ExecutionState& state) { std::string message = "out of memory"; Trap::throwException(state, message); } -static ComponentResourceWasiStream* asStream(ComponentHandle* handle) +static inline ComponentResourceWasiStream* asStream(ComponentHandle* handle) { - ASSERT(handle->kind() == ComponentHandle::ResourceWasiStreamKind); + ASSERT(handle->kind() == ComponentHandle::ResourceWasiInputStreamKind || handle->kind() == ComponentHandle::ResourceWasiOutputStreamKind); return reinterpret_cast(handle); } +static inline ComponentResourceWasiFile* asFile(ComponentHandle* handle) +{ + ASSERT(handle->kind() == ComponentHandle::ResourceWasiFileKind); + return reinterpret_cast(handle); +} + +static inline ComponentResourceWasiDirectory* asDirectory(ComponentHandle* handle) +{ + ASSERT(handle->kind() == ComponentHandle::ResourceWasiDirectoryKind); + return reinterpret_cast(handle); +} + +static inline long int maxFileOffset(uint64_t offset) +{ + unsigned long int max = ~static_cast(0) >> 1; + return offset > max ? static_cast(max) : static_cast(offset); +} + +void WasiRefCountedFile::destroyFile() +{ + ASSERT(m_refCount == 0); + if (m_file != stdin && m_file != stdout && m_file != stderr) { + fclose(m_file); + } + delete this; +} + void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedWasiFunction* function, CanonOptions* options) { ComponentInstance* instance = function->instance(); switch (function->type()) { + case LiftedWasiFunction::ioPollableBlock02: { + uint32_t index = argv[0].asI32(); + ComponentHandle* handle = options->instance()->getHandle(state, index); + if (handle->kind() != ComponentHandle::ResourceWasiPollableKind) { + ComponentInstance::throwInvalidHandle(state, index); + } + break; + } case LiftedWasiFunction::ioOutputStreamCheckWrite02: { uint32_t offset = argv[1].asI32(); uint64_t value = 0xffffffff; @@ -46,37 +97,105 @@ void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedW options->memory()->buffer()[offset] = 0; break; } + case LiftedWasiFunction::ioInputStreamRead02: { + uint32_t index = argv[0].asI32(); + long int size = maxFileOffset(argv[1].asI64()); + uint32_t offset = argv[2].asI32(); + + ASSERT(!options->memory()->is64()); + options->memoryCheckRange32(state, 1, offset, 12); + + ComponentHandle* handle = options->instance()->getHandle(state, index); + if (handle->kind() != ComponentHandle::ResourceWasiInputStreamKind) { + ComponentInstance::throwInvalidHandle(state, index); + } + + ComponentResourceWasiStream* stream = asStream(handle); + if (stream->isClosed()) { + options->memory()->buffer()[offset + 4] = 1; + options->memory()->buffer()[offset] = 1; + break; + } + + if (stream->seekNeeded()) { + fseek(stream->file(), stream->offset(), SEEK_SET); + } + std::vector buffer(size); + size_t read = fread(buffer.data(), 1, static_cast(size), stream->file()); + stream->advanceOffset(read); + uint32_t start = options->memoryMalloc32(state, 1, read); + memcpy(options->memory()->buffer() + start, buffer.data(), size); + + options->memory()->buffer()[offset] = 0; + uint32_t* list = reinterpret_cast(options->memory()->buffer() + offset); + list[1] = start; + list[2] = read; + + if (read < static_cast(size)) { + stream->dropFileRef(); + } + break; + } + case LiftedWasiFunction::ioInputStreamSubscribe02: + case LiftedWasiFunction::ioOutputStreamSubscribe02: { + uint32_t index = argv[0].asI32(); + ComponentHandle* handle = options->instance()->getHandle(state, index); + + ComponentHandle::Kind kind = ComponentHandle::ResourceWasiInputStreamKind; + if (function->type() == LiftedWasiFunction::ioOutputStreamSubscribe02) { + kind = ComponentHandle::ResourceWasiOutputStreamKind; + } + + if (handle->kind() != kind) { + 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::ioOutputStreamWrite02: { uint32_t index = argv[0].asI32(); + uint32_t offset = argv[3].asI32(); + ComponentHandle* handle = options->instance()->getHandle(state, index); - if (handle->kind() != ComponentHandle::ResourceWasiStreamKind) { + if (handle->kind() != ComponentHandle::ResourceWasiOutputStreamKind) { ComponentInstance::throwInvalidHandle(state, index); } ASSERT(!options->memory()->is64()); + options->memoryCheckRange32(state, 1, offset, 12); + ComponentResourceWasiStream* stream = asStream(handle); + if (stream->isClosed()) { + options->memory()->buffer()[offset + 4] = 1; + options->memory()->buffer()[offset] = 1; + break; + } + 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()); + if (stream->seekNeeded()) { + fseek(stream->file(), stream->offset(), SEEK_SET); + } + size_t written = fwrite(options->memory()->buffer() + bufferStart, bufferSize, 1, stream->file()); + stream->advanceOffset(written); - 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(); + uint32_t offset = argv[1].asI32(); + ComponentHandle* handle = options->instance()->getHandle(state, index); - if (handle->kind() != ComponentHandle::ResourceWasiStreamKind) { + if (handle->kind() != ComponentHandle::ResourceWasiOutputStreamKind) { 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))); + ASSERT(!options->memory()->is64()); + options->memoryCheckRange32(state, 1, offset, 12); + options->memory()->buffer()[offset] = 0; break; } case LiftedWasiFunction::cliGetEnvironment02: { @@ -136,7 +255,9 @@ void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedW break; } case LiftedWasiFunction::cliGetStdout02: { - ComponentResource* resource = new ComponentResourceWasiStream(instance->type()->getType(0)->asTypeResource(), stdout); + WasiRefCountedFile* fileRef = new WasiRefCountedFile(stdout); + ComponentTypeResource* resourceType = instance->type()->getType(0)->asTypeResource(); + ComponentResource* resource = new ComponentResourceWasiStream(resourceType, ComponentHandle::ResourceWasiOutputStreamKind, fileRef, ComponentResourceWasiStream::NoSeek); result[0] = Value(static_cast(options->instance()->appendHandle(state, resource))); break; } @@ -148,6 +269,122 @@ void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedW options->memory()->buffer()[offset] = 1; break; } + case LiftedWasiFunction::clockMonotonicNow02: { + WasiStoreData* data = instance->store()->wasiData(); + clock_t now = clock(); + uint64_t now64 = data->prevNow() + static_cast(now - data->prevClockNow()) * (1000000000 / CLOCKS_PER_SEC); + data->setPrevClockNow(now); + data->setPrevNow(now64); + result[0] = Value(static_cast(now64)); + break; + } + case LiftedWasiFunction::fileSystemDescriptorReadViaStream02: { + uint32_t descriptorIndex = argv[0].asI32(); + long int offset = maxFileOffset(argv[1].asI64()); + uint32_t resultOffset = argv[2].asI32(); + + ComponentHandle* handle = options->instance()->getHandle(state, descriptorIndex); + if (handle->kind() != ComponentHandle::ResourceWasiFileKind) { + ComponentInstance::throwInvalidHandle(state, descriptorIndex); + } + + WasiRefCountedFile* fileRef = asFile(handle)->addFileRef(); + ComponentTypeResource* resourceType = instance->type()->getType(2)->asTypeResource(); + ComponentResource* resource = new ComponentResourceWasiStream(resourceType, ComponentHandle::ResourceWasiInputStreamKind, fileRef, offset); + uint32_t resultResource = options->instance()->appendHandle(state, resource); + options->memory()->store(state, resultOffset, 4, resultResource); + options->memory()->buffer()[resultOffset] = 0; + break; + } + case LiftedWasiFunction::fileSystemDescriptorOpenAt02: { + uint32_t descriptorIndex = argv[0].asI32(); + uint32_t pathStart = argv[2].asI32(); + uint32_t pathSize = argv[3].asI32(); + uint32_t openFlags = argv[4].asI32(); + uint32_t flags = argv[5].asI32(); + uint32_t resultOffset = argv[6].asI32(); + + ASSERT(!options->memory()->is64()); + + ComponentHandle* handle = options->instance()->getHandle(state, descriptorIndex); + if (handle->kind() != ComponentHandle::ResourceWasiDirectoryKind) { + ComponentInstance::throwInvalidHandle(state, descriptorIndex); + } + + const char* access = "r"; + bool invalid = false; + if ((flags & flagWrite) != 0) { + if (openFlags & openCreate) { + access = (flags & flagRead) != 0 ? "w+" : "w"; + } else { + access = "r+"; + } + } else if ((flags & flagRead) != 0) { + if ((openFlags & openCreate) != 0) { + invalid = true; + } + } else { + invalid = true; + } + + if ((openFlags & (openDirectory | openExclusive | openTruncate)) != 0) { + invalid = true; + } + + if (invalid) { + std::string message = "invalid/unsupported flag combination for open"; + Trap::throwException(state, message); + } + + std::string path = asDirectory(handle)->realPath(); + path.append("/"); + options->memoryCheckRange32(state, 1, pathStart, pathSize); + path.append(reinterpret_cast(options->memory()->buffer() + pathStart), pathSize); + FILE* file = fopen(path.c_str(), access); + + if (file == NULL) { + // Support more error codes. + options->memory()->store(state, resultOffset, 4, 0); + options->memory()->buffer()[resultOffset] = 1; + break; + } + + WasiRefCountedFile* fileRef = new WasiRefCountedFile(file); + ComponentResource* resource = new ComponentResourceWasiFile(instance->type()->getType(0)->asTypeResource(), fileRef); + uint32_t resultResource = options->instance()->appendHandle(state, resource); + options->memory()->store(state, resultOffset, 4, resultResource); + options->memory()->buffer()[resultOffset] = 0; + break; + } + case LiftedWasiFunction::fileSystemGetDirectories02: { + uint32_t offset = argv[0].asI32(); + const std::vector>& preOpens = instance->store()->wasiData()->preOpens(); + + ASSERT(!options->memory()->is64()); + if (preOpens.size() >= Memory::s_maxMemory32 / 12) { + throwNoMemory(state); + } + + uint32_t length = static_cast(preOpens.size()); + uint32_t start = options->memoryMalloc32(state, 4, length * 12); + 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 : preOpens) { + if (it.first.length() >= Component::MaxStringByteLength || it.second.length() >= Component::MaxStringByteLength) { + throwNoMemory(state); + } + + ComponentResource* resource = new ComponentResourceWasiDirectory(instance->type()->getType(0)->asTypeResource(), it.first, it.second); + *argBuffer++ = options->instance()->appendHandle(state, resource); + length = static_cast(it.first.length()); + start = static_cast(options->storeLatin1String(state, reinterpret_cast(it.first.data()), &length)); + *argBuffer++ = start; + *argBuffer++ = length; + } + break; + } default: std::string message = "unimplemented wasi function"; Trap::throwException(state, message); @@ -158,15 +395,17 @@ void callWasiFunction(ExecutionState& state, Value* argv, Value* result, LiftedW bool dropWasiResource(ExecutionState& state, ComponentHandle* handle) { switch (handle->kind()) { - case ComponentHandle::ResourceWasiStreamKind: + case ComponentHandle::ResourceWasiInputStreamKind: + case ComponentHandle::ResourceWasiOutputStreamKind: 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: + case ComponentHandle::ResourceWasiFileKind: + case ComponentHandle::ResourceWasiDirectoryKind: break; default: return false; diff --git a/src/wasi/WASI02Impl.h b/src/wasi/WASI02Impl.h index bbb02126a..29b3ff912 100644 --- a/src/wasi/WASI02Impl.h +++ b/src/wasi/WASI02Impl.h @@ -27,7 +27,27 @@ namespace Walrus { class WasiStoreData { public: - WasiStoreData(int argc, const char** argv, const char** envp); + WasiStoreData(int argc, const char** argv, const char** envp, Wasi02DirMap& preOpens); + + uint64_t prevNow() const + { + return m_prevNow; + } + + void setPrevNow(uint64_t value) + { + m_prevNow = value; + } + + clock_t prevClockNow() const + { + return m_prevClockNow; + } + + void setPrevClockNow(clock_t value) + { + m_prevClockNow = value; + } const std::vector& arguments() const { @@ -39,31 +59,88 @@ class WasiStoreData { return m_environment; } + const std::vector>& preOpens() const + { + return m_preOpens; + } + std::map& wasiInstances() { return m_wasiInstances; } private: + uint64_t m_prevNow; + clock_t m_prevClockNow; std::vector m_arguments; std::vector> m_environment; + std::vector> m_preOpens; std::map m_wasiInstances; }; +class WasiRefCountedFile { +public: + WasiRefCountedFile(FILE* file) + : m_file(file) + , m_refCount(1) + { + } + + FILE* file() + { + return m_file; + } + + void addRef() + { + m_refCount++; + } + + void releaseRef() + { + if (--m_refCount == 0) { + destroyFile(); + } + } + +private: + void destroyFile(); + + FILE* m_file; + size_t m_refCount; +}; + class ComponentResourceWasiStream : public ComponentResource { friend class ComponentResourceWasiPollable; public: - ComponentResourceWasiStream(ComponentTypeResource* type, FILE* file) - : ComponentResource(ResourceWasiStreamKind, type) + static constexpr long int NoSeek = -1; + + ComponentResourceWasiStream(ComponentTypeResource* type, Kind kind, WasiRefCountedFile* file, long int offset) + : ComponentResource(kind, type) , m_file(file) , m_pollableCount(0) + , m_offset(offset) + { + ASSERT(kind == ResourceWasiInputStreamKind || kind == ResourceWasiOutputStreamKind); + } + + ~ComponentResourceWasiStream() { + if (m_file != nullptr) { + m_file->releaseRef(); + } + } + + bool isClosed() const + { + return m_file == nullptr; } FILE* file() const { - return m_file; + ASSERT(!isClosed()); + return m_file->file(); } size_t pollableCount() const @@ -71,9 +148,35 @@ class ComponentResourceWasiStream : public ComponentResource { return m_pollableCount; } + bool seekNeeded() const + { + return m_offset != NoSeek; + } + + long int offset() const + { + return m_offset; + } + + void advanceOffset(size_t bytes) + { + if (m_offset != NoSeek) { + m_offset += static_cast(bytes); + } + } + + void dropFileRef() + { + ASSERT(!isClosed()); + m_file->releaseRef(); + m_file = nullptr; + } + private: - FILE* m_file; + // The m_file is nullptr for closed streams. + WasiRefCountedFile* m_file; size_t m_pollableCount; + long int m_offset; }; class ComponentResourceWasiPollable : public ComponentResource { @@ -116,6 +219,60 @@ class ComponentResourceWasiTerminal : public ComponentResource { int m_fileNo; }; +class ComponentResourceWasiFile : public ComponentResource { + friend class ComponentResourceWasiPollable; + +public: + ComponentResourceWasiFile(ComponentTypeResource* type, WasiRefCountedFile* file) + : ComponentResource(ResourceWasiFileKind, type) + , m_file(file) + { + } + + ~ComponentResourceWasiFile() + { + m_file->releaseRef(); + } + + FILE* file() const + { + return m_file->file(); + } + + WasiRefCountedFile* addFileRef() + { + m_file->addRef(); + return m_file; + } + +private: + WasiRefCountedFile* m_file; +}; + +class ComponentResourceWasiDirectory : public ComponentResource { +public: + ComponentResourceWasiDirectory(ComponentTypeResource* type, const std::string& mappedPath, const std::string& realPath) + : ComponentResource(ResourceWasiDirectoryKind, type) + , m_mappedPath(mappedPath) + , m_realPath(realPath) + { + } + + const std::string mappedPath() const + { + return m_mappedPath; + } + + std::string realPath() + { + return m_realPath; + } + +private: + std::string m_mappedPath; + std::string m_realPath; +}; + class LiftedWasiFunction : public LiftedFunction { public: enum Type {