Path: blob/main/system/lib/wasmfs/js_impl_backend.h
6174 views
// Copyright 2022 The Emscripten Authors. All rights reserved.1// Emscripten is available under two separate licenses, the MIT license and the2// University of Illinois/NCSA Open Source License. Both these licenses can be3// found in the LICENSE file.45#pragma once67#include "backend.h"8#include "memory_backend.h"9#include "support.h"10#include "wasmfs.h"1112//13// A JS Impl backend has files that are implemented by JS code. Each backend14// object in C++ can call JS to define the proper JS code on that side, and we15// keep a mapping of C++ pointer of backend => JS code in JS. Then, when we do16// something like a read from a JSImplFile we pass it the backend, find the17// proper code to run, and run it. This adds a layer of indirection at the JS18// level that makes it easy to write new backends in 99% JS.19//20// Each file operation in the _wasmfs_jsimpl_* APIs that we call from here take21// the backend and a pointer to this file itself. Those allow the JS to identify22// both the backend and the particular file. TODO: We could use dense indexes23// instead of pointers, and use an array instead of a map.24//25// To write a new backend in JS, you basically do the following:26//27// 1. Add a declaration of the C function to create the backend in the28// "backend creation" section of emscripten/wasmfs.h. (One line.)29// 2. Add a cpp file for the new backend, and implement the C function from 1,30// which should create it on both the C++ (using JSImplBackend) and JS31// sides. (By convention, the C function should just call into C++ and JS32// which do the interesting work; the C is just a thin wrapper.) (A few33// lines.)34// 3. Write a new JS library, and add the implementation of the JS method just35// mentioned, which should set up the mapping from the C++ backend object's36// address to the JS code containing the hooks to read and write etc. (99%37// of the work happens here.)38//39// For a simple example, see js_file_backend.cpp and library_wasmfs_js_file.js40//4142// Index type that is used on the JS side to refer to backends and file43// handles. Currently these are both passed as raw pointers rather than44// integer handles which is why we use uintptr_t here.45// TODO: Use a narrower type here and avoid passing raw pointers.46using js_index_t = uintptr_t;4748extern "C" {49// JSImpl API (see below for overview).50void _wasmfs_jsimpl_alloc_file(js_index_t backend, js_index_t index);51void _wasmfs_jsimpl_free_file(js_index_t backend, js_index_t index);52int _wasmfs_jsimpl_write(js_index_t backend,53js_index_t index,54const uint8_t* buffer,55size_t length,56off_t offset);57int _wasmfs_jsimpl_read(js_index_t backend,58js_index_t index,59const uint8_t* buffer,60size_t length,61off_t offset);62int _wasmfs_jsimpl_get_size(js_index_t backend, js_index_t index);63int _wasmfs_jsimpl_set_size(js_index_t backend, js_index_t index, off_t size);64}6566namespace wasmfs {6768class JSImplFile : public DataFile {69js_index_t getBackendIndex() {70static_assert(sizeof(backend_t) == sizeof(js_index_t));71return js_index_t(getBackend());72}7374js_index_t getFileIndex() {75static_assert(sizeof(this) == sizeof(js_index_t));76return js_index_t(this);77}7879// TODO: Notify the JS about open and close events?80int open(oflags_t) override { return 0; }81int close() override { return 0; }8283ssize_t write(const uint8_t* buf, size_t len, off_t offset) override {84return _wasmfs_jsimpl_write(85getBackendIndex(), getFileIndex(), buf, len, offset);86}8788ssize_t read(uint8_t* buf, size_t len, off_t offset) override {89return _wasmfs_jsimpl_read(90getBackendIndex(), getFileIndex(), buf, len, offset);91}9293int flush() override { return 0; }9495off_t getSize() override {96return _wasmfs_jsimpl_get_size(getBackendIndex(), getFileIndex());97}9899int setSize(off_t size) override {100return _wasmfs_jsimpl_set_size(getBackendIndex(), getFileIndex(), size);101}102103public:104JSImplFile(mode_t mode, backend_t backend) : DataFile(mode, backend) {105_wasmfs_jsimpl_alloc_file(getBackendIndex(), getFileIndex());106}107108~JSImplFile() { _wasmfs_jsimpl_free_file(getBackendIndex(), getFileIndex()); }109};110111class JSImplBackend : public Backend {112public:113std::shared_ptr<DataFile> createFile(mode_t mode) override {114return std::make_shared<JSImplFile>(mode, this);115}116std::shared_ptr<Directory> createDirectory(mode_t mode) override {117return std::make_shared<MemoryDirectory>(mode, this);118}119std::shared_ptr<Symlink> createSymlink(std::string target) override {120return std::make_shared<MemorySymlink>(target, this);121}122};123124extern "C" {125126backend_t wasmfs_create_jsimpl_backend(void) {127return wasmFS.addBackend(std::make_unique<JSImplBackend>());128}129130} // extern "C"131132} // namespace wasmfs133134135