1 | | | import 'dart:js_interop'; |
2 | | |
|
3 | | | import 'package:using/using.dart'; |
4 | | | import 'package:web/web.dart' as web; |
5 | | |
|
6 | | | import '../../exceptions/squadron_error.dart'; |
7 | | |
|
8 | | | class EntryPointUri with Releasable { |
9 | | 11 | EntryPointUri._(this.uri, {required bool revoke}) : _revoke = revoke; |
10 | | |
|
11 | | | final String uri; |
12 | | | final bool _revoke; |
13 | | |
|
14 | | | @override |
15 | | 1 | void release() { |
16 | | 11 | if (_revoke) { |
17 | | 11 | web.URL.revokeObjectURL(uri); |
18 | | | } |
19 | | 11 | super.release(); |
20 | | 1 | } |
21 | | |
|
22 | | 11 | factory EntryPointUri.from(Uri workerEntrypoint) { |
23 | | | final fileName = |
24 | | 11 | workerEntrypoint.pathSegments.lastOrNull?.toString().toLowerCase() ?? |
25 | | 0 | ''; |
26 | | |
|
27 | | 11 | if (fileName.endsWith('.js') || fileName.endsWith('.mjs')) { |
28 | | | // a JavaScript worker |
29 | | 11 | return EntryPointUri._(workerEntrypoint.toString(), revoke: false); |
30 | | 11 | } else if (fileName.endsWith('.wasm')) { |
31 | | | // blob containing the JavaScript code to load and invoke the Web Assembly worker |
32 | | | final blob = web.Blob( |
33 | | 11 | [wasmLoaderScript(workerEntrypoint.toString()).toJS].toJS, |
34 | | | web.BlobPropertyBag(type: 'application/javascript'), |
35 | | | ); |
36 | | 11 | return EntryPointUri._(web.URL.createObjectURL(blob), revoke: true); |
37 | | 1 | } else if (workerEntrypoint.isScheme('data') || |
38 | | 0 | workerEntrypoint.isScheme('javascript')) { |
39 | | | // something else, eg. inline JavaScript |
40 | | 1 | return EntryPointUri._(workerEntrypoint.toString(), revoke: false); |
41 | | | } else { |
42 | | 0 | throw SquadronErrorExt.create('Invalid entry point URI'); |
43 | | | } |
44 | | 11 | } |
45 | | |
|
46 | | | static String wasmLoaderScript(String url) => '''(async function() { |
47 | | | const workerUri = new URL("${url.replaceAll('"', '\\"')}", self.location.origin).href; |
48 | | | try { |
49 | | | let dart2wasm_runtime; let moduleInstance; |
50 | | | const runtimeUri = workerUri.replaceAll('.unopt', '').replaceAll('.wasm', '.mjs'); |
51 | | | try { |
52 | | | const dartModule = WebAssembly.compileStreaming(fetch(workerUri)); |
53 | | | dart2wasm_runtime = await import(runtimeUri); |
54 | | | moduleInstance = await dart2wasm_runtime.instantiate(dartModule, {}); |
55 | | | } catch (exception) { |
56 | | | console.error(`Failed to fetch and instantiate wasm module \${workerUri}: \${exception}`); |
57 | | | console.error('See https://dart.dev/web/wasm for more information.'); |
58 | | | throw new Error(exception.message ?? 'Unknown error when instantiating worker module'); |
59 | | | } |
60 | | | try { |
61 | | | await dart2wasm_runtime.invoke(moduleInstance); |
62 | | | console.log(`Succesfully loaded and invoked \${workerUri}`); |
63 | | | } catch (exception) { |
64 | | | console.error(`Exception while invoking wasm module \${workerUri}: \${exception}`); |
65 | | | throw new Error(exception.message ?? 'Unknown error when invoking worker module'); |
66 | | | } |
67 | | | } catch (ex) { |
68 | | | const ts = (Date.now() - Date.UTC(2020, 1, 2)) * 1000; |
69 | | | postMessage([ts, null, ["\$sqdrn", `Failed to load Web Worker from \${workerUri}: \${ex}`, null], null, null]); |
70 | | | } |
71 | | | })()'''; |
72 | | | } |