@ruby/wasm-wasi 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -3
- package/dist/browser.script.umd.js +143 -110
- package/dist/browser.umd.js +1339 -1306
- package/dist/cjs/browser.d.ts +1 -1
- package/dist/cjs/browser.js +4 -3
- package/dist/cjs/index.d.ts +1 -195
- package/dist/cjs/index.js +2 -641
- package/dist/cjs/node.d.ts +1 -1
- package/dist/cjs/node.js +2 -2
- package/dist/cjs/vm.d.ts +216 -0
- package/dist/cjs/vm.js +677 -0
- package/dist/esm/browser.d.ts +1 -1
- package/dist/esm/browser.js +2 -1
- package/dist/esm/index.d.ts +1 -195
- package/dist/esm/index.js +1 -636
- package/dist/esm/node.d.ts +1 -1
- package/dist/esm/node.js +1 -1
- package/dist/esm/vm.d.ts +216 -0
- package/dist/esm/vm.js +669 -0
- package/dist/index.umd.js +1317 -1284
- package/package.json +7 -9
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ See [Cheat Sheet](https://github.com/ruby/ruby.wasm/blob/main/docs/cheat_sheet.m
|
|
|
8
8
|
|
|
9
9
|
## Ruby Version Support
|
|
10
10
|
|
|
11
|
-
| Version | Package
|
|
12
|
-
| ------- |
|
|
11
|
+
| Version | Package |
|
|
12
|
+
| ------- | --------------------------------------------------------------------------------------------------------------- |
|
|
13
13
|
| `head` | [`@ruby/head-wasm-wasi`](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-head-wasm-wasi) |
|
|
14
14
|
| `3.3` | [`@ruby/3.3-wasm-wasi`](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-3.3-wasm-wasi) |
|
|
15
15
|
| `3.2` | [`@ruby/3.2-wasm-wasi`](https://github.com/ruby/ruby.wasm/tree/main/packages/npm-packages/ruby-3.2-wasm-wasi) |
|
|
@@ -18,6 +18,41 @@ See [Cheat Sheet](https://github.com/ruby/ruby.wasm/blob/main/docs/cheat_sheet.m
|
|
|
18
18
|
|
|
19
19
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
|
|
20
20
|
|
|
21
|
+
### consolePrinter
|
|
22
|
+
|
|
23
|
+
Create a console printer that can be used as an overlay of WASI imports.
|
|
24
|
+
See the example below for how to use it.
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
const imports = {
|
|
28
|
+
wasi_snapshot_preview1: wasi.wasiImport,
|
|
29
|
+
};
|
|
30
|
+
const printer = consolePrinter();
|
|
31
|
+
printer.addToImports(imports);
|
|
32
|
+
|
|
33
|
+
const instance = await WebAssembly.instantiate(module, imports);
|
|
34
|
+
printer.setMemory(instance.exports.memory);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Note that the `stdout` and `stderr` functions are called with text, not
|
|
38
|
+
bytes. This means that bytes written to stdout/stderr will be decoded as
|
|
39
|
+
UTF-8 and then passed to the `stdout`/`stderr` functions every time a write
|
|
40
|
+
occurs without buffering.
|
|
41
|
+
|
|
42
|
+
#### Parameters
|
|
43
|
+
|
|
44
|
+
- `$0` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** (optional, default `{stdout:console.log,stderr:console.warn}`)
|
|
45
|
+
|
|
46
|
+
- `$0.stdout`  
|
|
47
|
+
- `$0.stderr`  
|
|
48
|
+
|
|
49
|
+
- `stdout` A function that will be called when stdout is written to.
|
|
50
|
+
Defaults to `console.log`.
|
|
51
|
+
- `stderr` A function that will be called when stderr is written to.
|
|
52
|
+
Defaults to `console.warn`.
|
|
53
|
+
|
|
54
|
+
Returns **any** An object that can be used as an overlay of WASI imports.
|
|
55
|
+
|
|
21
56
|
### RubyVM
|
|
22
57
|
|
|
23
58
|
A Ruby VM instance
|
|
@@ -46,7 +81,7 @@ Initialize the Ruby VM with the given command line arguments
|
|
|
46
81
|
##### Parameters
|
|
47
82
|
|
|
48
83
|
- `args` The command line arguments to pass to Ruby. Must be
|
|
49
|
-
an array of strings starting with the Ruby program name. (optional, default `["ruby.wasm","
|
|
84
|
+
an array of strings starting with the Ruby program name. (optional, default `["ruby.wasm","-EUTF-8","-e_=0"]`)
|
|
50
85
|
|
|
51
86
|
#### setInstance
|
|
52
87
|
|
|
@@ -151,6 +186,37 @@ ary.call("push", 4);
|
|
|
151
186
|
console.log(ary.call("sample").toString());
|
|
152
187
|
```
|
|
153
188
|
|
|
189
|
+
Returns **any** The result of the method call as a new RbValue.
|
|
190
|
+
|
|
191
|
+
#### callAsync
|
|
192
|
+
|
|
193
|
+
Call a given method that may call `JS::Object#await` with given arguments
|
|
194
|
+
|
|
195
|
+
##### Parameters
|
|
196
|
+
|
|
197
|
+
- `callee` name of the Ruby method to call
|
|
198
|
+
- `args` **...any** arguments to pass to the method. Must be an array of RbValue
|
|
199
|
+
|
|
200
|
+
##### Examples
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
const client = vm.eval(`
|
|
204
|
+
require 'js'
|
|
205
|
+
class HttpClient
|
|
206
|
+
def get(url)
|
|
207
|
+
JS.global.fetch(url).await
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
HttpClient.new
|
|
211
|
+
`);
|
|
212
|
+
const response = await client.callAsync(
|
|
213
|
+
"get",
|
|
214
|
+
vm.eval(`"https://example.com"`),
|
|
215
|
+
);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Returns **any** A Promise that resolves to the result of the method call as a new RbValue.
|
|
219
|
+
|
|
154
220
|
#### toPrimitive
|
|
155
221
|
|
|
156
222
|
- **See**: <https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive>
|
|
@@ -52,6 +52,107 @@
|
|
|
52
52
|
|
|
53
53
|
class WASIProcExit extends Error{constructor(code){super("exit with exit code "+code);this.code=code;}}let WASI=class WASI{start(instance){this.inst=instance;try{instance.exports._start();}catch(e){if(e instanceof WASIProcExit){return e.code}else {throw e}}}initialize(instance){this.inst=instance;instance.exports._initialize();}constructor(args,env,fds,options={}){this.args=[];this.env=[];this.fds=[];debug.enable(options.debug);this.args=args;this.env=env;this.fds=fds;const self=this;this.wasiImport={args_sizes_get(argc,argv_buf_size){const buffer=new DataView(self.inst.exports.memory.buffer);buffer.setUint32(argc,self.args.length,true);let buf_size=0;for(const arg of self.args){buf_size+=arg.length+1;}buffer.setUint32(argv_buf_size,buf_size,true);debug.log(buffer.getUint32(argc,true),buffer.getUint32(argv_buf_size,true));return 0},args_get(argv,argv_buf){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);const orig_argv_buf=argv_buf;for(let i=0;i<self.args.length;i++){buffer.setUint32(argv,argv_buf,true);argv+=4;const arg=new TextEncoder().encode(self.args[i]);buffer8.set(arg,argv_buf);buffer.setUint8(argv_buf+arg.length,0);argv_buf+=arg.length+1;}if(debug.enabled){debug.log(new TextDecoder("utf-8").decode(buffer8.slice(orig_argv_buf,argv_buf)));}return 0},environ_sizes_get(environ_count,environ_size){const buffer=new DataView(self.inst.exports.memory.buffer);buffer.setUint32(environ_count,self.env.length,true);let buf_size=0;for(const environ of self.env){buf_size+=environ.length+1;}buffer.setUint32(environ_size,buf_size,true);debug.log(buffer.getUint32(environ_count,true),buffer.getUint32(environ_size,true));return 0},environ_get(environ,environ_buf){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);const orig_environ_buf=environ_buf;for(let i=0;i<self.env.length;i++){buffer.setUint32(environ,environ_buf,true);environ+=4;const e=new TextEncoder().encode(self.env[i]);buffer8.set(e,environ_buf);buffer.setUint8(environ_buf+e.length,0);environ_buf+=e.length+1;}if(debug.enabled){debug.log(new TextDecoder("utf-8").decode(buffer8.slice(orig_environ_buf,environ_buf)));}return 0},clock_res_get(id,res_ptr){let resolutionValue;switch(id){case CLOCKID_MONOTONIC:{resolutionValue=5000n;break}case CLOCKID_REALTIME:{resolutionValue=1000000n;break}default:return ERRNO_NOSYS}const view=new DataView(self.inst.exports.memory.buffer);view.setBigUint64(res_ptr,resolutionValue,true);return ERRNO_SUCCESS},clock_time_get(id,precision,time){const buffer=new DataView(self.inst.exports.memory.buffer);if(id===CLOCKID_REALTIME){buffer.setBigUint64(time,BigInt(new Date().getTime())*1000000n,true);}else if(id==CLOCKID_MONOTONIC){let monotonic_time;try{monotonic_time=BigInt(Math.round(performance.now()*1e6));}catch(e){monotonic_time=0n;}buffer.setBigUint64(time,monotonic_time,true);}else {buffer.setBigUint64(time,0n,true);}return 0},fd_advise(fd,offset,len,advice){if(self.fds[fd]!=undefined){return self.fds[fd].fd_advise(offset,len,advice)}else {return ERRNO_BADF}},fd_allocate(fd,offset,len){if(self.fds[fd]!=undefined){return self.fds[fd].fd_allocate(offset,len)}else {return ERRNO_BADF}},fd_close(fd){if(self.fds[fd]!=undefined){const ret=self.fds[fd].fd_close();self.fds[fd]=undefined;return ret}else {return ERRNO_BADF}},fd_datasync(fd){if(self.fds[fd]!=undefined){return self.fds[fd].fd_datasync()}else {return ERRNO_BADF}},fd_fdstat_get(fd,fdstat_ptr){if(self.fds[fd]!=undefined){const{ret,fdstat}=self.fds[fd].fd_fdstat_get();if(fdstat!=null){fdstat.write_bytes(new DataView(self.inst.exports.memory.buffer),fdstat_ptr);}return ret}else {return ERRNO_BADF}},fd_fdstat_set_flags(fd,flags){if(self.fds[fd]!=undefined){return self.fds[fd].fd_fdstat_set_flags(flags)}else {return ERRNO_BADF}},fd_fdstat_set_rights(fd,fs_rights_base,fs_rights_inheriting){if(self.fds[fd]!=undefined){return self.fds[fd].fd_fdstat_set_rights(fs_rights_base,fs_rights_inheriting)}else {return ERRNO_BADF}},fd_filestat_get(fd,filestat_ptr){if(self.fds[fd]!=undefined){const{ret,filestat}=self.fds[fd].fd_filestat_get();if(filestat!=null){filestat.write_bytes(new DataView(self.inst.exports.memory.buffer),filestat_ptr);}return ret}else {return ERRNO_BADF}},fd_filestat_set_size(fd,size){if(self.fds[fd]!=undefined){return self.fds[fd].fd_filestat_set_size(size)}else {return ERRNO_BADF}},fd_filestat_set_times(fd,atim,mtim,fst_flags){if(self.fds[fd]!=undefined){return self.fds[fd].fd_filestat_set_times(atim,mtim,fst_flags)}else {return ERRNO_BADF}},fd_pread(fd,iovs_ptr,iovs_len,offset,nread_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const iovecs=Iovec.read_bytes_array(buffer,iovs_ptr,iovs_len);const{ret,nread}=self.fds[fd].fd_pread(buffer8,iovecs,offset);buffer.setUint32(nread_ptr,nread,true);return ret}else {return ERRNO_BADF}},fd_prestat_get(fd,buf_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const{ret,prestat}=self.fds[fd].fd_prestat_get();if(prestat!=null){prestat.write_bytes(buffer,buf_ptr);}return ret}else {return ERRNO_BADF}},fd_prestat_dir_name(fd,path_ptr,path_len){if(self.fds[fd]!=undefined){const{ret,prestat_dir_name}=self.fds[fd].fd_prestat_dir_name();if(prestat_dir_name!=null){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);buffer8.set(prestat_dir_name,path_ptr);}return ret}else {return ERRNO_BADF}},fd_pwrite(fd,iovs_ptr,iovs_len,offset,nwritten_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const iovecs=Ciovec.read_bytes_array(buffer,iovs_ptr,iovs_len);const{ret,nwritten}=self.fds[fd].fd_pwrite(buffer8,iovecs,offset);buffer.setUint32(nwritten_ptr,nwritten,true);return ret}else {return ERRNO_BADF}},fd_read(fd,iovs_ptr,iovs_len,nread_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const iovecs=Iovec.read_bytes_array(buffer,iovs_ptr,iovs_len);const{ret,nread}=self.fds[fd].fd_read(buffer8,iovecs);buffer.setUint32(nread_ptr,nread,true);return ret}else {return ERRNO_BADF}},fd_readdir(fd,buf,buf_len,cookie,bufused_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){let bufused=0;while(true){const{ret,dirent}=self.fds[fd].fd_readdir_single(cookie);if(ret!=0){buffer.setUint32(bufused_ptr,bufused,true);return ret}if(dirent==null){break}if(buf_len-bufused<dirent.head_length()){bufused=buf_len;break}const head_bytes=new ArrayBuffer(dirent.head_length());dirent.write_head_bytes(new DataView(head_bytes),0);buffer8.set(new Uint8Array(head_bytes).slice(0,Math.min(head_bytes.byteLength,buf_len-bufused)),buf);buf+=dirent.head_length();bufused+=dirent.head_length();if(buf_len-bufused<dirent.name_length()){bufused=buf_len;break}dirent.write_name_bytes(buffer8,buf,buf_len-bufused);buf+=dirent.name_length();bufused+=dirent.name_length();cookie=dirent.d_next;}buffer.setUint32(bufused_ptr,bufused,true);return 0}else {return ERRNO_BADF}},fd_renumber(fd,to){if(self.fds[fd]!=undefined&&self.fds[to]!=undefined){const ret=self.fds[to].fd_close();if(ret!=0){return ret}self.fds[to]=self.fds[fd];self.fds[fd]=undefined;return 0}else {return ERRNO_BADF}},fd_seek(fd,offset,whence,offset_out_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const{ret,offset:offset_out}=self.fds[fd].fd_seek(offset,whence);buffer.setBigInt64(offset_out_ptr,offset_out,true);return ret}else {return ERRNO_BADF}},fd_sync(fd){if(self.fds[fd]!=undefined){return self.fds[fd].fd_sync()}else {return ERRNO_BADF}},fd_tell(fd,offset_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const{ret,offset}=self.fds[fd].fd_tell();buffer.setBigUint64(offset_ptr,offset,true);return ret}else {return ERRNO_BADF}},fd_write(fd,iovs_ptr,iovs_len,nwritten_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const iovecs=Ciovec.read_bytes_array(buffer,iovs_ptr,iovs_len);const{ret,nwritten}=self.fds[fd].fd_write(buffer8,iovecs);buffer.setUint32(nwritten_ptr,nwritten,true);return ret}else {return ERRNO_BADF}},path_create_directory(fd,path_ptr,path_len){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const path=new TextDecoder("utf-8").decode(buffer8.slice(path_ptr,path_ptr+path_len));return self.fds[fd].path_create_directory(path)}},path_filestat_get(fd,flags,path_ptr,path_len,filestat_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const path=new TextDecoder("utf-8").decode(buffer8.slice(path_ptr,path_ptr+path_len));const{ret,filestat}=self.fds[fd].path_filestat_get(flags,path);if(filestat!=null){filestat.write_bytes(buffer,filestat_ptr);}return ret}else {return ERRNO_BADF}},path_filestat_set_times(fd,flags,path_ptr,path_len,atim,mtim,fst_flags){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const path=new TextDecoder("utf-8").decode(buffer8.slice(path_ptr,path_ptr+path_len));return self.fds[fd].path_filestat_set_times(flags,path,atim,mtim,fst_flags)}else {return ERRNO_BADF}},path_link(old_fd,old_flags,old_path_ptr,old_path_len,new_fd,new_path_ptr,new_path_len){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[old_fd]!=undefined&&self.fds[new_fd]!=undefined){const old_path=new TextDecoder("utf-8").decode(buffer8.slice(old_path_ptr,old_path_ptr+old_path_len));const new_path=new TextDecoder("utf-8").decode(buffer8.slice(new_path_ptr,new_path_ptr+new_path_len));return self.fds[new_fd].path_link(old_fd,old_flags,old_path,new_path)}else {return ERRNO_BADF}},path_open(fd,dirflags,path_ptr,path_len,oflags,fs_rights_base,fs_rights_inheriting,fd_flags,opened_fd_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const path=new TextDecoder("utf-8").decode(buffer8.slice(path_ptr,path_ptr+path_len));debug.log(path);const{ret,fd_obj}=self.fds[fd].path_open(dirflags,path,oflags,fs_rights_base,fs_rights_inheriting,fd_flags);if(ret!=0){return ret}self.fds.push(fd_obj);const opened_fd=self.fds.length-1;buffer.setUint32(opened_fd_ptr,opened_fd,true);return 0}else {return ERRNO_BADF}},path_readlink(fd,path_ptr,path_len,buf_ptr,buf_len,nread_ptr){const buffer=new DataView(self.inst.exports.memory.buffer);const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const path=new TextDecoder("utf-8").decode(buffer8.slice(path_ptr,path_ptr+path_len));debug.log(path);const{ret,data}=self.fds[fd].path_readlink(path);if(data!=null){if(data.length>buf_len){buffer.setUint32(nread_ptr,0,true);return ERRNO_BADF}buffer8.set(data,buf_ptr);buffer.setUint32(nread_ptr,data.length,true);}return ret}else {return ERRNO_BADF}},path_remove_directory(fd,path_ptr,path_len){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const path=new TextDecoder("utf-8").decode(buffer8.slice(path_ptr,path_ptr+path_len));return self.fds[fd].path_remove_directory(path)}else {return ERRNO_BADF}},path_rename(fd,old_path_ptr,old_path_len,new_fd,new_path_ptr,new_path_len){throw "FIXME what is the best abstraction for this?"},path_symlink(old_path_ptr,old_path_len,fd,new_path_ptr,new_path_len){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const old_path=new TextDecoder("utf-8").decode(buffer8.slice(old_path_ptr,old_path_ptr+old_path_len));const new_path=new TextDecoder("utf-8").decode(buffer8.slice(new_path_ptr,new_path_ptr+new_path_len));return self.fds[fd].path_symlink(old_path,new_path)}else {return ERRNO_BADF}},path_unlink_file(fd,path_ptr,path_len){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);if(self.fds[fd]!=undefined){const path=new TextDecoder("utf-8").decode(buffer8.slice(path_ptr,path_ptr+path_len));return self.fds[fd].path_unlink_file(path)}else {return ERRNO_BADF}},poll_oneoff(in_,out,nsubscriptions){throw "async io not supported"},proc_exit(exit_code){throw new WASIProcExit(exit_code)},proc_raise(sig){throw "raised signal "+sig},sched_yield(){},random_get(buf,buf_len){const buffer8=new Uint8Array(self.inst.exports.memory.buffer);for(let i=0;i<buf_len;i++){buffer8[buf+i]=Math.random()*256|0;}},sock_recv(fd,ri_data,ri_flags){throw "sockets not supported"},sock_send(fd,si_data,si_flags){throw "sockets not supported"},sock_shutdown(fd,how){throw "sockets not supported"},sock_accept(fd,flags){throw "sockets not supported"}};}};
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Create a console printer that can be used as an overlay of WASI imports.
|
|
57
|
+
* See the example below for how to use it.
|
|
58
|
+
*
|
|
59
|
+
* ```javascript
|
|
60
|
+
* const imports = {
|
|
61
|
+
* "wasi_snapshot_preview1": wasi.wasiImport,
|
|
62
|
+
* }
|
|
63
|
+
* const printer = consolePrinter();
|
|
64
|
+
* printer.addToImports(imports);
|
|
65
|
+
*
|
|
66
|
+
* const instance = await WebAssembly.instantiate(module, imports);
|
|
67
|
+
* printer.setMemory(instance.exports.memory);
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* Note that the `stdout` and `stderr` functions are called with text, not
|
|
71
|
+
* bytes. This means that bytes written to stdout/stderr will be decoded as
|
|
72
|
+
* UTF-8 and then passed to the `stdout`/`stderr` functions every time a write
|
|
73
|
+
* occurs without buffering.
|
|
74
|
+
*
|
|
75
|
+
* @param stdout A function that will be called when stdout is written to.
|
|
76
|
+
* Defaults to `console.log`.
|
|
77
|
+
* @param stderr A function that will be called when stderr is written to.
|
|
78
|
+
* Defaults to `console.warn`.
|
|
79
|
+
* @returns An object that can be used as an overlay of WASI imports.
|
|
80
|
+
*/
|
|
81
|
+
function consolePrinter({ stdout, stderr, } = {
|
|
82
|
+
stdout: console.log,
|
|
83
|
+
stderr: console.warn,
|
|
84
|
+
}) {
|
|
85
|
+
let memory = undefined;
|
|
86
|
+
let _view = undefined;
|
|
87
|
+
function getMemoryView() {
|
|
88
|
+
if (typeof memory === "undefined") {
|
|
89
|
+
throw new Error("Memory is not set");
|
|
90
|
+
}
|
|
91
|
+
if (_view === undefined || _view.buffer.byteLength === 0) {
|
|
92
|
+
_view = new DataView(memory.buffer);
|
|
93
|
+
}
|
|
94
|
+
return _view;
|
|
95
|
+
}
|
|
96
|
+
const decoder = new TextDecoder();
|
|
97
|
+
return {
|
|
98
|
+
addToImports(imports) {
|
|
99
|
+
const wasiImport = imports.wasi_snapshot_preview1;
|
|
100
|
+
const original_fd_write = wasiImport.fd_write;
|
|
101
|
+
wasiImport.fd_write = (fd, iovs, iovsLen, nwritten) => {
|
|
102
|
+
if (fd !== 1 && fd !== 2) {
|
|
103
|
+
return original_fd_write(fd, iovs, iovsLen, nwritten);
|
|
104
|
+
}
|
|
105
|
+
const view = getMemoryView();
|
|
106
|
+
const buffers = Array.from({ length: iovsLen }, (_, i) => {
|
|
107
|
+
const ptr = iovs + i * 8;
|
|
108
|
+
const buf = view.getUint32(ptr, true);
|
|
109
|
+
const bufLen = view.getUint32(ptr + 4, true);
|
|
110
|
+
return new Uint8Array(memory.buffer, buf, bufLen);
|
|
111
|
+
});
|
|
112
|
+
let written = 0;
|
|
113
|
+
let str = "";
|
|
114
|
+
for (const buffer of buffers) {
|
|
115
|
+
str += decoder.decode(buffer);
|
|
116
|
+
written += buffer.byteLength;
|
|
117
|
+
}
|
|
118
|
+
view.setUint32(nwritten, written, true);
|
|
119
|
+
const log = fd === 1 ? stdout : stderr;
|
|
120
|
+
log(str);
|
|
121
|
+
return 0;
|
|
122
|
+
};
|
|
123
|
+
const original_fd_filestat_get = wasiImport.fd_filestat_get;
|
|
124
|
+
wasiImport.fd_filestat_get = (fd, filestat) => {
|
|
125
|
+
if (fd !== 1 && fd !== 2) {
|
|
126
|
+
return original_fd_filestat_get(fd, filestat);
|
|
127
|
+
}
|
|
128
|
+
const view = getMemoryView();
|
|
129
|
+
const result = original_fd_filestat_get(fd, filestat);
|
|
130
|
+
if (result !== 0) {
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
const filetypePtr = filestat + 0;
|
|
134
|
+
view.setUint8(filetypePtr, 2); // FILETYPE_CHARACTER_DEVICE
|
|
135
|
+
return 0;
|
|
136
|
+
};
|
|
137
|
+
const original_fd_fdstat_get = wasiImport.fd_fdstat_get;
|
|
138
|
+
wasiImport.fd_fdstat_get = (fd, fdstat) => {
|
|
139
|
+
if (fd !== 1 && fd !== 2) {
|
|
140
|
+
return original_fd_fdstat_get(fd, fdstat);
|
|
141
|
+
}
|
|
142
|
+
const view = getMemoryView();
|
|
143
|
+
const fs_filetypePtr = fdstat + 0;
|
|
144
|
+
view.setUint8(fs_filetypePtr, 2); // FILETYPE_CHARACTER_DEVICE
|
|
145
|
+
const fs_rights_basePtr = fdstat + 8;
|
|
146
|
+
view.setBigUint64(fs_rights_basePtr, BigInt(1)); // RIGHTS_FD_WRITE
|
|
147
|
+
return 0;
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
setMemory(m) {
|
|
151
|
+
memory = m;
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
55
156
|
let DATA_VIEW = new DataView(new ArrayBuffer());
|
|
56
157
|
|
|
57
158
|
function data_view(mem) {
|
|
@@ -649,107 +750,6 @@
|
|
|
649
750
|
};
|
|
650
751
|
}
|
|
651
752
|
|
|
652
|
-
/**
|
|
653
|
-
* Create a console printer that can be used as an overlay of WASI imports.
|
|
654
|
-
* See the example below for how to use it.
|
|
655
|
-
*
|
|
656
|
-
* ```javascript
|
|
657
|
-
* const imports = {
|
|
658
|
-
* "wasi_snapshot_preview1": wasi.wasiImport,
|
|
659
|
-
* }
|
|
660
|
-
* const printer = consolePrinter();
|
|
661
|
-
* printer.addToImports(imports);
|
|
662
|
-
*
|
|
663
|
-
* const instance = await WebAssembly.instantiate(module, imports);
|
|
664
|
-
* printer.setMemory(instance.exports.memory);
|
|
665
|
-
* ```
|
|
666
|
-
*
|
|
667
|
-
* Note that the `stdout` and `stderr` functions are called with text, not
|
|
668
|
-
* bytes. This means that bytes written to stdout/stderr will be decoded as
|
|
669
|
-
* UTF-8 and then passed to the `stdout`/`stderr` functions every time a write
|
|
670
|
-
* occurs without buffering.
|
|
671
|
-
*
|
|
672
|
-
* @param stdout A function that will be called when stdout is written to.
|
|
673
|
-
* Defaults to `console.log`.
|
|
674
|
-
* @param stderr A function that will be called when stderr is written to.
|
|
675
|
-
* Defaults to `console.warn`.
|
|
676
|
-
* @returns An object that can be used as an overlay of WASI imports.
|
|
677
|
-
*/
|
|
678
|
-
function consolePrinter({ stdout, stderr, } = {
|
|
679
|
-
stdout: console.log,
|
|
680
|
-
stderr: console.warn,
|
|
681
|
-
}) {
|
|
682
|
-
let memory = undefined;
|
|
683
|
-
let _view = undefined;
|
|
684
|
-
function getMemoryView() {
|
|
685
|
-
if (typeof memory === "undefined") {
|
|
686
|
-
throw new Error("Memory is not set");
|
|
687
|
-
}
|
|
688
|
-
if (_view === undefined || _view.buffer.byteLength === 0) {
|
|
689
|
-
_view = new DataView(memory.buffer);
|
|
690
|
-
}
|
|
691
|
-
return _view;
|
|
692
|
-
}
|
|
693
|
-
const decoder = new TextDecoder();
|
|
694
|
-
return {
|
|
695
|
-
addToImports(imports) {
|
|
696
|
-
const wasiImport = imports.wasi_snapshot_preview1;
|
|
697
|
-
const original_fd_write = wasiImport.fd_write;
|
|
698
|
-
wasiImport.fd_write = (fd, iovs, iovsLen, nwritten) => {
|
|
699
|
-
if (fd !== 1 && fd !== 2) {
|
|
700
|
-
return original_fd_write(fd, iovs, iovsLen, nwritten);
|
|
701
|
-
}
|
|
702
|
-
const view = getMemoryView();
|
|
703
|
-
const buffers = Array.from({ length: iovsLen }, (_, i) => {
|
|
704
|
-
const ptr = iovs + i * 8;
|
|
705
|
-
const buf = view.getUint32(ptr, true);
|
|
706
|
-
const bufLen = view.getUint32(ptr + 4, true);
|
|
707
|
-
return new Uint8Array(memory.buffer, buf, bufLen);
|
|
708
|
-
});
|
|
709
|
-
let written = 0;
|
|
710
|
-
let str = "";
|
|
711
|
-
for (const buffer of buffers) {
|
|
712
|
-
str += decoder.decode(buffer);
|
|
713
|
-
written += buffer.byteLength;
|
|
714
|
-
}
|
|
715
|
-
view.setUint32(nwritten, written, true);
|
|
716
|
-
const log = fd === 1 ? stdout : stderr;
|
|
717
|
-
log(str);
|
|
718
|
-
return 0;
|
|
719
|
-
};
|
|
720
|
-
const original_fd_filestat_get = wasiImport.fd_filestat_get;
|
|
721
|
-
wasiImport.fd_filestat_get = (fd, filestat) => {
|
|
722
|
-
if (fd !== 1 && fd !== 2) {
|
|
723
|
-
return original_fd_filestat_get(fd, filestat);
|
|
724
|
-
}
|
|
725
|
-
const view = getMemoryView();
|
|
726
|
-
const result = original_fd_filestat_get(fd, filestat);
|
|
727
|
-
if (result !== 0) {
|
|
728
|
-
return result;
|
|
729
|
-
}
|
|
730
|
-
const filetypePtr = filestat + 0;
|
|
731
|
-
view.setUint8(filetypePtr, 2); // FILETYPE_CHARACTER_DEVICE
|
|
732
|
-
return 0;
|
|
733
|
-
};
|
|
734
|
-
const original_fd_fdstat_get = wasiImport.fd_fdstat_get;
|
|
735
|
-
wasiImport.fd_fdstat_get = (fd, fdstat) => {
|
|
736
|
-
if (fd !== 1 && fd !== 2) {
|
|
737
|
-
return original_fd_fdstat_get(fd, fdstat);
|
|
738
|
-
}
|
|
739
|
-
const view = getMemoryView();
|
|
740
|
-
const fs_filetypePtr = fdstat + 0;
|
|
741
|
-
view.setUint8(fs_filetypePtr, 2); // FILETYPE_CHARACTER_DEVICE
|
|
742
|
-
const fs_rights_basePtr = fdstat + 8;
|
|
743
|
-
view.setBigUint64(fs_rights_basePtr, BigInt(1)); // RIGHTS_FD_WRITE
|
|
744
|
-
return 0;
|
|
745
|
-
};
|
|
746
|
-
},
|
|
747
|
-
setMemory(m) {
|
|
748
|
-
memory = m;
|
|
749
|
-
},
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
|
|
753
753
|
/**
|
|
754
754
|
* A Ruby VM instance
|
|
755
755
|
*
|
|
@@ -822,11 +822,12 @@
|
|
|
822
822
|
* @param args The command line arguments to pass to Ruby. Must be
|
|
823
823
|
* an array of strings starting with the Ruby program name.
|
|
824
824
|
*/
|
|
825
|
-
initialize(args = ["ruby.wasm", "
|
|
825
|
+
initialize(args = ["ruby.wasm", "-EUTF-8", "-e_=0"]) {
|
|
826
826
|
const c_args = args.map((arg) => arg + "\0");
|
|
827
827
|
this.guest.rubyInit();
|
|
828
828
|
this.guest.rubySysinit(c_args);
|
|
829
829
|
this.guest.rubyOptions(c_args);
|
|
830
|
+
this.eval(`require "/bundle/setup"`);
|
|
830
831
|
}
|
|
831
832
|
/**
|
|
832
833
|
* Set a given instance to interact JavaScript and Ruby's
|
|
@@ -1061,13 +1062,8 @@
|
|
|
1061
1062
|
*/
|
|
1062
1063
|
evalAsync(code) {
|
|
1063
1064
|
const JS = this.eval("require 'js'; JS");
|
|
1064
|
-
return
|
|
1065
|
-
JS.call("__eval_async_rb", this.wrap(code),
|
|
1066
|
-
resolve,
|
|
1067
|
-
reject: (error) => {
|
|
1068
|
-
reject(new RbError(this.exceptionFormatter.format(error, this, this.privateObject())));
|
|
1069
|
-
},
|
|
1070
|
-
}));
|
|
1065
|
+
return newRbPromise(this, this.privateObject(), (future) => {
|
|
1066
|
+
JS.call("__eval_async_rb", this.wrap(code), future);
|
|
1071
1067
|
});
|
|
1072
1068
|
}
|
|
1073
1069
|
/**
|
|
@@ -1152,17 +1148,42 @@
|
|
|
1152
1148
|
*
|
|
1153
1149
|
* @param callee name of the Ruby method to call
|
|
1154
1150
|
* @param args arguments to pass to the method. Must be an array of RbValue
|
|
1151
|
+
* @returns The result of the method call as a new RbValue.
|
|
1155
1152
|
*
|
|
1156
1153
|
* @example
|
|
1157
1154
|
* const ary = vm.eval("[1, 2, 3]");
|
|
1158
1155
|
* ary.call("push", 4);
|
|
1159
1156
|
* console.log(ary.call("sample").toString());
|
|
1160
|
-
*
|
|
1161
1157
|
*/
|
|
1162
1158
|
call(callee, ...args) {
|
|
1163
1159
|
const innerArgs = args.map((arg) => arg.inner);
|
|
1164
1160
|
return new RbValue(callRbMethod(this.vm, this.privateObject, this.inner, callee, innerArgs), this.vm, this.privateObject);
|
|
1165
1161
|
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Call a given method that may call `JS::Object#await` with given arguments
|
|
1164
|
+
*
|
|
1165
|
+
* @param callee name of the Ruby method to call
|
|
1166
|
+
* @param args arguments to pass to the method. Must be an array of RbValue
|
|
1167
|
+
* @returns A Promise that resolves to the result of the method call as a new RbValue.
|
|
1168
|
+
*
|
|
1169
|
+
* @example
|
|
1170
|
+
* const client = vm.eval(`
|
|
1171
|
+
* require 'js'
|
|
1172
|
+
* class HttpClient
|
|
1173
|
+
* def get(url)
|
|
1174
|
+
* JS.global.fetch(url).await
|
|
1175
|
+
* end
|
|
1176
|
+
* end
|
|
1177
|
+
* HttpClient.new
|
|
1178
|
+
* `);
|
|
1179
|
+
* const response = await client.callAsync("get", vm.eval(`"https://example.com"`));
|
|
1180
|
+
*/
|
|
1181
|
+
callAsync(callee, ...args) {
|
|
1182
|
+
const JS = this.vm.eval("require 'js'; JS");
|
|
1183
|
+
return newRbPromise(this.vm, this.privateObject, (future) => {
|
|
1184
|
+
JS.call("__call_async_method", this, this.vm.wrap(callee), future, ...args);
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1166
1187
|
/**
|
|
1167
1188
|
* @see {@link https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive}
|
|
1168
1189
|
* @param hint Preferred type of the result primitive value. `"number"`, `"string"`, or `"default"`.
|
|
@@ -1361,6 +1382,18 @@
|
|
|
1361
1382
|
return new RbValue(value, vm, privateObject);
|
|
1362
1383
|
});
|
|
1363
1384
|
};
|
|
1385
|
+
function newRbPromise(vm, privateObject, body) {
|
|
1386
|
+
return new Promise((resolve, reject) => {
|
|
1387
|
+
const future = vm.wrap({
|
|
1388
|
+
resolve,
|
|
1389
|
+
reject: (error) => {
|
|
1390
|
+
const rbError = new RbError(privateObject.exceptionFormatter.format(error, vm, privateObject));
|
|
1391
|
+
reject(rbError);
|
|
1392
|
+
},
|
|
1393
|
+
});
|
|
1394
|
+
body(future);
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1364
1397
|
/**
|
|
1365
1398
|
* Error class thrown by Ruby execution
|
|
1366
1399
|
*/
|