├── .gitignore ├── .prettierrc ├── .vscode ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── example ├── .gitignore ├── Makefile ├── README.md ├── example.cpp └── example.js ├── package-lock.json ├── package.json ├── src └── TinyWASI.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | node_modules/ 3 | dist/ -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "endOfLine": "auto", 4 | "proseWrap": "preserve", 5 | "trailingComma": "all", 6 | "printWidth": 100, 7 | "tabWidth": 4, 8 | "useTabs": false 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [100], 3 | "editor.tabSize": 4, 4 | "editor.insertSpaces": true 5 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "prepare", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | "problemMatcher": ["$tsc"], 12 | "label": "npm: prepare", 13 | "detail": "tsc" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 QR Date 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tiny WASI - A tiny WASI runtime written in typescript 2 | 3 | This runtime implements the bare minimum set of functions to get most C/C++ 4 | libraries that have been compiled with the "reactor" exec model running. 5 | 6 | The only thing it allows is writing to stdout/stderr, getting the current time 7 | and getting random numbers, which should really be enough for everyone. 8 | 9 | ## Example 10 | 11 | ```ts 12 | import { TinyWASI } from "tinywasi"; 13 | 14 | let tinywasi = new TinyWASI(); 15 | 16 | let imports = { 17 | , 18 | ...tinywasi.imports 19 | }; 20 | 21 | const { module, instance } = await WebAssembly.instantiate( , imports ); 22 | 23 | tinywasi.initialize( instance ); 24 | ``` 25 | 26 | ## API 27 | 28 | ### `new TinyWASI( trace?: boolean )` 29 | 30 | Instantiates a new TinyWASI runtime. 31 | 32 | #### Parameters 33 | 34 | * `trace`: boolean, optional. Wether or not to print all system calls to the 35 | console for debugging. 36 | 37 | 38 | ### `initialize( instance: WebAssembly.Instance )` 39 | 40 | Initializes the runtime and WASM instance. Call this before making any 41 | other calls. 42 | 43 | #### Parameters 44 | 45 | * `instance`: WebAssembly instance to connect to and initialize. 46 | 47 | 48 | ### `imports: array` 49 | 50 | Array containing the functions to be imported by the WebAssembly module. 51 | Pass this directly to `WebAssembly.instantiate` or add it to your own imports. 52 | 53 | 54 | ## Implemented system calls 55 | 56 | Currently TinyWASI implements the following system calls: 57 | 58 | 59 | ### `fd_fdstat_get` 60 | 61 | Implemented for the proper function of `stat` and `isatty` which are needed for 62 | printing to stdout/stderr to work correctly. 63 | 64 | 65 | ### `fd_write` 66 | 67 | Implemented for writing to stdout and stderr. 68 | Writing to any other file descriptor returns `ERRNO_BADF`. 69 | 70 | All writes to stdout (fd 1) are logged to the console using `console.log`. 71 | All writes to stderr (fd 2) are logged to the console using `console.error`. 72 | 73 | 74 | ### `clock_res_get` and `clock_time_get` 75 | 76 | Implemented to make functions like `time()` work. 77 | Right now only the realtime clock (id 0) is supported. 78 | 79 | 80 | ### `random_get` 81 | 82 | Implemented to make C++ `std::random_device` work. 83 | 84 | 85 | ## Unimplemented system calls 86 | 87 | All other system calls are unimplemented and return `ERRNO_NOSYS` while printing 88 | an error message to the console. 89 | 90 | 91 | ## Dependencies 92 | 93 | None. 94 | 95 | ### Dev-Dependencies 96 | 97 | * typescript - for compiling the typescript source files to javascript 98 | * @types/node - typescript definitions for node's API 99 | 100 | Optionally: 101 | 102 | * prettier - for code formatting using the included .prettierrc -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | *.o -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | WASI_SYSROOT = /usr/share/wasi-sysroot 2 | CC = clang++ 3 | CXX = clang++ 4 | 5 | TARGET_ARCH = \ 6 | --target=wasm32-unknown-wasi \ 7 | --sysroot=$(WASI_SYSROOT) \ 8 | -mexec-model=reactor 9 | 10 | CFLAGS = \ 11 | -Wall \ 12 | -Wno-unused-command-line-argument \ 13 | -Werror \ 14 | -Ofast \ 15 | -flto \ 16 | -fno-exceptions \ 17 | -ffunction-sections \ 18 | -fdata-sections 19 | 20 | CXXFLAGS = \ 21 | $(CFLAGS) \ 22 | -fno-exceptions 23 | 24 | LDFLAGS = \ 25 | -Wl,--strip-all \ 26 | -Wl,--lto-O3 27 | 28 | LDLIBS = 29 | 30 | 31 | example.wasm: example.o 32 | $(LINK.o) $^ $(LDLIBS) -o $@ 33 | 34 | example.o: example.cpp 35 | 36 | 37 | .PHONY: clean 38 | clean: 39 | $(RM) example.wasm example.o -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # TinyWASI example 2 | 3 | This is just a tiny example showing off how to use some of the TinyWASI 4 | functionality. 5 | 6 | Call `make` to build the example c++ code. 7 | 8 | You need to have a recent enough version clang and the WASI C library installed 9 | and have the `WASI_SYSROOT` path in the Makefile points to the right location. -------------------------------------------------------------------------------- /example/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // These are some handy macros to import and export function from JavaScript. 5 | #define WASM_EXPORT(name) __attribute__((export_name(#name))) 6 | #define WASM_IMPORT(module, name) __attribute__((import_module(#module), import_name(#name))) 7 | 8 | 9 | /* This is an example for a simple function that gets called from JavaScript 10 | // and returns a value. Since the parameters are all numbers they can be passed 11 | // directly. For an example with strings see the next example. 12 | */ 13 | int WASM_EXPORT(multiply) multiply(int a, int b) 14 | { 15 | return a * b; 16 | } 17 | 18 | 19 | /* This is an example of a function that takes a string parameter. 20 | // Since complex data types can't be passed directly there is a function that 21 | // allocates a buffer and returns a pointer to it so it can be filled from 22 | // JavaScript before calling the actual function. 23 | */ 24 | std::string string; 25 | 26 | const char *WASM_EXPORT(getString) getString(size_t length) 27 | { 28 | string.resize(length); 29 | return string.data(); 30 | } 31 | 32 | void WASM_EXPORT(printString) printString(void) 33 | { 34 | std::cout << "The string is: " << string << std::endl; 35 | } 36 | 37 | 38 | /* A function showing off making a syscall to the WASI runtime 39 | // to get the current time and then print it to stdout, which gets redirected 40 | // to console.log by the runtime. 41 | */ 42 | void WASM_EXPORT(printTime) printTime() 43 | { 44 | int now = time(NULL); 45 | 46 | std::cout << "The UNIX time is: " << now << std::endl; 47 | } 48 | 49 | 50 | /* This function makes a call to an imported JavaSCript function and 51 | // prints the return value. 52 | */ 53 | extern int WASM_IMPORT(js, getValue) getJSValue(void); 54 | 55 | void WASM_EXPORT(printJSValue) printJSValue() 56 | { 57 | int value = getJSValue(); 58 | 59 | std::cout << "The value is: " << value << std::endl; 60 | } 61 | 62 | 63 | /* This function prints a message to stderr, which should show up as error 64 | // in the javascript console. 65 | */ 66 | void WASM_EXPORT(printError) printError() 67 | { 68 | std::cerr << "Look at this error!"; 69 | } -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | import { TinyWASI } from './../dist/TinyWASI.js'; 2 | import fs from 'fs'; 3 | 4 | // Load the compiled wasm file. 5 | let data = fs.readFileSync('./example.wasm'); 6 | var source = new Uint8Array(data); 7 | 8 | // Instantiate the TinyWASI runtime with tracing enabled. 9 | // This logs all the system calls the WASM module makes to the console. 10 | let tinywasi = new TinyWASI(true); 11 | 12 | // Set up our imports object consisting of out own functions 13 | // and the TinyWASI imports object. 14 | let imports = { 15 | js: { 16 | getValue: () => { 17 | return 7; 18 | }, 19 | }, 20 | ...tinywasi.imports, 21 | }; 22 | 23 | // Instantiate the wasm module from the source. 24 | const { instance } = await WebAssembly.instantiate(source, imports); 25 | 26 | // Initialize the TinyWASI runtime. 27 | tinywasi.initialize(instance); 28 | 29 | // Call the exported multiply function and log the result. 30 | console.log(`2 * 3 is ${instance.exports.multiply(2, 3)}`); 31 | 32 | // Create a string and allocate memory for it in the WASM module by calling 33 | // the exported getString function to allocate space and get the location 34 | // of the buffer in memory. 35 | const utf8EncodedString = new TextEncoder('utf-8').encode('TinyWASI!'); 36 | const stringData = new Uint8Array( 37 | instance.exports.memory.buffer, 38 | instance.exports.getString(utf8EncodedString.length), 39 | utf8EncodedString.length, 40 | ); 41 | 42 | // Copy the encoded string into the buffer inside the WASM module. 43 | stringData.set(utf8EncodedString); 44 | 45 | // Call the printString function to log the string to the console. 46 | instance.exports.printString(); 47 | 48 | // Call the printTime function. Should log the current UNIX time to the console. 49 | instance.exports.printTime(); 50 | 51 | // Call the printJSValue function. This will call the getValue function 52 | // specified in the imports and log the result to the console. 53 | instance.exports.printJSValue(); 54 | 55 | // Call the printError function. This will print an error to the console. 56 | instance.exports.printError(); 57 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinywasi", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tinywasi", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "^17.0.21", 13 | "typescript": "^4.6.2" 14 | } 15 | }, 16 | "node_modules/@types/node": { 17 | "version": "17.0.21", 18 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", 19 | "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", 20 | "dev": true 21 | }, 22 | "node_modules/typescript": { 23 | "version": "4.6.2", 24 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", 25 | "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", 26 | "dev": true, 27 | "bin": { 28 | "tsc": "bin/tsc", 29 | "tsserver": "bin/tsserver" 30 | }, 31 | "engines": { 32 | "node": ">=4.2.0" 33 | } 34 | } 35 | }, 36 | "dependencies": { 37 | "@types/node": { 38 | "version": "17.0.21", 39 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", 40 | "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", 41 | "dev": true 42 | }, 43 | "typescript": { 44 | "version": "4.6.2", 45 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", 46 | "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", 47 | "dev": true 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinywasi", 3 | "version": "1.0.2", 4 | "description": "A tiny WASI runtime", 5 | "types": "dist/TinyWASI.d.ts", 6 | "type": "module", 7 | "exports": { 8 | "./types": "./dist/TinyWASI.d.ts", 9 | "./package.json": "./package.json", 10 | ".": "./dist/TinyWASI.js" 11 | }, 12 | "scripts": { 13 | "prepare": "tsc", 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "engines": { 17 | "node": "^14.19.0 || >=16.0.0" 18 | }, 19 | "license": "MIT", 20 | "author": "QRDate.org + contributors", 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/qrdate/tinywasi.git" 24 | }, 25 | "devDependencies": { 26 | "@types/node": "^17.0.21", 27 | "typescript": "^4.6.2" 28 | }, 29 | "files": [ 30 | "dist/" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/TinyWASI.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto'; 2 | 3 | export class TinyWASI { 4 | private instance?: WebAssembly.Instance = undefined; 5 | 6 | private WASI_ERRNO_SUCCESS = 0; 7 | private WASI_ERRNO_BADF = 8; 8 | private WASI_ERRNO_NOSYS = 52; 9 | private WASI_ERRNO_INVAL = 28; 10 | 11 | private WASI_FILETYPE_CHARACTER_DEVICE = 2; 12 | 13 | private WASI_RIGHTS_FD_SYNC = 1 << 4; 14 | private WASI_RIGHTS_FD_WRITE = 1 << 6; 15 | private WASI_RIGHTS_FD_FILESTAT_GET = 1 << 21; 16 | 17 | private WASI_FDFLAGS_APPEND = 1 << 0; 18 | 19 | private nameSpaces: { [key: string]: { [key: string]: CallableFunction | undefined } } = { 20 | wasi_snapshot_preview1: { 21 | args_get: undefined, // ((param i32 i32) (result i32)) 22 | args_sizes_get: undefined, // ((param i32 i32) (result i32)) 23 | 24 | clock_res_get: this.clock_res_get, // ((param i32 i32) (result i32)) 25 | clock_time_get: this.clock_time_get, // ((param i32 i64 i32) (result i32)) 26 | 27 | environ_get: undefined, // ((param i32 i32) (result i32)) 28 | environ_sizes_get: undefined, // ((param i32 i32) (result i32)) 29 | 30 | fd_advise: undefined, // ((param i32 i64 i64 i32) (result i32)) 31 | fd_allocate: undefined, // ((param i32 i64 i64) (result i32)) 32 | fd_close: undefined, // ((param i32) (result i32)) 33 | fd_datasync: undefined, // ((param i32) (result i32)) 34 | fd_fdstat_get: this.fd_fdstat_get, // ((param i32 i32) (result i32)) 35 | fd_fdstat_set_flags: undefined, // ((param i32 i32) (result i32)) 36 | fd_fdstat_set_rights: undefined, // ((param i32 i64 i64) (result i32)) 37 | fd_filestat_get: undefined, // ((param i32 i32) (result i32)) 38 | fd_filestat_set_size: undefined, // ((param i32 i64) (result i32)) 39 | fd_filestat_set_times: undefined, // ((param i32 i64 i64 i32) (result i32)) 40 | fd_pread: undefined, // ((param i32 i32 i32 i64 i32) (result i32)) 41 | fd_prestat_dir_name: undefined, // ((param i32 i32 i32) (result i32)) 42 | fd_prestat_get: undefined, // ((param i32 i32) (result i32)) 43 | fd_pwrite: undefined, // ((param i32 i32 i32 i64 i32) (result i32)) 44 | fd_read: undefined, // ((param i32 i32 i32 i32) (result i32)) 45 | fd_readdir: undefined, // ((param i32 i32 i32 i64 i32) (result i32)) 46 | fd_renumber: undefined, // ((param i32 i32) (result i32)) 47 | fd_seek: undefined, // ((param i32 i64 i32 i32) (result i32)) 48 | fd_sync: undefined, // ((param i32) (result i32)) 49 | fd_tell: undefined, // ((param i32 i32) (result i32)) 50 | fd_write: this.fd_write, // ((param i32 i32 i32 i32) (result i32)) 51 | 52 | path_create_directory: undefined, // ((param i32 i32 i32) (result i32)) 53 | path_filestat_get: undefined, // ((param i32 i32 i32 i32 i32) (result i32)) 54 | path_filestat_set_times: undefined, // ((param i32 i32 i32 i32 i64 i64 i32) (result i32)) 55 | path_link: undefined, // ((param i32 i32 i32 i32 i32 i32 i32) (result i32)) 56 | path_open: undefined, // ((param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)) 57 | path_readlink: undefined, // ((param i32 i32 i32 i32 i32 i32) (result i32)) 58 | path_remove_directory: undefined, // ((param i32 i32 i32) (result i32)) 59 | path_rename: undefined, // ((param i32 i32 i32 i32 i32 i32) (result i32)) 60 | path_symlink: undefined, // ((param i32 i32 i32 i32 i32) (result i32)) 61 | path_unlink_file: undefined, // ((param i32 i32 i32) (result i32)) 62 | 63 | poll_oneoff: undefined, // ((param i32 i32 i32 i32) (result i32)) 64 | 65 | proc_exit: undefined, // ((param i32)) 66 | proc_raise: undefined, // ((param i32) (result i32)) 67 | 68 | random_get: this.random_get, // ((param i32 i32) (result i32)) 69 | 70 | sched_yield: undefined, // ((result i32)) 71 | 72 | sock_recv: undefined, // ((param i32 i32 i32 i32 i32 i32) (result i32)) 73 | sock_send: undefined, // ((param i32 i32 i32 i32 i32) (result i32)) 74 | sock_shutdown: undefined, // ((param i32 i32) (result i32)) 75 | }, 76 | }; 77 | 78 | constructor(trace?: boolean) { 79 | for (const ns of Object.keys(this.nameSpaces)) { 80 | const nameSpace = this.nameSpaces[ns]; 81 | 82 | for (const fn of Object.keys(nameSpace)) { 83 | let func = nameSpace[fn] || this.nosys(fn); 84 | 85 | func = func.bind(this); 86 | 87 | if (trace) func = this.trace(fn, func).bind(this); 88 | 89 | nameSpace[fn] = func; 90 | } 91 | } 92 | } 93 | 94 | initialize(instance: WebAssembly.Instance) { 95 | this.instance = instance; 96 | 97 | const initialize = instance.exports._initialize as CallableFunction; 98 | initialize(); 99 | } 100 | 101 | get imports(): WebAssembly.Imports { 102 | return this.nameSpaces as WebAssembly.Imports; 103 | } 104 | 105 | private getMemory(): WebAssembly.Memory { 106 | if (this.instance) return this.instance.exports.memory as WebAssembly.Memory; 107 | else throw new Error('Attempt to access instance before initialisation!'); 108 | } 109 | 110 | private getDataView(): DataView { 111 | if (this.instance) 112 | return new DataView((this.instance.exports.memory as WebAssembly.Memory).buffer); 113 | else throw new Error('Attempt to access instance before initialisation!'); 114 | } 115 | 116 | private trace(name: string, origFunc: CallableFunction): CallableFunction { 117 | return (...args: number[]): number => { 118 | const result = origFunc(...args); 119 | console.log(`Trace: ${name}(${args.toString()}) -> ${result}`); 120 | return result; 121 | }; 122 | } 123 | 124 | private nosys(name: string): CallableFunction { 125 | return (...args: number[]): number => { 126 | console.error(`Unimplemented call to ${name}(${args.toString()})`); 127 | return this.WASI_ERRNO_NOSYS; 128 | }; 129 | } 130 | 131 | private clock_res_get(id: number, resOut: number): number { 132 | if (id !== 0) return this.WASI_ERRNO_INVAL; 133 | 134 | const view = this.getDataView(); 135 | 136 | view.setUint32(resOut, 1000000.0 % 0x100000000, true); 137 | view.setUint32(resOut + 4, 1000000.0 / 0x100000000, true); 138 | 139 | return this.WASI_ERRNO_SUCCESS; 140 | } 141 | 142 | private clock_time_get(id: number, precision: number, timeOut: number): number { 143 | if (id !== 0) return this.WASI_ERRNO_INVAL; 144 | 145 | const view = this.getDataView(); 146 | 147 | const now = new Date().getTime(); 148 | 149 | view.setUint32(timeOut, (now * 1000000.0) % 0x100000000, true); 150 | view.setUint32(timeOut + 4, (now * 1000000.0) / 0x100000000, true); 151 | 152 | return this.WASI_ERRNO_SUCCESS; 153 | } 154 | 155 | private fd_fdstat_get(fd: number, fdstat: number): number { 156 | if (fd > 2) return this.WASI_ERRNO_BADF; 157 | 158 | const view = this.getDataView(); 159 | 160 | view.setUint8(fdstat, this.WASI_FILETYPE_CHARACTER_DEVICE); 161 | view.setUint16(fdstat + 2, this.WASI_FDFLAGS_APPEND, true); 162 | view.setUint16( 163 | fdstat + 8, 164 | this.WASI_RIGHTS_FD_SYNC | this.WASI_RIGHTS_FD_WRITE | this.WASI_RIGHTS_FD_FILESTAT_GET, 165 | true, 166 | ); 167 | view.setUint16(fdstat + 16, 0, true); 168 | 169 | return this.WASI_ERRNO_SUCCESS; 170 | } 171 | 172 | private fd_write(fd: number, iovs: number, iovsLen: number, nwritten: number): number { 173 | if (fd > 2) return this.WASI_ERRNO_BADF; 174 | 175 | const view = this.getDataView(); 176 | const memory = this.getMemory(); 177 | 178 | const buffers: Uint8Array[] = []; 179 | 180 | for (let i = 0; i < iovsLen; i++) { 181 | const iov = iovs + i * 8; 182 | const offset = view.getUint32(iov, true); 183 | const len = view.getUint32(iov + 4, true); 184 | 185 | buffers.push(new Uint8Array(memory.buffer, offset, len)); 186 | } 187 | 188 | const length = buffers.reduce((s, b) => s + b.length, 0); 189 | 190 | const buffer = new Uint8Array(length); 191 | let offset = 0; 192 | 193 | buffers.forEach((b) => { 194 | buffer.set(b, offset); 195 | offset += b.length; 196 | }); 197 | 198 | const string = new TextDecoder('utf-8').decode(buffer).replace(/\n$/, ''); 199 | 200 | if (fd === 1) console.log(string); 201 | else console.error(string); 202 | 203 | view.setUint32(nwritten, buffer.length, true); 204 | 205 | return this.WASI_ERRNO_SUCCESS; 206 | } 207 | 208 | private random_get(pointer: number, size: number): number { 209 | const memory = this.getMemory(); 210 | 211 | const buffer = new Uint8Array(memory.buffer, pointer, size); 212 | 213 | crypto.randomFillSync(buffer); 214 | 215 | return this.WASI_ERRNO_SUCCESS; 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "ES6", 5 | "moduleResolution": "Node", 6 | "declaration": true, 7 | "rootDir": "./src", 8 | "outDir": "./dist", 9 | "strict": true, 10 | "types": [ 11 | "node" 12 | ], 13 | } 14 | } --------------------------------------------------------------------------------