A Flan struct is a JS object, and the memory model does not come along
lib/js.ml lowers the same checked Tast the other two backends take to one CommonJS file, by object mapping rather than linear memory: docs/DISCUSS.md item 5 settled that fork before this was written, and item 5's consequence is the whole shape of the file. Object mapping means the host's collector owns every value, so there is no (Ptr T), no free, no arena and no allocator, and a program that uses one is refused by name with a location rather than compiled badly. flan build --target=js leaves Build.executable through its own two lines, before anything that assumes a clang: there is no object to compile and no linker to run. --dev, --debug, --sanitize and --x86 are refused there rather than swallowed. Js.Unsupported exits 3 beside X86.Unsupported, so a sweep can count refused-by-name apart from did-not-compile. What runs end to end: integer and float arithmetic with the normalisation each width needs, let, if, while with break and continue, calls, function values, structs, fixed arrays, slices, unions, options, match, and println through the same structural printer the other backends walk. Value semantics is the trap the object mapping sets and the reason the header carries a section on it. A Flan struct and a fixed array copy on assignment and a JS object does not, so every site emit.ml memcpys emits a generated Point$copy here. Fable's JS backend faces the same question for F# structs and answers it the other way -- it inserts no clone, and its Rust backend does -- so the divergence is deliberate and the survey pins it.
This commit is contained in:
parent
8a430bb50c
commit
dfb02ee26a
15
bin/main.ml
15
bin/main.ml
@ -23,6 +23,14 @@ let with_errors path f =
|
||||
prerr_endline ("x86: " ^ m);
|
||||
ignore path;
|
||||
exit 3
|
||||
(* The JS dialect's refusal, and the same status for the same reason. It is
|
||||
a wider category than the x86 one — that backend is behind on a node, and
|
||||
this one is a dialect that deliberately does not carry the memory model —
|
||||
but a sweep counts them the same way: refused by name, not a failure. *)
|
||||
| Flan.Js.Unsupported m ->
|
||||
prerr_endline ("js: " ^ m);
|
||||
ignore path;
|
||||
exit 3
|
||||
(* A refusal with no location: a combination of flags this command does not
|
||||
offer, or a build step that failed. Every [failwith] this binary can reach
|
||||
is one of those, and a sentence is what a user can act on where an
|
||||
@ -518,14 +526,17 @@ let () =
|
||||
(* A web build is three files — the page, its JS and the module — and
|
||||
the page is the one named here: emcc derives the other two from it,
|
||||
and it is the one a browser opens. *)
|
||||
(* The JS dialect's output is one file and it is source, so it is
|
||||
named the way source is: node runs it by name. *)
|
||||
(match target with
|
||||
| Some t when Flan.Build.is_web t -> base ^ ".html"
|
||||
| Some t when Flan.Build.is_js t -> base ^ ".js"
|
||||
| Some t when String.starts_with ~prefix:"wasm32" t -> base ^ ".wasm"
|
||||
| _ -> base)
|
||||
| _ ->
|
||||
prerr_endline
|
||||
"usage: flan build <file.flan> [-o out] [--no-bounds-checks] \
|
||||
[--dev] [--debug] [--sanitize] [--target=wasm32-wasi|web]";
|
||||
[--dev] [--debug] [--sanitize] [--target=wasm32-wasi|web|js]";
|
||||
exit 2
|
||||
in
|
||||
with_errors path (fun () ->
|
||||
@ -646,7 +657,7 @@ let () =
|
||||
\ flan import-c <header.h> [package.flan...] [clang flags...]\n\
|
||||
\ flan generate-c <package-dir>\n\
|
||||
\ flan build <file.flan> [-o out] [--no-bounds-checks] [--dev] \
|
||||
[--debug] [--sanitize] [--x86] [--target=wasm32-wasi|web]\n\
|
||||
[--debug] [--sanitize] [--x86] [--target=wasm32-wasi|web|js]\n\
|
||||
\ flan run <file.flan> [args...]\n\
|
||||
\ flan reload <program.flan> <forms.flan> [-o out.so] [--x86]\n\
|
||||
\ flan dev <program.flan> [-s socket] [--x86]";
|
||||
|
||||
34
lib/build.ml
34
lib/build.ml
@ -221,6 +221,20 @@ let web_target opts =
|
||||
let wasi_target opts =
|
||||
match opts.target with Some t when is_wasi t -> true | _ -> false
|
||||
|
||||
(* ── The fourth target, and the only one with no clang in it ──────────
|
||||
|
||||
[--target=js] is a *dialect*, not a machine: docs/DISCUSS.md item 5 settled
|
||||
object mapping over linear memory, and object mapping leaves the memory
|
||||
model behind. See [lib/js.ml]'s header for what maps to what and what is
|
||||
refused. Nothing below the fork in [executable] applies to it — there is no
|
||||
object to compile, no runtime C to link and no linker to run — so it leaves
|
||||
through its own two lines rather than threading a fourth case through
|
||||
[target_flags], [compiler] and [cflags]. *)
|
||||
let is_js t = t = "js" || t = "javascript" || t = "node"
|
||||
|
||||
let js_target opts =
|
||||
match opts.target with Some t when is_js t -> true | _ -> false
|
||||
|
||||
(* Which compiler a target is built by. This is not a flag difference: emcc is
|
||||
a different program with a different driver, and the object cache key below
|
||||
carries it for the same reason it carries clang's mtime. *)
|
||||
@ -687,6 +701,26 @@ let compile_c ~opts ?tflags ~src ~name () =
|
||||
it binds to. *)
|
||||
let executable ?(opts = default) ?(csrcs = []) ?(lflags = []) ?(pnames = [])
|
||||
(p : Tast.program) ~out =
|
||||
(* The JS dialect leaves here, before anything that assumes a clang. Its
|
||||
flags are refused rather than ignored, for the reason [flan emit] refuses
|
||||
[--target]: a flag that is silently swallowed is the shape the house rule
|
||||
exists to prevent. [--dev] is the one worth a sentence of its own — the
|
||||
dev loop on JS is a real possibility and deliberately not this lane. *)
|
||||
if js_target opts then begin
|
||||
if opts.dev then
|
||||
failwith
|
||||
"js: --dev is not built yet — evaluating new code is the one thing \
|
||||
JavaScript makes easy, so this is a lane and not a limit";
|
||||
if opts.debug then
|
||||
failwith "js: --debug is native only — there is no DWARF in a .js file";
|
||||
if opts.sanitize then
|
||||
failwith "js: --sanitize is native only";
|
||||
if opts.x86 then
|
||||
failwith "js: --x86 and --target=js are two different backends";
|
||||
write out (Js.program ~checks:opts.checks p);
|
||||
out
|
||||
end
|
||||
else
|
||||
(* A dev build is the REPL's, and the REPL reaches a running process through
|
||||
[-rdynamic] and [dlopen]. Neither exists on wasm32, so the combination is
|
||||
refused rather than quietly producing a module nothing can attach to. *)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user