Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/system/lib/wasmfs/js_impl_backend.h
6174 views
1
// Copyright 2022 The Emscripten Authors. All rights reserved.
2
// Emscripten is available under two separate licenses, the MIT license and the
3
// University of Illinois/NCSA Open Source License. Both these licenses can be
4
// found in the LICENSE file.
5
6
#pragma once
7
8
#include "backend.h"
9
#include "memory_backend.h"
10
#include "support.h"
11
#include "wasmfs.h"
12
13
//
14
// A JS Impl backend has files that are implemented by JS code. Each backend
15
// object in C++ can call JS to define the proper JS code on that side, and we
16
// keep a mapping of C++ pointer of backend => JS code in JS. Then, when we do
17
// something like a read from a JSImplFile we pass it the backend, find the
18
// proper code to run, and run it. This adds a layer of indirection at the JS
19
// level that makes it easy to write new backends in 99% JS.
20
//
21
// Each file operation in the _wasmfs_jsimpl_* APIs that we call from here take
22
// the backend and a pointer to this file itself. Those allow the JS to identify
23
// both the backend and the particular file. TODO: We could use dense indexes
24
// instead of pointers, and use an array instead of a map.
25
//
26
// To write a new backend in JS, you basically do the following:
27
//
28
// 1. Add a declaration of the C function to create the backend in the
29
// "backend creation" section of emscripten/wasmfs.h. (One line.)
30
// 2. Add a cpp file for the new backend, and implement the C function from 1,
31
// which should create it on both the C++ (using JSImplBackend) and JS
32
// sides. (By convention, the C function should just call into C++ and JS
33
// which do the interesting work; the C is just a thin wrapper.) (A few
34
// lines.)
35
// 3. Write a new JS library, and add the implementation of the JS method just
36
// mentioned, which should set up the mapping from the C++ backend object's
37
// address to the JS code containing the hooks to read and write etc. (99%
38
// of the work happens here.)
39
//
40
// For a simple example, see js_file_backend.cpp and library_wasmfs_js_file.js
41
//
42
43
// Index type that is used on the JS side to refer to backends and file
44
// handles. Currently these are both passed as raw pointers rather than
45
// integer handles which is why we use uintptr_t here.
46
// TODO: Use a narrower type here and avoid passing raw pointers.
47
using js_index_t = uintptr_t;
48
49
extern "C" {
50
// JSImpl API (see below for overview).
51
void _wasmfs_jsimpl_alloc_file(js_index_t backend, js_index_t index);
52
void _wasmfs_jsimpl_free_file(js_index_t backend, js_index_t index);
53
int _wasmfs_jsimpl_write(js_index_t backend,
54
js_index_t index,
55
const uint8_t* buffer,
56
size_t length,
57
off_t offset);
58
int _wasmfs_jsimpl_read(js_index_t backend,
59
js_index_t index,
60
const uint8_t* buffer,
61
size_t length,
62
off_t offset);
63
int _wasmfs_jsimpl_get_size(js_index_t backend, js_index_t index);
64
int _wasmfs_jsimpl_set_size(js_index_t backend, js_index_t index, off_t size);
65
}
66
67
namespace wasmfs {
68
69
class JSImplFile : public DataFile {
70
js_index_t getBackendIndex() {
71
static_assert(sizeof(backend_t) == sizeof(js_index_t));
72
return js_index_t(getBackend());
73
}
74
75
js_index_t getFileIndex() {
76
static_assert(sizeof(this) == sizeof(js_index_t));
77
return js_index_t(this);
78
}
79
80
// TODO: Notify the JS about open and close events?
81
int open(oflags_t) override { return 0; }
82
int close() override { return 0; }
83
84
ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {
85
return _wasmfs_jsimpl_write(
86
getBackendIndex(), getFileIndex(), buf, len, offset);
87
}
88
89
ssize_t read(uint8_t* buf, size_t len, off_t offset) override {
90
return _wasmfs_jsimpl_read(
91
getBackendIndex(), getFileIndex(), buf, len, offset);
92
}
93
94
int flush() override { return 0; }
95
96
off_t getSize() override {
97
return _wasmfs_jsimpl_get_size(getBackendIndex(), getFileIndex());
98
}
99
100
int setSize(off_t size) override {
101
return _wasmfs_jsimpl_set_size(getBackendIndex(), getFileIndex(), size);
102
}
103
104
public:
105
JSImplFile(mode_t mode, backend_t backend) : DataFile(mode, backend) {
106
_wasmfs_jsimpl_alloc_file(getBackendIndex(), getFileIndex());
107
}
108
109
~JSImplFile() { _wasmfs_jsimpl_free_file(getBackendIndex(), getFileIndex()); }
110
};
111
112
class JSImplBackend : public Backend {
113
public:
114
std::shared_ptr<DataFile> createFile(mode_t mode) override {
115
return std::make_shared<JSImplFile>(mode, this);
116
}
117
std::shared_ptr<Directory> createDirectory(mode_t mode) override {
118
return std::make_shared<MemoryDirectory>(mode, this);
119
}
120
std::shared_ptr<Symlink> createSymlink(std::string target) override {
121
return std::make_shared<MemorySymlink>(target, this);
122
}
123
};
124
125
extern "C" {
126
127
backend_t wasmfs_create_jsimpl_backend(void) {
128
return wasmFS.addBackend(std::make_unique<JSImplBackend>());
129
}
130
131
} // extern "C"
132
133
} // namespace wasmfs
134
135