@live-codes/ocaml 0.1.0 → 0.2.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 +23 -16
- package/assets/compiler/compile_stdout.browser.js +18185 -18181
- package/assets/compiler/libs/base64.cma +0 -0
- package/assets/compiler/libs/base64.cmi +0 -0
- package/assets/compiler/libs/big_int_Z.cmi +0 -0
- package/assets/compiler/libs/digestif.cmi +0 -0
- package/assets/compiler/libs/dllcamlruntime_eventsbyt-x86_64-pc-linux-gnu-a100.so +0 -0
- package/assets/compiler/libs/dllcamlstrbyt-x86_64-pc-linux-gnu-a100.so +0 -0
- package/assets/compiler/libs/dlldigestif_c_stubs.so +0 -0
- package/assets/compiler/libs/dllgraphics_js_stubs.so +0 -0
- package/assets/compiler/libs/dllgraphics_stubs.so +0 -0
- package/assets/compiler/libs/dlljs_of_ocaml_compiler_dynlink_stubs.so +0 -0
- package/assets/compiler/libs/dlljs_of_ocaml_stubs.so +0 -0
- package/assets/compiler/libs/dlljsoo_runtime_stubs.so +0 -0
- package/assets/compiler/libs/dlllwt_unix_stubs.so +0 -0
- package/assets/compiler/libs/dllthreads-x86_64-pc-linux-gnu-a100.so +0 -0
- package/assets/compiler/libs/dllunixbyt-x86_64-pc-linux-gnu-a100.so +0 -0
- package/assets/compiler/libs/dllzarith.so +0 -0
- package/assets/compiler/libs/fmt.cma +0 -0
- package/assets/compiler/libs/fmt.cmi +0 -0
- package/assets/compiler/libs/graphics.cma +0 -0
- package/assets/compiler/libs/graphics.cmi +0 -0
- package/assets/compiler/libs/graphicsX11.cmi +0 -0
- package/assets/compiler/libs/index.txt +105 -0
- package/assets/compiler/libs/js_of_ocaml.cma +0 -0
- package/assets/compiler/libs/js_of_ocaml.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Abort.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__CSS.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Console.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Dom.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Dom_events.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Dom_html.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Dom_svg.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Effect_js.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__EventSource.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Fetch.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__File.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Form.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Geolocation.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Import.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__IntersectionObserver.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Intl.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Js.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Json.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Jstable.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Lib_version.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__MutationObserver.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Performance.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__PerformanceObserver.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Promise.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Regexp.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__ResizeObserver.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Sys_js.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Typed_array.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Url.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__WebGL.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__WebSockets.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__Worker.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml__XmlHttpRequest.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt.cma +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__Import.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__Lwt_file.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__Lwt_js.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__Lwt_js_events.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__Lwt_jsonp.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__Lwt_promise.cmi +0 -0
- package/assets/compiler/libs/js_of_ocaml_lwt__Lwt_xmlHttpRequest.cmi +0 -0
- package/assets/compiler/libs/jsonm.cma +0 -0
- package/assets/compiler/libs/jsonm.cmi +0 -0
- package/assets/compiler/libs/jsoo_runtime.cma +0 -0
- package/assets/compiler/libs/jsoo_runtime.cmi +0 -0
- package/assets/compiler/libs/jsoo_runtime__.cmi +0 -0
- package/assets/compiler/libs/jsoo_runtime__Runtime_version.cmi +0 -0
- package/assets/compiler/libs/logs.cma +0 -0
- package/assets/compiler/libs/logs.cmi +0 -0
- package/assets/compiler/libs/lwt.cma +0 -0
- package/assets/compiler/libs/lwt.cmi +0 -0
- package/assets/compiler/libs/lwt_condition.cmi +0 -0
- package/assets/compiler/libs/lwt_list.cmi +0 -0
- package/assets/compiler/libs/lwt_mutex.cmi +0 -0
- package/assets/compiler/libs/lwt_mvar.cmi +0 -0
- package/assets/compiler/libs/lwt_pool.cmi +0 -0
- package/assets/compiler/libs/lwt_pqueue.cmi +0 -0
- package/assets/compiler/libs/lwt_result.cmi +0 -0
- package/assets/compiler/libs/lwt_rte.cmi +0 -0
- package/assets/compiler/libs/lwt_seq.cmi +0 -0
- package/assets/compiler/libs/lwt_sequence.cmi +0 -0
- package/assets/compiler/libs/lwt_stream.cmi +0 -0
- package/assets/compiler/libs/lwt_switch.cmi +0 -0
- package/assets/compiler/libs/manifest.txt +132 -0
- package/assets/compiler/libs/q.cmi +0 -0
- package/assets/compiler/libs/re.cma +0 -0
- package/assets/compiler/libs/re.cmi +0 -0
- package/assets/compiler/libs/re__.cmi +0 -0
- package/assets/compiler/libs/re__Ast.cmi +0 -0
- package/assets/compiler/libs/re__Automata.cmi +0 -0
- package/assets/compiler/libs/re__Bit_vector.cmi +0 -0
- package/assets/compiler/libs/re__Category.cmi +0 -0
- package/assets/compiler/libs/re__Color_map.cmi +0 -0
- package/assets/compiler/libs/re__Compile.cmi +0 -0
- package/assets/compiler/libs/re__Core.cmi +0 -0
- package/assets/compiler/libs/re__Cset.cmi +0 -0
- package/assets/compiler/libs/re__Dense_map.cmi +0 -0
- package/assets/compiler/libs/re__Dyn.cmi +0 -0
- package/assets/compiler/libs/re__Emacs.cmi +0 -0
- package/assets/compiler/libs/re__Fmt.cmi +0 -0
- package/assets/compiler/libs/re__Glob.cmi +0 -0
- package/assets/compiler/libs/re__Group.cmi +0 -0
- package/assets/compiler/libs/re__Hash_set.cmi +0 -0
- package/assets/compiler/libs/re__Import.cmi +0 -0
- package/assets/compiler/libs/re__Mark_infos.cmi +0 -0
- package/assets/compiler/libs/re__Parse_buffer.cmi +0 -0
- package/assets/compiler/libs/re__Pcre.cmi +0 -0
- package/assets/compiler/libs/re__Perl.cmi +0 -0
- package/assets/compiler/libs/re__Pmark.cmi +0 -0
- package/assets/compiler/libs/re__Posix.cmi +0 -0
- package/assets/compiler/libs/re__Posix_class.cmi +0 -0
- package/assets/compiler/libs/re__Replace.cmi +0 -0
- package/assets/compiler/libs/re__Search.cmi +0 -0
- package/assets/compiler/libs/re__Slice.cmi +0 -0
- package/assets/compiler/libs/re__Str.cmi +0 -0
- package/assets/compiler/libs/re__View.cmi +0 -0
- package/assets/compiler/libs/str.cma +0 -0
- package/assets/compiler/libs/str.cmi +0 -0
- package/assets/compiler/libs/stubs.txt +12 -0
- package/assets/compiler/libs/unix.cma +0 -0
- package/assets/compiler/libs/unix.cmi +0 -0
- package/assets/compiler/libs/unixLabels.cmi +0 -0
- package/assets/compiler/libs/uutf.cma +0 -0
- package/assets/compiler/libs/uutf.cmi +0 -0
- package/assets/compiler/libs/xmlm.cma +0 -0
- package/assets/compiler/libs/xmlm.cmi +0 -0
- package/assets/compiler/libs/z.cmi +0 -0
- package/assets/compiler/libs/zarith.cma +0 -0
- package/assets/compiler/libs/zarith_stubs_js.runtime.js +1246 -0
- package/assets/compiler/worker.js +38 -2
- package/assets/interpreter/libs/base64.cma +0 -0
- package/assets/interpreter/libs/base64.cmi +0 -0
- package/assets/interpreter/libs/big_int_Z.cmi +0 -0
- package/assets/interpreter/libs/fmt.cma +0 -0
- package/assets/interpreter/libs/fmt.cmi +0 -0
- package/assets/interpreter/libs/graphics.cma +0 -0
- package/assets/interpreter/libs/graphics.cmi +0 -0
- package/assets/interpreter/libs/graphicsX11.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml.cma +0 -0
- package/assets/interpreter/libs/js_of_ocaml.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Abort.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__CSS.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Console.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Dom.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Dom_events.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Dom_html.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Dom_svg.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Effect_js.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__EventSource.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Fetch.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__File.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Form.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Geolocation.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Import.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__IntersectionObserver.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Intl.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Js.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Json.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Jstable.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Lib_version.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__MutationObserver.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Performance.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__PerformanceObserver.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Promise.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Regexp.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__ResizeObserver.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Sys_js.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Typed_array.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Url.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__WebGL.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__WebSockets.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__Worker.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml__XmlHttpRequest.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt.cma +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__Import.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__Lwt_file.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__Lwt_js.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__Lwt_js_events.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__Lwt_jsonp.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__Lwt_promise.cmi +0 -0
- package/assets/interpreter/libs/js_of_ocaml_lwt__Lwt_xmlHttpRequest.cmi +0 -0
- package/assets/interpreter/libs/jsonm.cma +0 -0
- package/assets/interpreter/libs/jsonm.cmi +0 -0
- package/assets/interpreter/libs/jsoo_runtime.cma +0 -0
- package/assets/interpreter/libs/logs.cma +0 -0
- package/assets/interpreter/libs/logs.cmi +0 -0
- package/assets/interpreter/libs/lwt.cma +0 -0
- package/assets/interpreter/libs/lwt.cmi +0 -0
- package/assets/interpreter/libs/lwt_condition.cmi +0 -0
- package/assets/interpreter/libs/lwt_list.cmi +0 -0
- package/assets/interpreter/libs/lwt_mutex.cmi +0 -0
- package/assets/interpreter/libs/lwt_mvar.cmi +0 -0
- package/assets/interpreter/libs/lwt_pool.cmi +0 -0
- package/assets/interpreter/libs/lwt_pqueue.cmi +0 -0
- package/assets/interpreter/libs/lwt_result.cmi +0 -0
- package/assets/interpreter/libs/lwt_rte.cmi +0 -0
- package/assets/interpreter/libs/lwt_seq.cmi +0 -0
- package/assets/interpreter/libs/lwt_sequence.cmi +0 -0
- package/assets/interpreter/libs/lwt_stream.cmi +0 -0
- package/assets/interpreter/libs/lwt_switch.cmi +0 -0
- package/assets/interpreter/libs/manifest.txt +16 -0
- package/assets/interpreter/libs/q.cmi +0 -0
- package/assets/interpreter/libs/re.cma +0 -0
- package/assets/interpreter/libs/re.cmi +0 -0
- package/assets/interpreter/libs/re__.cmi +0 -0
- package/assets/interpreter/libs/re__Ast.cmi +0 -0
- package/assets/interpreter/libs/re__Automata.cmi +0 -0
- package/assets/interpreter/libs/re__Bit_vector.cmi +0 -0
- package/assets/interpreter/libs/re__Category.cmi +0 -0
- package/assets/interpreter/libs/re__Color_map.cmi +0 -0
- package/assets/interpreter/libs/re__Compile.cmi +0 -0
- package/assets/interpreter/libs/re__Core.cmi +0 -0
- package/assets/interpreter/libs/re__Cset.cmi +0 -0
- package/assets/interpreter/libs/re__Dense_map.cmi +0 -0
- package/assets/interpreter/libs/re__Dyn.cmi +0 -0
- package/assets/interpreter/libs/re__Emacs.cmi +0 -0
- package/assets/interpreter/libs/re__Fmt.cmi +0 -0
- package/assets/interpreter/libs/re__Glob.cmi +0 -0
- package/assets/interpreter/libs/re__Group.cmi +0 -0
- package/assets/interpreter/libs/re__Hash_set.cmi +0 -0
- package/assets/interpreter/libs/re__Import.cmi +0 -0
- package/assets/interpreter/libs/re__Mark_infos.cmi +0 -0
- package/assets/interpreter/libs/re__Parse_buffer.cmi +0 -0
- package/assets/interpreter/libs/re__Pcre.cmi +0 -0
- package/assets/interpreter/libs/re__Perl.cmi +0 -0
- package/assets/interpreter/libs/re__Pmark.cmi +0 -0
- package/assets/interpreter/libs/re__Posix.cmi +0 -0
- package/assets/interpreter/libs/re__Posix_class.cmi +0 -0
- package/assets/interpreter/libs/re__Replace.cmi +0 -0
- package/assets/interpreter/libs/re__Search.cmi +0 -0
- package/assets/interpreter/libs/re__Slice.cmi +0 -0
- package/assets/interpreter/libs/re__Str.cmi +0 -0
- package/assets/interpreter/libs/re__View.cmi +0 -0
- package/assets/interpreter/libs/str.cma +0 -0
- package/assets/interpreter/libs/str.cmi +0 -0
- package/assets/interpreter/libs/unix.cma +0 -0
- package/assets/interpreter/libs/unix.cmi +0 -0
- package/assets/interpreter/libs/unixLabels.cmi +0 -0
- package/assets/interpreter/libs/uutf.cma +0 -0
- package/assets/interpreter/libs/uutf.cmi +0 -0
- package/assets/interpreter/libs/xmlm.cma +0 -0
- package/assets/interpreter/libs/xmlm.cmi +0 -0
- package/assets/interpreter/libs/z.cmi +0 -0
- package/assets/interpreter/libs/zarith.cma +0 -0
- package/assets/interpreter/libs/zarith_stubs_js.cma +0 -0
- package/assets/interpreter/libs/zarith_stubs_js.cmi +0 -0
- package/assets/interpreter/worker.js +20246 -20050
- package/package.json +1 -1
- package/src/compiler/compile.js +185 -14
- package/src/compiler/worker.js +38 -2
package/package.json
CHANGED
package/src/compiler/compile.js
CHANGED
|
@@ -11,11 +11,24 @@
|
|
|
11
11
|
|
|
12
12
|
const lines = (text) => text.split('\n').filter((line) => line.trim() !== '');
|
|
13
13
|
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
14
|
+
// Three kinds of upstream noise reach the worker's stderr, and none says anything
|
|
15
|
+
// about the program being compiled:
|
|
16
|
+
//
|
|
17
|
+
// - js_of_ocaml is a command-line program and calls `exit` after writing its
|
|
18
|
+
// output -- and in a browser-target build `exit` is not implemented, so it
|
|
19
|
+
// raises. The output has already arrived by then.
|
|
20
|
+
// - js_of_ocaml's runtime tries to `importScripts` its own chunk files, which
|
|
21
|
+
// cannot work once the worker is created from a data URL -- the trick that lets
|
|
22
|
+
// the compiler run from a CDN, since a worker script must otherwise be
|
|
23
|
+
// same-origin. It falls back to the files it was handed and the bundle is
|
|
24
|
+
// correct. A genuinely fatal load leaves no output at all, and that is reported
|
|
25
|
+
// separately by `compile`.
|
|
26
|
+
// - js_of_ocaml warns when it cannot represent an integer exactly, and does so
|
|
27
|
+
// about its own generated runtime rather than about the program. It is filtered
|
|
28
|
+
// on request; the cost is that a program relying on such an integer will not see
|
|
29
|
+
// it either.
|
|
30
|
+
const PHANTOM =
|
|
31
|
+
/Function 'exit' not implemented|Failed to execute 'importScripts'|^Uncaught NetworkError:|Warning \[integer-overflow\]/;
|
|
19
32
|
const diagnosticsOf = (text) => lines(text).filter((line) => !PHANTOM.test(line));
|
|
20
33
|
|
|
21
34
|
// Byte-for-byte, and deliberately not TextDecoder: in browsers "latin1" and
|
|
@@ -38,9 +51,25 @@ function hexToBytes(hex) {
|
|
|
38
51
|
return bytes;
|
|
39
52
|
}
|
|
40
53
|
|
|
41
|
-
|
|
54
|
+
// A worker script has to be same-origin with whatever creates it: `new Worker()`
|
|
55
|
+
// refuses a cross-origin URL, and CORS does not help -- it is the script fetch the
|
|
56
|
+
// browser will not make. So the worker is created from a small data URL whose only
|
|
57
|
+
// job is to `importScripts` the harness, which *is* allowed across origins and then
|
|
58
|
+
// loads everything else itself.
|
|
59
|
+
//
|
|
60
|
+
// A data URL rather than a blob because the compiler may itself be running inside a
|
|
61
|
+
// worker, where `URL.createObjectURL` is not dependable. The URL is interpolated
|
|
62
|
+
// into JavaScript, so it is quoted with `JSON.stringify` and base64-encoded: the
|
|
63
|
+
// base64 form needs no escaping of its own.
|
|
64
|
+
const toDataUrl = (content, type = 'text/javascript') =>
|
|
65
|
+
`data:${type};charset=UTF-8;base64,` + btoa(content);
|
|
66
|
+
|
|
67
|
+
const workerDataUrlFor = (script) =>
|
|
68
|
+
toDataUrl(`importScripts(${JSON.stringify(String(script))});`);
|
|
69
|
+
|
|
70
|
+
async function runInWorker(baseUrl, script, files, env, argv, fs = [], libs) {
|
|
71
|
+
const worker = new Worker(workerDataUrlFor(new URL('worker.js', baseUrl).href));
|
|
42
72
|
return new Promise((resolve, reject) => {
|
|
43
|
-
const worker = new Worker(new URL('worker.js', baseUrl));
|
|
44
73
|
worker.onmessage = (event) => {
|
|
45
74
|
worker.terminate();
|
|
46
75
|
resolve(event.data);
|
|
@@ -49,7 +78,7 @@ function runInWorker(baseUrl, script, files, env, argv, fs = []) {
|
|
|
49
78
|
worker.terminate();
|
|
50
79
|
reject(new Error(event.message || 'the compiler worker failed to load'));
|
|
51
80
|
};
|
|
52
|
-
worker.postMessage({ script, files, env, argv, fs });
|
|
81
|
+
worker.postMessage({ script, files, env, argv, fs, libs });
|
|
53
82
|
});
|
|
54
83
|
}
|
|
55
84
|
|
|
@@ -94,8 +123,92 @@ async function loadRuntime(baseUrl) {
|
|
|
94
123
|
const STDLIB = 'stdlib.js';
|
|
95
124
|
const STDLIB_DIR = '/stdlib';
|
|
96
125
|
|
|
126
|
+
// The libraries a program names are fetched on demand rather than shipped up front.
|
|
127
|
+
// `libs/index.txt` says which files a module needs, and a compile that could not find
|
|
128
|
+
// a module is retried with those files added. Only what a program uses is fetched, and
|
|
129
|
+
// once fetched it stays, so the set converges as a session goes on.
|
|
130
|
+
const required = new Set();
|
|
131
|
+
let libraryIndex;
|
|
132
|
+
|
|
133
|
+
const loadLibraryIndex = async (baseUrl) => {
|
|
134
|
+
const byUnit = new Map();
|
|
135
|
+
try {
|
|
136
|
+
const text = await (await fetch(new URL('libs/index.txt', baseUrl))).text();
|
|
137
|
+
for (const line of lines(text)) {
|
|
138
|
+
const [unit, files] = line.split('\t');
|
|
139
|
+
if (unit && files) byUnit.set(unit.toLowerCase(), files.split(' ').filter(Boolean));
|
|
140
|
+
}
|
|
141
|
+
// The stub libraries are small and every link may need them, so they are always
|
|
142
|
+
// fetched rather than being part of a group.
|
|
143
|
+
const stubs = await (await fetch(new URL('libs/stubs.txt', baseUrl))).text();
|
|
144
|
+
for (const name of lines(stubs)) required.add(name.trim());
|
|
145
|
+
} catch {
|
|
146
|
+
// No index, so nothing can be fetched on demand: a program gets the standard
|
|
147
|
+
// library and whatever else it can link without one.
|
|
148
|
+
}
|
|
149
|
+
return byUnit;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// The two ways the compiler says a library is missing: a module it cannot find at all,
|
|
153
|
+
// and a unit with nothing to link against.
|
|
154
|
+
const missingUnits = (diagnostics) => {
|
|
155
|
+
const text = diagnostics.join('\n');
|
|
156
|
+
const names = new Set();
|
|
157
|
+
for (const [, name] of text.matchAll(/Unbound module ([A-Za-z0-9_']+)/g)) names.add(name);
|
|
158
|
+
for (const [, block] of text.matchAll(
|
|
159
|
+
/No implementation provided for the following modules:([\s\S]*?)(?:\n\s*\n|$)/g
|
|
160
|
+
)) {
|
|
161
|
+
for (const [, name] of block.matchAll(/([A-Za-z0-9_']+) referenced from/g)) names.add(name);
|
|
162
|
+
}
|
|
163
|
+
return [...names];
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const filesFor = (byUnit, names) => {
|
|
167
|
+
const files = new Set();
|
|
168
|
+
const known = [...byUnit.keys()];
|
|
169
|
+
for (const name of names) {
|
|
170
|
+
const key = name.toLowerCase();
|
|
171
|
+
const exact = byUnit.get(key);
|
|
172
|
+
if (exact) {
|
|
173
|
+
for (const file of exact) files.add(file);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// A submodule is named without its package: Dom_html is Js_of_ocaml's.
|
|
177
|
+
for (const unit of known.filter((u) => u.endsWith('__' + key))) {
|
|
178
|
+
for (const file of byUnit.get(unit)) files.add(file);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return [...files];
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Byte by byte, not through TextDecoder: these are binary files, and decoding them as
|
|
185
|
+
// UTF-8 would corrupt every byte above 127 into something else entirely.
|
|
186
|
+
const toByteString = (bytes) => {
|
|
187
|
+
const chunks = [];
|
|
188
|
+
for (let i = 0; i < bytes.length; i += 0x8000) {
|
|
189
|
+
chunks.push(String.fromCharCode(...bytes.subarray(i, i + 0x8000)));
|
|
190
|
+
}
|
|
191
|
+
return chunks.join('');
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// The library files are fetched here, in the page, rather than in the worker: a worker
|
|
195
|
+
// created from a data URL has an opaque origin, so a fetch from inside it to the server
|
|
196
|
+
// that served the page is refused. The page is same-origin, so it can.
|
|
197
|
+
const fetchLibraries = async (baseUrl, names) => {
|
|
198
|
+
const base = new URL('libs/', baseUrl);
|
|
199
|
+
return Promise.all(
|
|
200
|
+
names.map(async (name) => {
|
|
201
|
+
const bytes = new Uint8Array(await (await fetch(new URL(name, base))).arrayBuffer());
|
|
202
|
+
// The stub libraries are looked for at /static, from the stub directory of
|
|
203
|
+
// the build; everything else is read from the compiler's library directory.
|
|
204
|
+
const path = (name.startsWith('dll') ? '/static/' : '/stdlib/') + name;
|
|
205
|
+
return { path, content: toByteString(bytes) };
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
|
|
97
210
|
/**
|
|
98
|
-
* Compile OCaml source to JavaScript.
|
|
211
|
+
* Compile OCaml source to JavaScript, fetching whatever libraries it turns out to need.
|
|
99
212
|
*
|
|
100
213
|
* @param {string} source
|
|
101
214
|
* @param {object} options
|
|
@@ -105,19 +218,58 @@ const STDLIB_DIR = '/stdlib';
|
|
|
105
218
|
* @param {string} [options.stdlib] - the filesystem artifact carrying the standard library.
|
|
106
219
|
* @returns {Promise<{js: string|null, diagnostics: string[], error?: string}>}
|
|
107
220
|
*/
|
|
108
|
-
export async function compile(source,
|
|
221
|
+
export async function compile(source, options) {
|
|
222
|
+
libraryIndex ??= await loadLibraryIndex(options.baseUrl);
|
|
223
|
+
let libs = [...required];
|
|
224
|
+
for (let attempt = 0; attempt < 6; attempt++) {
|
|
225
|
+
const result = await compileOnce(source, { ...options, libs });
|
|
226
|
+
const added = filesFor(libraryIndex, missingUnits(result.diagnostics)).filter(
|
|
227
|
+
(name) => !required.has(name)
|
|
228
|
+
);
|
|
229
|
+
if (added.length === 0) return result;
|
|
230
|
+
for (const name of added) required.add(name);
|
|
231
|
+
libs = [...required];
|
|
232
|
+
}
|
|
233
|
+
// Six rounds and still missing something: run once more, so the caller sees the
|
|
234
|
+
// compiler's own error rather than a guess about it.
|
|
235
|
+
return compileOnce(source, { ...options, libs });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// zarith's primitives are C stubs, and `zarith_stubs_js` implements them in JavaScript as a
|
|
239
|
+
// js_of_ocaml runtime chunk -- `//Provides:` and `//Requires:` lines that have to be a *link
|
|
240
|
+
// input* to js_of_ocaml, not text pasted into the output. Only as an input does js_of_ocaml
|
|
241
|
+
// honour them and put each definition where its dependencies already exist; concatenated,
|
|
242
|
+
// they land outside its primitive table and the program fails at load with
|
|
243
|
+
// "f.ml_z_init is not a function".
|
|
244
|
+
const RUNTIME_CHUNK = 'zarith_stubs_js.runtime.js';
|
|
245
|
+
const CHUNK_AS_INPUT = 'zarith_stubs_js.js';
|
|
246
|
+
let runtimeChunk;
|
|
247
|
+
|
|
248
|
+
const loadRuntimeChunk = async (baseUrl) => {
|
|
249
|
+
runtimeChunk ??= await (await fetch(new URL('libs/' + RUNTIME_CHUNK, baseUrl))).text();
|
|
250
|
+
return runtimeChunk;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
async function compileOnce(
|
|
254
|
+
source,
|
|
255
|
+
{ baseUrl, driver = 'compile_stdout.browser.js', jsoo = 'jsoo.js', stdlib = STDLIB, libs = [] }
|
|
256
|
+
) {
|
|
109
257
|
if (typeof source !== 'string') throw new TypeError('compile: source must be a string');
|
|
110
258
|
const runtimeFiles = await loadRuntime(baseUrl);
|
|
111
259
|
const files = [{ name: 'main.ml', content: source }, ...runtimeFiles];
|
|
260
|
+
const libraryFiles = await fetchLibraries(baseUrl, libs);
|
|
112
261
|
|
|
113
|
-
// Source -> bytecode, as hex.
|
|
262
|
+
// Source -> bytecode, as hex. The library files go over with the source, one request
|
|
263
|
+
// each, from the browser's cache after the first compile. The bytecode ->
|
|
264
|
+
// JavaScript step needs none of them.
|
|
114
265
|
const front = await runInWorker(
|
|
115
266
|
baseUrl,
|
|
116
267
|
new URL(driver, baseUrl).href,
|
|
117
268
|
files,
|
|
118
269
|
{ OCAMLLIB: STDLIB_DIR },
|
|
119
270
|
['node', 'compile_stdout', 'main.ml'],
|
|
120
|
-
[new URL(stdlib, baseUrl).href]
|
|
271
|
+
[new URL(stdlib, baseUrl).href],
|
|
272
|
+
libraryFiles
|
|
121
273
|
);
|
|
122
274
|
const diagnostics = diagnosticsOf(front.err.join('\n'));
|
|
123
275
|
// The driver prints its hex with no newlines anywhere, so its chunks are
|
|
@@ -134,12 +286,31 @@ export async function compile(source, { baseUrl, driver = 'compile_stdout.browse
|
|
|
134
286
|
// `--pretty` is for the reader -- the emitted JavaScript is what a Compiled
|
|
135
287
|
// Code pane shows -- and it also keeps the output line-oriented, which the
|
|
136
288
|
// joining below depends on.
|
|
289
|
+
const stubs = required.has('zarith.cma') ? await loadRuntimeChunk(baseUrl) : null;
|
|
137
290
|
const back = await runInWorker(
|
|
138
291
|
baseUrl,
|
|
139
292
|
new URL(jsoo, baseUrl).href,
|
|
140
|
-
[
|
|
293
|
+
[
|
|
294
|
+
{ name: 'main.byte', content: bytesToString(hexToBytes(hex)) },
|
|
295
|
+
...runtimeFiles,
|
|
296
|
+
// The chunk is mounted as a file and named on the command line, which is what
|
|
297
|
+
// makes js_of_ocaml treat it as a link input rather than something to paste in.
|
|
298
|
+
...(stubs ? [{ name: CHUNK_AS_INPUT, content: stubs }] : [])
|
|
299
|
+
],
|
|
141
300
|
{},
|
|
142
|
-
[
|
|
301
|
+
[
|
|
302
|
+
'node',
|
|
303
|
+
'js_of_ocaml',
|
|
304
|
+
// The chunk comes before the bytecode: js_of_ocaml decides how to read its
|
|
305
|
+
// positional inputs from the first one, so a .js has to lead or the bytecode
|
|
306
|
+
// gets parsed as JavaScript.
|
|
307
|
+
...(stubs ? [CHUNK_AS_INPUT] : []),
|
|
308
|
+
'main.byte',
|
|
309
|
+
'-o',
|
|
310
|
+
'-',
|
|
311
|
+
'--target-env=browser',
|
|
312
|
+
'--pretty'
|
|
313
|
+
]
|
|
143
314
|
);
|
|
144
315
|
const backDiagnostics = diagnosticsOf(back.err.join('\n'));
|
|
145
316
|
// js_of_ocaml's stdout is line-buffered, so it hands over one console call
|
package/src/compiler/worker.js
CHANGED
|
@@ -8,8 +8,42 @@
|
|
|
8
8
|
// browser filesystem fd 1 and fd 2 are wired to console.log and console.error
|
|
9
9
|
// (runtime/js/fs_fake.js). A worker has no other way in: the output never
|
|
10
10
|
// reaches a file the page can read.
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// Bytes to a bytestring, one character per byte, chunked so a multi-megabyte archive
|
|
12
|
+
// does not overflow the argument list. Deliberately not TextDecoder: in browsers
|
|
13
|
+
// "latin1" and "iso-8859-1" are both aliases for windows-1252, which remaps
|
|
14
|
+
// 0x80-0x9F, and that is enough to make a .cmi or a .cma unreadable.
|
|
15
|
+
const bytesToString = (bytes) => {
|
|
16
|
+
let s = '';
|
|
17
|
+
const CHUNK = 0x8000;
|
|
18
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
19
|
+
s += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
|
|
20
|
+
}
|
|
21
|
+
return s;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// The library files this compile needs. The *page* fetches them, not this worker: a
|
|
25
|
+
// worker created from a data URL has an opaque origin, so a fetch from here to the
|
|
26
|
+
// server that served the page is refused. They arrive with the path each belongs at,
|
|
27
|
+
// which is the same shape a filesystem artifact uses.
|
|
28
|
+
const mountLibraries = (files) => {
|
|
29
|
+
const archives = [];
|
|
30
|
+
for (const { path, content } of files) {
|
|
31
|
+
globalThis.jsoo_fs_tmp.push({ name: path, content });
|
|
32
|
+
if (path.endsWith('.cma')) archives.push(path.slice(path.lastIndexOf('/') + 1));
|
|
33
|
+
}
|
|
34
|
+
// What the linker links, and the order matters: a library has to come before the
|
|
35
|
+
// archive that references it. stdlib first, because that is where a program's
|
|
36
|
+
// runtime starts, then js_of_ocaml's runtime, then the rest.
|
|
37
|
+
const rank = (name) => (name === 'stdlib.cma' ? 0 : name === 'jsoo_runtime.cma' ? 1 : 2);
|
|
38
|
+
archives.sort((a, b) => rank(a) - rank(b) || (a < b ? -1 : a > b ? 1 : 0));
|
|
39
|
+
globalThis.jsoo_fs_tmp.push({
|
|
40
|
+
name: '/stdlib/cmas.txt',
|
|
41
|
+
content: ['stdlib.cma', ...archives].join('\n') + '\n',
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
self.onmessage = async (event) => {
|
|
46
|
+
const { script, files, env, argv, fs = [], libs = [] } = event.data;
|
|
13
47
|
const out = [];
|
|
14
48
|
const err = [];
|
|
15
49
|
const realLog = console.log;
|
|
@@ -30,6 +64,8 @@ self.onmessage = (event) => {
|
|
|
30
64
|
// take the other branch and register the files directly, so the order is
|
|
31
65
|
// not load-bearing, but this is the cheaper one.
|
|
32
66
|
for (const url of fs) importScripts(url);
|
|
67
|
+
// Then the libraries, which have to be in place before the program starts.
|
|
68
|
+
if (libs.length > 0) mountLibraries(libs);
|
|
33
69
|
importScripts(script);
|
|
34
70
|
// The chunks go back as they arrived, not joined: the caller knows how to
|
|
35
71
|
// put them together, and joining here would lose the newlines between
|