|
1 |
| -import { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js"; |
| 1 | +import type { RubyJsJsRuntime } from "./bindgen/interfaces/ruby-js-js-runtime.js"; |
| 2 | +import type { RubyJsRubyRuntime } from "./bindgen/interfaces/ruby-js-ruby-runtime.js"; |
2 | 3 | import * as RbAbi from "./bindgen/legacy/rb-abi-guest.js";
|
3 | 4 | import {
|
4 | 5 | RbJsAbiHost,
|
@@ -83,9 +84,18 @@ export class RubyVM {
|
83 | 84 | this.exceptionFormatter = new RbExceptionFormatter();
|
84 | 85 | }
|
85 | 86 |
|
86 |
| - static _instantiate(component: typeof RubyJsRubyRuntime): RubyVM { |
87 |
| - const binding = new ComponentBinding(component) |
88 |
| - return new RubyVM(binding); |
| 87 | + static async _instantiate(initComponent: (_: typeof RubyJsJsRuntime) => Promise<typeof RubyJsRubyRuntime>): Promise<RubyVM> { |
| 88 | + const binding = new ComponentBinding() |
| 89 | + const vm = new RubyVM(binding); |
| 90 | + const component = await initComponent({ |
| 91 | + ...vm.getImports(), |
| 92 | + throwProhibitRewindException: (message: string) => { |
| 93 | + throw new RbFatalError(message); |
| 94 | + }, |
| 95 | + JsAbiValue: Object, |
| 96 | + }); |
| 97 | + binding.setUnderlying(component); |
| 98 | + return vm |
89 | 99 | }
|
90 | 100 |
|
91 | 101 | /**
|
@@ -122,20 +132,6 @@ export class RubyVM {
|
122 | 132 | */
|
123 | 133 | addToImports(imports: WebAssembly.Imports) {
|
124 | 134 | this.guest.addToImports(imports);
|
125 |
| - function wrapTry(f: (...args: any[]) => JsAbiValue): () => JsAbiResult { |
126 |
| - return (...args) => { |
127 |
| - try { |
128 |
| - return { tag: "success", val: f(...args) }; |
129 |
| - } catch (e) { |
130 |
| - if (e instanceof RbFatalError) { |
131 |
| - // RbFatalError should not be caught by Ruby because it Ruby VM |
132 |
| - // can be already in an inconsistent state. |
133 |
| - throw e; |
134 |
| - } |
135 |
| - return { tag: "failure", val: e }; |
136 |
| - } |
137 |
| - }; |
138 |
| - } |
139 | 135 | imports["rb-js-abi-host"] = {
|
140 | 136 | rb_wasm_throw_prohibit_rewind_exception: (
|
141 | 137 | messagePtr: number,
|
@@ -185,132 +181,150 @@ export class RubyVM {
|
185 | 181 |
|
186 | 182 | addRbJsAbiHostToImports(
|
187 | 183 | imports,
|
188 |
| - proxyImports({ |
189 |
| - evalJs: wrapTry((code) => { |
190 |
| - return Function(code)(); |
191 |
| - }), |
192 |
| - isJs: (value) => { |
193 |
| - // Just for compatibility with the old JS API |
194 |
| - return true; |
195 |
| - }, |
196 |
| - globalThis: () => { |
197 |
| - if (typeof globalThis !== "undefined") { |
198 |
| - return globalThis; |
199 |
| - } else if (typeof global !== "undefined") { |
200 |
| - return global; |
201 |
| - } else if (typeof window !== "undefined") { |
202 |
| - return window; |
203 |
| - } |
204 |
| - throw new Error("unable to locate global object"); |
205 |
| - }, |
206 |
| - intToJsNumber: (value) => { |
207 |
| - return value; |
208 |
| - }, |
209 |
| - floatToJsNumber: (value) => { |
210 |
| - return value; |
211 |
| - }, |
212 |
| - stringToJsString: (value) => { |
213 |
| - return value; |
214 |
| - }, |
215 |
| - boolToJsBool: (value) => { |
216 |
| - return value; |
217 |
| - }, |
218 |
| - procToJsFunction: (rawRbAbiValue) => { |
219 |
| - const rbValue = this.rbValueOfPointer(rawRbAbiValue); |
220 |
| - return (...args) => { |
221 |
| - return rbValue.call("call", ...args.map((arg) => this.wrap(arg))).toJS(); |
222 |
| - }; |
223 |
| - }, |
224 |
| - rbObjectToJsRbValue: (rawRbAbiValue) => { |
225 |
| - return this.rbValueOfPointer(rawRbAbiValue); |
226 |
| - }, |
227 |
| - jsValueToString: (value) => { |
228 |
| - // According to the [spec](https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value) |
229 |
| - // `String(value)` always returns a string. |
230 |
| - return String(value); |
231 |
| - }, |
232 |
| - jsValueToInteger(value) { |
233 |
| - if (typeof value === "number") { |
234 |
| - return { tag: "as-float", val: value }; |
235 |
| - } else if (typeof value === "bigint") { |
236 |
| - return { tag: "bignum", val: BigInt(value).toString(10) + "\0" }; |
237 |
| - } else if (typeof value === "string") { |
238 |
| - return { tag: "bignum", val: value + "\0" }; |
239 |
| - } else if (typeof value === "undefined") { |
240 |
| - return { tag: "as-float", val: 0 }; |
241 |
| - } else { |
242 |
| - return { tag: "as-float", val: Number(value) }; |
243 |
| - } |
244 |
| - }, |
245 |
| - exportJsValueToHost: (value) => { |
246 |
| - // See `JsValueExporter` for the reason why we need to do this |
247 |
| - this.transport.takeJsValue(value); |
248 |
| - }, |
249 |
| - importJsValueFromHost: () => { |
250 |
| - return this.transport.consumeJsValue(); |
251 |
| - }, |
252 |
| - instanceOf: (value, klass) => { |
253 |
| - if (typeof klass === "function") { |
254 |
| - return value instanceof klass; |
255 |
| - } else { |
256 |
| - return false; |
257 |
| - } |
258 |
| - }, |
259 |
| - jsValueTypeof(value) { |
260 |
| - return typeof value; |
261 |
| - }, |
262 |
| - jsValueEqual(lhs, rhs) { |
263 |
| - return lhs == rhs; |
264 |
| - }, |
265 |
| - jsValueStrictlyEqual(lhs, rhs) { |
266 |
| - return lhs === rhs; |
267 |
| - }, |
268 |
| - reflectApply: wrapTry((target, thisArgument, args) => { |
269 |
| - return Reflect.apply(target as any, thisArgument, args); |
270 |
| - }), |
271 |
| - reflectConstruct: function (target, args) { |
272 |
| - throw new Error("Function not implemented."); |
273 |
| - }, |
274 |
| - reflectDeleteProperty: function (target, propertyKey): boolean { |
275 |
| - throw new Error("Function not implemented."); |
276 |
| - }, |
277 |
| - reflectGet: wrapTry((target, propertyKey) => { |
278 |
| - return target[propertyKey]; |
279 |
| - }), |
280 |
| - reflectGetOwnPropertyDescriptor: function ( |
281 |
| - target, |
282 |
| - propertyKey: string, |
283 |
| - ) { |
284 |
| - throw new Error("Function not implemented."); |
285 |
| - }, |
286 |
| - reflectGetPrototypeOf: function (target) { |
287 |
| - throw new Error("Function not implemented."); |
288 |
| - }, |
289 |
| - reflectHas: function (target, propertyKey): boolean { |
290 |
| - throw new Error("Function not implemented."); |
291 |
| - }, |
292 |
| - reflectIsExtensible: function (target): boolean { |
293 |
| - throw new Error("Function not implemented."); |
294 |
| - }, |
295 |
| - reflectOwnKeys: function (target) { |
296 |
| - throw new Error("Function not implemented."); |
297 |
| - }, |
298 |
| - reflectPreventExtensions: function (target): boolean { |
299 |
| - throw new Error("Function not implemented."); |
300 |
| - }, |
301 |
| - reflectSet: wrapTry((target, propertyKey, value) => { |
302 |
| - return Reflect.set(target, propertyKey, value); |
303 |
| - }), |
304 |
| - reflectSetPrototypeOf: function (target, prototype): boolean { |
305 |
| - throw new Error("Function not implemented."); |
306 |
| - }, |
307 |
| - }), |
| 184 | + proxyImports(this.getImports()), |
308 | 185 | (name) => {
|
309 | 186 | return this.instance.exports[name];
|
310 | 187 | },
|
311 | 188 | );
|
312 | 189 | }
|
313 | 190 |
|
| 191 | + private getImports(): RbJsAbiHost { |
| 192 | + function wrapTry(f: (...args: any[]) => JsAbiValue): () => JsAbiResult { |
| 193 | + return (...args) => { |
| 194 | + try { |
| 195 | + return { tag: "success", val: f(...args) }; |
| 196 | + } catch (e) { |
| 197 | + if (e instanceof RbFatalError) { |
| 198 | + // RbFatalError should not be caught by Ruby because it Ruby VM |
| 199 | + // can be already in an inconsistent state. |
| 200 | + throw e; |
| 201 | + } |
| 202 | + return { tag: "failure", val: e }; |
| 203 | + } |
| 204 | + }; |
| 205 | + } |
| 206 | + return { |
| 207 | + evalJs: wrapTry((code) => { |
| 208 | + return Function(code)(); |
| 209 | + }), |
| 210 | + isJs: (value) => { |
| 211 | + // Just for compatibility with the old JS API |
| 212 | + return true; |
| 213 | + }, |
| 214 | + globalThis: () => { |
| 215 | + if (typeof globalThis !== "undefined") { |
| 216 | + return globalThis; |
| 217 | + } else if (typeof global !== "undefined") { |
| 218 | + return global; |
| 219 | + } else if (typeof window !== "undefined") { |
| 220 | + return window; |
| 221 | + } |
| 222 | + throw new Error("unable to locate global object"); |
| 223 | + }, |
| 224 | + intToJsNumber: (value) => { |
| 225 | + return value; |
| 226 | + }, |
| 227 | + floatToJsNumber: (value) => { |
| 228 | + return value; |
| 229 | + }, |
| 230 | + stringToJsString: (value) => { |
| 231 | + return value; |
| 232 | + }, |
| 233 | + boolToJsBool: (value) => { |
| 234 | + return value; |
| 235 | + }, |
| 236 | + procToJsFunction: (rawRbAbiValue) => { |
| 237 | + const rbValue = this.rbValueOfPointer(rawRbAbiValue); |
| 238 | + return (...args) => { |
| 239 | + return rbValue.call("call", ...args.map((arg) => this.wrap(arg))).toJS(); |
| 240 | + }; |
| 241 | + }, |
| 242 | + rbObjectToJsRbValue: (rawRbAbiValue) => { |
| 243 | + return this.rbValueOfPointer(rawRbAbiValue); |
| 244 | + }, |
| 245 | + jsValueToString: (value) => { |
| 246 | + // According to the [spec](https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor-string-value) |
| 247 | + // `String(value)` always returns a string. |
| 248 | + return String(value); |
| 249 | + }, |
| 250 | + jsValueToInteger(value) { |
| 251 | + if (typeof value === "number") { |
| 252 | + return { tag: "as-float", val: value }; |
| 253 | + } else if (typeof value === "bigint") { |
| 254 | + return { tag: "bignum", val: BigInt(value).toString(10) + "\0" }; |
| 255 | + } else if (typeof value === "string") { |
| 256 | + return { tag: "bignum", val: value + "\0" }; |
| 257 | + } else if (typeof value === "undefined") { |
| 258 | + return { tag: "as-float", val: 0 }; |
| 259 | + } else { |
| 260 | + return { tag: "as-float", val: Number(value) }; |
| 261 | + } |
| 262 | + }, |
| 263 | + exportJsValueToHost: (value) => { |
| 264 | + // See `JsValueExporter` for the reason why we need to do this |
| 265 | + this.transport.takeJsValue(value); |
| 266 | + }, |
| 267 | + importJsValueFromHost: () => { |
| 268 | + return this.transport.consumeJsValue(); |
| 269 | + }, |
| 270 | + instanceOf: (value, klass) => { |
| 271 | + if (typeof klass === "function") { |
| 272 | + return value instanceof klass; |
| 273 | + } else { |
| 274 | + return false; |
| 275 | + } |
| 276 | + }, |
| 277 | + jsValueTypeof(value) { |
| 278 | + return typeof value; |
| 279 | + }, |
| 280 | + jsValueEqual(lhs, rhs) { |
| 281 | + return lhs == rhs; |
| 282 | + }, |
| 283 | + jsValueStrictlyEqual(lhs, rhs) { |
| 284 | + return lhs === rhs; |
| 285 | + }, |
| 286 | + reflectApply: wrapTry((target, thisArgument, args) => { |
| 287 | + return Reflect.apply(target as any, thisArgument, args); |
| 288 | + }), |
| 289 | + reflectConstruct: function (target, args) { |
| 290 | + throw new Error("Function not implemented."); |
| 291 | + }, |
| 292 | + reflectDeleteProperty: function (target, propertyKey): boolean { |
| 293 | + throw new Error("Function not implemented."); |
| 294 | + }, |
| 295 | + reflectGet: wrapTry((target, propertyKey) => { |
| 296 | + return target[propertyKey]; |
| 297 | + }), |
| 298 | + reflectGetOwnPropertyDescriptor: function ( |
| 299 | + target, |
| 300 | + propertyKey: string, |
| 301 | + ) { |
| 302 | + throw new Error("Function not implemented."); |
| 303 | + }, |
| 304 | + reflectGetPrototypeOf: function (target) { |
| 305 | + throw new Error("Function not implemented."); |
| 306 | + }, |
| 307 | + reflectHas: function (target, propertyKey): boolean { |
| 308 | + throw new Error("Function not implemented."); |
| 309 | + }, |
| 310 | + reflectIsExtensible: function (target): boolean { |
| 311 | + throw new Error("Function not implemented."); |
| 312 | + }, |
| 313 | + reflectOwnKeys: function (target) { |
| 314 | + throw new Error("Function not implemented."); |
| 315 | + }, |
| 316 | + reflectPreventExtensions: function (target): boolean { |
| 317 | + throw new Error("Function not implemented."); |
| 318 | + }, |
| 319 | + reflectSet: wrapTry((target, propertyKey, value) => { |
| 320 | + return Reflect.set(target, propertyKey, value); |
| 321 | + }), |
| 322 | + reflectSetPrototypeOf: function (target, prototype): boolean { |
| 323 | + throw new Error("Function not implemented."); |
| 324 | + }, |
| 325 | + } |
| 326 | + } |
| 327 | + |
314 | 328 | /**
|
315 | 329 | * Print the Ruby version to stdout
|
316 | 330 | */
|
|
0 commit comments