[WASMFS] JS File Backend Structure (#15562)

Relevant Issue: #15041

Tentative JS File Backend Structure

Introduced user-visible header file.
Users can create a new JS Backend and create JS Backed Files under both In-Memory and JS Backend directories.
This commit is contained in:
Ethan Lee 2021-11-24 00:38:48 -05:00 коммит произвёл GitHub
Родитель 63b3f94493
Коммит 7e6d511d91
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
14 изменённых файлов: 467 добавлений и 38 удалений

Просмотреть файл

@ -1,7 +1,9 @@
var WasmfsLibrary = {
$wasmFS$JSMemoryFiles : [],
$wasmFS$JSMemoryFreeList: [],
$wasmFS$preloadedFiles: [],
$wasmFS$preloadedDirs: [],
$FS__deps: ['$wasmFS$preloadedFiles', '$wasmFS$preloadedDirs'],
$FS__deps: ['$wasmFS$preloadedFiles', '$wasmFS$preloadedDirs', '$wasmFS$JSMemoryFiles', '$wasmFS$JSMemoryFreeList'],
$FS : {
// TODO: Clean up the following functions - currently copied from library_fs.js directly.
createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) {
@ -85,6 +87,54 @@ var WasmfsLibrary = {
// For file preloading, cwd should be '/' to begin with.
return '/';
}
},
_emscripten_write_js_file: function(index, buffer, length, offset) {
try {
if (!wasmFS$JSMemoryFiles[index]) {
// Initialize typed array on first write operation.
wasmFS$JSMemoryFiles[index] = new Uint8Array(offset + length);
}
if (offset + length > wasmFS$JSMemoryFiles[index].length) {
// Resize the typed array if the length of the write buffer exceeds its capacity.
var oldContents = wasmFS$JSMemoryFiles[index];
var newContents = new Uint8Array(offset + length);
newContents.set(oldContents);
wasmFS$JSMemoryFiles[index] = newContents;
}
wasmFS$JSMemoryFiles[index].set(HEAPU8.subarray(buffer, buffer + length), offset);
return 0;
} catch (err) {
return {{{ cDefine('EIO') }}};
}
},
_emscripten_read_js_file: function(index, buffer, length, offset) {
try {
HEAPU8.set(wasmFS$JSMemoryFiles[index].subarray(offset, offset + length), buffer);
return 0;
} catch (err) {
return {{{ cDefine('EIO') }}};
}
},
_emscripten_get_js_file_size: function(index) {
return wasmFS$JSMemoryFiles[index] ? wasmFS$JSMemoryFiles[index].length : 0;
},
_emscripten_create_js_file: function() {
// Find a free entry in the $wasmFS$JSMemoryFreeList or append a new entry to
// wasmFS$JSMemoryFiles.
if (wasmFS$JSMemoryFreeList.length) {
// Pop off the top of the free list.
var index = wasmFS$JSMemoryFreeList.pop();
return index;
}
wasmFS$JSMemoryFiles.push(null);
return wasmFS$JSMemoryFiles.length - 1;
},
_emscripten_remove_js_file: function(index) {
wasmFS$JSMemoryFiles[index] = null;
// Add the index to the free list.
wasmFS$JSMemoryFreeList.push(index);
}
}

Просмотреть файл

@ -0,0 +1,36 @@
/*
* Copyright 2021 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/
#pragma once
#include <stdint.h>
#include <sys/stat.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Backend* backend_t;
// Obtains the backend_t of a specified path.
backend_t wasmfs_get_backend_by_path(char* path);
// Obtains the backend_t of a specified fd.
backend_t wasmfs_get_backend_by_fd(int fd);
// Creates a JSFile Backend in the new file system.
backend_t wasmfs_create_js_file_backend();
// Creates a file in a specific backend and returns an fd to an open file.
uint32_t wasmfs_create_file(char* pathname, mode_t mode, backend_t backend);
// Creates a new directory in the new file system under a specific backend.
long wasmfs_create_directory(char* path, long mode, backend_t backend);
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -14,8 +14,8 @@
namespace wasmfs {
// A backend (or modular backend) provides a base for the new file system to
// extend its storage capabilities. Files and directories will be represented in
// the file system structure, but their underlying backing could exist in
// extend its storage capabilities. Files and directories will be represented
// in the file system structure, but their underlying backing could exist in
// persistent storage, another thread, etc.
class Backend {
@ -29,7 +29,5 @@ public:
// Note: Backends will be defined in cpp files, but functions to instantiate
// them will be defined in a header file. This is so that any unused backends
// are not linked in if they are not called.
// TODO: In the next PR, a user-visible header is introduced. Update this
// comment then.
backend_t createMemoryFileBackend();
} // namespace wasmfs

Просмотреть файл

@ -2,6 +2,7 @@
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
// This file defines the file object of the new file system.
// Current Status: Work in Progress.
// See https://github.com/emscripten-core/emscripten/issues/15041.
@ -25,6 +26,7 @@ class Backend;
// This represents an opaque pointer to a Backend. A user may use this to
// specify a backend in file operations.
using backend_t = Backend*;
const backend_t NullBackend = nullptr;
class File : public std::enable_shared_from_this<File> {
@ -62,6 +64,8 @@ public:
return (ino_t)this;
}
backend_t getBackend() { return backend; }
class Handle {
protected:
@ -101,7 +105,8 @@ public:
}
protected:
File(FileKind kind, mode_t mode) : kind(kind), mode(mode) {}
File(FileKind kind, mode_t mode, backend_t backend)
: kind(kind), mode(mode), backend(backend) {}
// A mutex is needed for multiple accesses to the same file.
std::recursive_mutex mutex;
@ -121,6 +126,9 @@ protected:
// dependencies where the parent and child have shared_ptrs that reference
// each other. This prevents the case in which an uncollectable cycle occurs.
std::weak_ptr<File> parent;
// This specifies which backend a file is associated with.
backend_t backend;
};
class DataFile : public File {
@ -131,7 +139,8 @@ class DataFile : public File {
public:
static constexpr FileKind expectedKind = File::DataFileKind;
DataFile(mode_t mode) : File(File::DataFileKind, mode) {}
DataFile(mode_t mode, backend_t backend)
: File(File::DataFileKind, mode, backend) {}
virtual ~DataFile() = default;
class Handle : public File::Handle {
@ -161,15 +170,10 @@ protected:
// This value was also copied from the existing file system.
size_t getSize() override { return 4096; }
// This specifies which backend a directory is associated with. By default,
// files and sub-directories added to this directory's entries will be created
// through this same backend unless an alternative is specified.
backend_t backend;
public:
static constexpr FileKind expectedKind = File::DirectoryKind;
Directory(mode_t mode, backend_t backend)
: File(File::DirectoryKind, mode), backend(backend) {}
: File(File::DirectoryKind, mode, backend) {}
struct Entry {
std::string name;
@ -231,8 +235,6 @@ public:
return entries;
}
backend_t getBackend() { return getDir()->backend; }
#ifdef WASMFS_DEBUG
void printKeys() {
for (auto keyPair : getDir()->entries) {
@ -253,7 +255,6 @@ public:
}
}
};
// Obtains parent directory of a given pathname.
// Will return a nullptr if the parent is not a directory.
// Will error if the forbiddenAncestor is encountered while processing.
@ -265,8 +266,8 @@ getDir(std::vector<std::string>::iterator begin,
long& err,
std::shared_ptr<File> forbiddenAncestor = nullptr);
// Return a vector of the '/'-delimited components of a path. The first element
// will be "/" iff the path is an absolute path.
// Return a vector of the '/'-delimited components of a path. The first
// element will be "/" iff the path is an absolute path.
std::vector<std::string> splitPath(char* pathname);
} // namespace wasmfs

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright 2021 The Emscripten Authors. All rights reserved.
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
// This file defines the JS file backend and JS file of the new file system.
// Current Status: Work in Progress.
// See https://github.com/emscripten-core/emscripten/issues/15041.
#include "backend.h"
#include "wasmfs.h"
using js_index_t = uint32_t;
extern "C" {
int _emscripten_write_js_file(js_index_t index,
const uint8_t* buffer,
size_t length,
off_t offset);
int _emscripten_read_js_file(js_index_t index,
const uint8_t* buffer,
size_t length,
off_t offset);
int _emscripten_get_js_file_size(js_index_t index);
int _emscripten_create_js_file();
void _emscripten_remove_js_file(js_index_t index);
}
namespace wasmfs {
// This class describes a file that lives in JS Memory
class JSFile : public DataFile {
// This index indicates the location of the JS File in the backing JS array.
js_index_t index;
// JSFiles will write from a Wasm Memory buffer into the backing JS array.
__wasi_errno_t write(const uint8_t* buf, size_t len, off_t offset) override {
return _emscripten_write_js_file(index, buf, len, offset);
}
// JSFiles will read from the backing JS array into a Wasm Memory buffer.
__wasi_errno_t read(uint8_t* buf, size_t len, off_t offset) override {
// The caller should have already checked that the offset + len does
// not exceed the file's size.
assert(offset + len <= getSize());
return _emscripten_read_js_file(index, buf, len, offset);
}
// The size of the JSFile is defined as the length of the backing JS array.
size_t getSize() override { return _emscripten_get_js_file_size(index); }
public:
JSFile(mode_t mode, backend_t backend) : DataFile(mode, backend) {
// Create a new file in the backing JS array and store its index.
index = _emscripten_create_js_file();
}
// Remove the typed array file contents in the backing JS array.
~JSFile() { _emscripten_remove_js_file(index); }
};
class JSFileBackend : public Backend {
public:
std::shared_ptr<DataFile> createFile(mode_t mode) override {
return std::make_shared<JSFile>(mode, this);
}
std::shared_ptr<Directory> createDirectory(mode_t mode) override {
return std::make_shared<Directory>(mode, this);
}
};
// This function is exposed to users to instantiate a new JSBackend.
extern "C" backend_t wasmfs_create_js_file_backend() {
return wasmFS.addBackend(std::make_unique<JSFileBackend>());
}
} // namespace wasmfs

Просмотреть файл

@ -2,6 +2,7 @@
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
// This file defines the memory file class of the new file system.
// This should be the only backend file type defined in a header since it is the
// default type. Current Status: Work in Progress. See
@ -23,7 +24,7 @@ class MemoryFile : public DataFile {
size_t getSize() override { return buffer.size(); }
public:
MemoryFile(mode_t mode) : DataFile(mode) {}
MemoryFile(mode_t mode, backend_t backend) : DataFile(mode, backend) {}
class Handle : public DataFile::Handle {

Просмотреть файл

@ -2,6 +2,7 @@
// Emscripten is available under two separate licenses, the MIT license and the
// University of Illinois/NCSA Open Source License. Both these licenses can be
// found in the LICENSE file.
// This file defines the memory file backend of the new file system.
// Current Status: Work in Progress.
// See https://github.com/emscripten-core/emscripten/issues/15041.
@ -15,7 +16,7 @@ class MemoryFileBackend : public Backend {
public:
std::shared_ptr<DataFile> createFile(mode_t mode) override {
return std::make_shared<MemoryFile>(mode);
return std::make_shared<MemoryFile>(mode, this);
}
std::shared_ptr<Directory> createDirectory(mode_t mode) override {
return std::make_shared<Directory>(mode, this);

Просмотреть файл

@ -28,7 +28,7 @@ class StdinFile : public DataFile {
size_t getSize() override { return 0; }
public:
StdinFile(mode_t mode) : DataFile(mode) {}
StdinFile(mode_t mode) : DataFile(mode, NullBackend) {}
static std::shared_ptr<StdinFile> getSingleton();
};
@ -45,7 +45,7 @@ class StdoutFile : public DataFile {
size_t getSize() override { return 0; }
public:
StdoutFile(mode_t mode) : DataFile(mode) {}
StdoutFile(mode_t mode) : DataFile(mode, NullBackend) {}
static std::shared_ptr<StdoutFile> getSingleton();
};
@ -65,7 +65,7 @@ class StderrFile : public DataFile {
size_t getSize() override { return 0; }
public:
StderrFile(mode_t mode) : DataFile(mode) {}
StderrFile(mode_t mode) : DataFile(mode, NullBackend) {}
static std::shared_ptr<StderrFile> getSingleton();
};
} // namespace wasmfs

Просмотреть файл

@ -288,6 +288,58 @@ __wasi_errno_t __wasi_fd_close(__wasi_fd_t fd) {
return __WASI_ERRNO_SUCCESS;
}
backend_t wasmfs_get_backend_by_fd(int fd) {
auto fileTable = wasmFS.getLockedFileTable();
// Check that an open file exists corresponding to the given fd.
auto openFile = fileTable[fd];
if (!openFile) {
return NullBackend;
}
auto lockedOpenFile = openFile.locked();
return lockedOpenFile.getFile()->getBackend();
}
// This function is exposed to users to allow them to obtain a backend_t for a
// specified path.
backend_t wasmfs_get_backend_by_path(char* path) {
auto pathParts = splitPath(path);
if (pathParts.empty()) {
return NullBackend;
}
// TODO: Remove this when path parsing has been re-factored.
if (pathParts.size() == 1 && pathParts[0] == "/") {
return wasmFS.getRootDirectory()->getBackend();
}
auto base = pathParts.back();
long err;
auto parentDir = getDir(pathParts.begin(), pathParts.end() - 1, err);
// Parent node doesn't exist.
if (!parentDir) {
return NullBackend;
}
auto lockedParentDir = parentDir->locked();
// TODO: In a future PR, edit function to just return the requested file
// instead of having to first obtain the parent dir.
auto curr = lockedParentDir.getEntry(base);
if (curr) {
auto dir = curr->dynCast<Directory>();
return dir ? dir->getBackend() : NullBackend;
}
return NullBackend;
}
static long doStat(std::shared_ptr<File> file, struct stat* buffer) {
auto lockedFile = file->locked();
@ -371,7 +423,10 @@ long __syscall_fstat64(long fd, long buf) {
return doStat(openFile.locked().getFile(), buffer);
}
__wasi_fd_t __syscall_open(long pathname, long flags, ...) {
static __wasi_fd_t doOpen(char* pathname,
long flags,
mode_t mode,
backend_t backend = NullBackend) {
int accessMode = (flags & O_ACCMODE);
bool canWrite = false;
@ -384,7 +439,7 @@ __wasi_fd_t __syscall_open(long pathname, long flags, ...) {
((flags) & ~(O_CREAT | O_EXCL | O_DIRECTORY | O_TRUNC | O_APPEND | O_RDWR |
O_WRONLY | O_RDONLY | O_LARGEFILE | O_CLOEXEC)) == 0);
auto pathParts = splitPath((char*)pathname);
auto pathParts = splitPath(pathname);
if (pathParts.empty()) {
return -EINVAL;
@ -417,16 +472,16 @@ __wasi_fd_t __syscall_open(long pathname, long flags, ...) {
// If O_DIRECTORY is also specified, still create a regular file:
// https://man7.org/linux/man-pages/man2/open.2.html#BUGS
if (flags & O_CREAT) {
// Since mode is optional, mode is specified using varargs.
mode_t mode = 0;
va_list vl;
va_start(vl, flags);
mode = va_arg(vl, int);
va_end(vl);
// Mask all permissions sent via mode.
mode &= S_IALLUGO;
// Create an empty in-memory file.
auto backend = lockedParentDir.getBackend();
// By default, the backend that the file is created in is the same as the
// parent directory. However, if a backend is passed as a parameter, then
// that backend is used.
if (!backend) {
backend = parentDir->getBackend();
}
auto created = backend->createFile(mode);
// TODO: When rename is implemented make sure that one can atomically
@ -456,8 +511,25 @@ __wasi_fd_t __syscall_open(long pathname, long flags, ...) {
return wasmFS.getLockedFileTable().add(openFile);
}
long __syscall_mkdir(long path, long mode) {
auto pathParts = splitPath((char*)path);
// This function is exposed to users and allows users to create a file in a
// specific backend. An fd to an open file is returned.
__wasi_fd_t wasmfs_create_file(char* pathname, mode_t mode, backend_t backend) {
return doOpen(pathname, O_CREAT, mode, backend);
}
__wasi_fd_t __syscall_open(long pathname, long flags, ...) {
// Since mode is optional, mode is specified using varargs.
mode_t mode = 0;
va_list vl;
va_start(vl, flags);
mode = va_arg(vl, int);
va_end(vl);
return doOpen((char*)pathname, flags, mode);
}
static long doMkdir(char* path, long mode, backend_t backend = NullBackend) {
auto pathParts = splitPath(path);
if (pathParts.empty()) {
return -EINVAL;
@ -490,7 +562,13 @@ long __syscall_mkdir(long path, long mode) {
// https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
mode &= S_IRWXUGO | S_ISVTX;
// Create an empty in-memory directory.
auto backend = lockedParentDir.getBackend();
// By default, the backend that the directory is created in is the same as
// the parent directory. However, if a backend is passed as a parameter,
// then that backend is used.
if (!backend) {
backend = parentDir->getBackend();
}
auto created = backend->createDirectory(mode);
lockedParentDir.setEntry(base, created);
@ -498,6 +576,16 @@ long __syscall_mkdir(long path, long mode) {
}
}
// This function is exposed to users and allows users to specify a particular
// backend that a directory should be created within.
long wasmfs_create_directory(char* path, long mode, backend_t backend) {
return doMkdir(path, mode, backend);
}
long __syscall_mkdir(long path, long mode) {
return doMkdir((char*)path, mode);
}
__wasi_errno_t __wasi_fd_seek(__wasi_fd_t fd,
__wasi_filedelta_t offset,
__wasi_whence_t whence,

Просмотреть файл

@ -56,7 +56,7 @@ void WasmFS::preloadFiles() {
#endif
// Obtain the backend of the root directory.
auto rootBackend = getRootDirectory()->locked().getBackend();
auto rootBackend = getRootDirectory()->getBackend();
// Ensure that files are preloaded from the main thread.
assert(emscripten_is_main_runtime_thread());
@ -129,7 +129,7 @@ void WasmFS::preloadFiles() {
auto base = pathParts.back();
// TODO: Generalize so that MemoryFile is not hard-coded.
auto created = std::make_shared<MemoryFile>((mode_t)mode);
auto created = std::make_shared<MemoryFile>((mode_t)mode, rootBackend);
long err;
auto parentDir = getDir(pathParts.begin(), pathParts.end() - 1, err);

4
tests/test_other.py поставляемый
Просмотреть файл

@ -11230,6 +11230,10 @@ void foo() {}
self.set_setting('WASMFS')
self.do_run_in_out_file_test(test_file('wasmfs/wasmfs_readfile.c'))
def test_wasmfs_jsfile(self):
self.set_setting('WASMFS')
self.do_run_in_out_file_test('wasmfs/wasmfs_jsfile.c')
@disabled('Running with initial >2GB heaps is not currently supported on the CI version of Node')
def test_hello_world_above_2gb(self):
self.run_process([EMCC, test_file('hello_world.c'), '-sGLOBAL_BASE=2147483648', '-sINITIAL_MEMORY=3GB'])

Просмотреть файл

@ -0,0 +1,131 @@
/*
* Copyright 2021 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/
#include <assert.h>
#include <emscripten/emscripten.h>
#include <emscripten/wasmfs.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
void write_and_read(const char* msg, int fd) {
char buf[200] = {};
errno = 0;
write(fd, msg, strlen(msg));
assert(errno == 0);
errno = 0;
lseek(fd, 0, SEEK_SET);
assert(errno == 0);
read(fd, buf, sizeof(buf));
assert(errno == 0);
printf("%s\n", buf);
}
int main() {
backend_t JSBackend = wasmfs_create_js_file_backend();
// Create a new JS file under root.
int fd = wasmfs_create_file("/testfile", 0777, JSBackend);
// Ensure that the size of the JS file is zero.
struct stat file;
assert(fstat(fd, &file) != -1);
assert(file.st_size == 0);
// Try writing to and reading from the file.
const char* msg = "Test with a new JS file\n";
write_and_read(msg, fd);
EM_ASM({out(wasmFS$JSMemoryFiles[0])});
// Verify that the size of the JS File is the same as the written buffer.
assert(fstat(fd, &file) != -1);
assert(file.st_size == strlen(msg));
// Try seeking to the beginning of the file and overwriting its contents.
assert(lseek(fd, 0, SEEK_SET) != -1);
write_and_read(msg, fd);
EM_ASM({out(wasmFS$JSMemoryFiles[0])});
// Try appending to the end of the JS File.
write_and_read(msg, fd);
// Verify that the size of the JS File has increased.
assert(fstat(fd, &file) != -1);
assert(file.st_size == strlen(msg) * 2);
close(fd);
assert(unlink("/testfile") != -1);
// Check that the file has been cleaned up in the JS array.
EM_ASM({out("Expect null: " + wasmFS$JSMemoryFiles[0])});
// Try creating a new JS directory under root.
assert(wasmfs_create_directory("/test-dir", 0777, JSBackend) != -1);
// Try to create a new JS file under this new directory.
int fd2 = open("/test-dir/jsfile", O_RDWR | O_CREAT, 0777);
// Try writing to and reading from the file.
const char* msg2 = "Test with a JS file created under a JS directory\n";
write_and_read(msg2, fd2);
// Under first-fit policy, wasmFS$JSMemoryFiles[0] should be populated again.
EM_ASM({out(wasmFS$JSMemoryFiles[0])});
// Check that the JS file has the correct backend type.
assert(wasmfs_get_backend_by_fd(fd2) == JSBackend);
// Try creating an in-memory file under a JS directory.
backend_t memoryBackend = wasmfs_get_backend_by_path("/");
int fd3 = wasmfs_create_file("/test-dir/inmemoryfile", 0777, memoryBackend);
// Ensure that the JS array size has not changed.
int size = EM_ASM_INT({ return wasmFS$JSMemoryFiles.length; });
assert(size == 1);
const char* msg3 =
"Test with an in-memory file created under a JS directory\n";
write_and_read(msg3, fd3);
// Create a new JS file under root.
int fd4 = wasmfs_create_file("/testfile2", 0777, JSBackend);
const char* msg4 = "Test with a JS file created under root\n";
// Try setting the file offset to an arbitrary value.
assert(lseek(fd4, 10, SEEK_SET) != -1);
char buf[200] = {};
errno = 0;
write(fd4, msg4, strlen(msg4));
assert(errno == 0);
errno = 0;
lseek(fd4, 0, SEEK_SET);
assert(errno == 0);
int bytesRead = read(fd4, buf, sizeof(buf));
for (ssize_t i = 0; i < bytesRead; i++) {
if (buf[i] == 0) {
printf("\\0");
} else {
printf("%c", buf[i]);
}
}
printf("\n");
EM_ASM({out(wasmFS$JSMemoryFiles[1])});
return 0;
}

Просмотреть файл

@ -0,0 +1,41 @@
Test with a new JS file
Uint8Array(24) [
84, 101, 115, 116, 32, 119, 105,
116, 104, 32, 97, 32, 110, 101,
119, 32, 74, 83, 32, 102, 105,
108, 101, 10
]
Test with a new JS file
Uint8Array(24) [
84, 101, 115, 116, 32, 119, 105,
116, 104, 32, 97, 32, 110, 101,
119, 32, 74, 83, 32, 102, 105,
108, 101, 10
]
Test with a new JS file
Test with a new JS file
Expect null: null
Test with a JS file created under a JS directory
Uint8Array(49) [
84, 101, 115, 116, 32, 119, 105, 116, 104,
32, 97, 32, 74, 83, 32, 102, 105, 108,
101, 32, 99, 114, 101, 97, 116, 101, 100,
32, 117, 110, 100, 101, 114, 32, 97, 32,
74, 83, 32, 100, 105, 114, 101, 99, 116,
111, 114, 121, 10
]
Test with an in-memory file created under a JS directory
\0\0\0\0\0\0\0\0\0\0Test with a JS file created under root
Uint8Array(49) [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84, 101, 115, 116, 32, 119, 105, 116, 104, 32,
97, 32, 74, 83, 32, 102, 105, 108, 101, 32,
99, 114, 101, 97, 116, 101, 100, 32, 117, 110,
100, 101, 114, 32, 114, 111, 111, 116, 10
]

Просмотреть файл

@ -1361,7 +1361,7 @@ class libwasmfs(MTLibrary):
def get_files(self):
return files_in_path(
path='system/lib/wasmfs',
filenames=['syscalls.cpp', 'file_table.cpp', 'file.cpp', 'wasmfs.cpp', 'streams.cpp', 'memory_file.cpp', 'memory_file_backend.cpp'])
filenames=['syscalls.cpp', 'file_table.cpp', 'file.cpp', 'wasmfs.cpp', 'streams.cpp', 'memory_file.cpp', 'memory_file_backend.cpp', 'js_file_backend.cpp'])
def can_build(self):
return settings.WASMFS