diff --git a/.github/workflows/build-wasm_of_ocaml.yml b/.github/workflows/build-wasm_of_ocaml.yml index bffb5bf824..28bebcb26a 100644 --- a/.github/workflows/build-wasm_of_ocaml.yml +++ b/.github/workflows/build-wasm_of_ocaml.yml @@ -75,7 +75,7 @@ jobs: - name: Set-up Node.js uses: actions/setup-node@v4 with: - node-version: latest + node-version: 'v24.0.0-v8-canary202412116884e26428' - name: Set-up OCaml ${{ matrix.ocaml-compiler }} uses: ocaml/setup-ocaml@v3 diff --git a/benchmarks/Makefile b/benchmarks/Makefile index 3d12c8b96a..7e0ef4214e 100644 --- a/benchmarks/Makefile +++ b/benchmarks/Makefile @@ -21,8 +21,8 @@ bench: $(MAKE) -C benchmark-fiat-crypto bench $(MAKE) -C benchmark-ocamlc bench $(MAKE) -C benchmark-partial-render-table bench - $(MAKE) -C benchmark-camlboy bench - $(MAKE) -C benchmark-others bench + #$(MAKE) -C benchmark-camlboy bench + $(MAKE) -C benchmark-others bench # To try later! microbenchmarks: @date -u +"%FT%TZ - Microbenchmarks: starting" diff --git a/benchmarks/benchmark-fiat-crypto/Makefile b/benchmarks/benchmark-fiat-crypto/Makefile index cfff448c45..02dc6b9111 100644 --- a/benchmarks/benchmark-fiat-crypto/Makefile +++ b/benchmarks/benchmark-fiat-crypto/Makefile @@ -2,7 +2,7 @@ export NAME=Fiat-Crypto -SHELL=/bin/bash -o pipefail +SHELL=/usr/bin/env bash -o pipefail bench: @date -u +"%FT%TZ - $(NAME): starting" @@ -11,7 +11,7 @@ bench: @date -u +"%FT%TZ - $(NAME): done" perform: bedrock2_fiat_crypto.byte - /usr/bin/time -f "%E %R" $(COMPILER) --debug times --source-map $(EXTRA_ARGS) $< -o out.js 2>&1 | \ + env time -f "%E %R" $(COMPILER) --debug times --source-map $(EXTRA_ARGS) $< -o out.js 2>&1 | \ ocaml -I +str str.cma ../utils/compilation_metrics.ml $(COMPILER) $(NAME) out.js | \ sh ../utils/aggregate.sh $(KIND) diff --git a/benchmarks/benchmark-ocamlc/Makefile b/benchmarks/benchmark-ocamlc/Makefile index be6f814711..f6b8915c35 100644 --- a/benchmarks/benchmark-ocamlc/Makefile +++ b/benchmarks/benchmark-ocamlc/Makefile @@ -2,7 +2,7 @@ export NAME=Ocamlc -SHELL=/bin/bash -o pipefail +SHELL=/usr/bin/env bash -o pipefail bench: @date -u +"%FT%TZ - $(NAME): starting" @@ -14,9 +14,9 @@ bench: ARGS=ml/*.ml ml/*.ml ml/*.ml ml/*.ml ml/*.ml ml/*.ml ml/*.ml ml/*.ml perform: - /usr/bin/time -f "%E %R" $(COMPILER) --debug times --opt 2 --pretty `which ocamlc.byte` -o $(SCRIPT) 2>&1 | \ + env time -f "%E %R" $(COMPILER) --debug times --opt 2 --pretty `which ocamlc.byte` -o $(SCRIPT) 2>&1 | \ ocaml -I +str str.cma ../utils/compilation_metrics.ml $(COMPILER) $(NAME) $(SCRIPT) | \ sh ../utils/aggregate.sh $(KIND) - /usr/bin/time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) -c $(ARGS) 2>&1 | \ + env time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) -c $(ARGS) 2>&1 | \ sh ../utils/format_metrics.sh exec | \ sh ../utils/aggregate.sh $(KIND) diff --git a/benchmarks/benchmark-others/bigarrays/Makefile b/benchmarks/benchmark-others/bigarrays/Makefile index dd5b69e3c4..2b538242ff 100644 --- a/benchmarks/benchmark-others/bigarrays/Makefile +++ b/benchmarks/benchmark-others/bigarrays/Makefile @@ -3,17 +3,17 @@ export NAME=Others export SUBNAME=bigarrays -SHELL=/bin/bash -o pipefail +SHELL=/usr/bin/env bash -o pipefail bench: @date -u +"%FT%TZ - $(NAME)/$(SUBNAME): starting" ocamlc bench.ml -o bench - $(MAKE) perform COMPILER=js_of_ocaml SCRIPT=bench.js KIND=js - $(MAKE) perform COMPILER=wasm_of_ocaml SCRIPT=bench.wasm.js KIND=wasm + #$(MAKE) perform COMPILER=js_of_ocaml FLAGS= SCRIPT=bench.js KIND=js + $(MAKE) perform COMPILER=wasm_of_ocaml FLAGS="--enable use-js-string" SCRIPT=bench.wasm.js KIND=wasm @date -u +"%FT%TZ - $(NAME)/$(SUBNAME): done" perform: - $(COMPILER) --opt 2 --pretty bench -o $(SCRIPT) - /usr/bin/time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) 2>&1 | \ + $(COMPILER) $(FLAGS) --opt 2 --pretty bench -o $(SCRIPT) + env time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) 2>&1 | \ sh ../../utils/format_metrics.sh exec | \ sh ../../utils/aggregate.sh $(KIND) diff --git a/benchmarks/benchmark-others/bin_prot/Makefile b/benchmarks/benchmark-others/bin_prot/Makefile index 51effc0579..4e4b7cf1df 100644 --- a/benchmarks/benchmark-others/bin_prot/Makefile +++ b/benchmarks/benchmark-others/bin_prot/Makefile @@ -3,7 +3,7 @@ export NAME=Others export SUBNAME=bin_prot -SHELL=/bin/bash -o pipefail +SHELL=/usr/bin/env bash -o pipefail bench: @date -u +"%FT%TZ - $(NAME)/$(SUBNAME): starting" @@ -14,7 +14,7 @@ bench: @date -u +"%FT%TZ - $(NAME)/$(SUBNAME): done" perform: - /usr/bin/time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) 2>&1 1> /dev/null | \ + env time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) 2>&1 1> /dev/null | \ tee /dev/stderr | \ sh ../../utils/format_metrics.sh exec | \ sh ../../utils/aggregate.sh $(KIND) diff --git a/benchmarks/benchmark-others/bin_prot/dune b/benchmarks/benchmark-others/bin_prot/dune index 3f60080847..9e7ea8a5f0 100644 --- a/benchmarks/benchmark-others/bin_prot/dune +++ b/benchmarks/benchmark-others/bin_prot/dune @@ -4,7 +4,7 @@ (js_of_ocaml (flags --opt 2)) (wasm_of_ocaml - (flags --opt 2)) + (flags --opt 2 --enable use-js-string)) (preprocess (pps ppx_bin_prot)) (libraries unix)) diff --git a/benchmarks/benchmark-others/lexifi-g2pp/Makefile b/benchmarks/benchmark-others/lexifi-g2pp/Makefile index c7358d6037..936b003d13 100644 --- a/benchmarks/benchmark-others/lexifi-g2pp/Makefile +++ b/benchmarks/benchmark-others/lexifi-g2pp/Makefile @@ -3,7 +3,7 @@ export NAME=Others export SUBNAME=lexifi-g2pp -SHELL=/bin/bash -o pipefail +SHELL=/usr/bin/env bash -o pipefail bench: @date -u +"%FT%TZ - $(NAME)/$(SUBNAME): starting" @@ -13,6 +13,6 @@ bench: @date -u +"%FT%TZ - $(NAME)/$(SUBNAME): done" perform: - /usr/bin/time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) 2>&1 1> /dev/null | \ + env time -f '{"compiler": "$(COMPILER)", "time":"%E"}' node $(SCRIPT) 2>&1 1> /dev/null | \ sh ../../utils/format_metrics.sh exec | \ sh ../../utils/aggregate.sh $(KIND) diff --git a/benchmarks/benchmark-partial-render-table/Makefile b/benchmarks/benchmark-partial-render-table/Makefile index 92889796d5..db90259441 100644 --- a/benchmarks/benchmark-partial-render-table/Makefile +++ b/benchmarks/benchmark-partial-render-table/Makefile @@ -2,7 +2,7 @@ export NAME=Partial Render Table -SHELL=/bin/bash -o pipefail +SHELL=/usr/bin/env bash -o pipefail bench: @date -u +"%FT%TZ - $(NAME): starting" @@ -14,7 +14,7 @@ bench: @date -u +"%FT%TZ - $(NAME): done" perform: - /usr/bin/time -f "%E %R" $(COMPILER) --debug times --opt 2 --pretty main.bc-for-jsoo -o out.js 2>&1 | \ + env time -f "%E %R" $(COMPILER) --debug times --opt 2 --pretty --enable use-js-string main.bc-for-jsoo -o out.js 2>&1 | \ tee /dev/stderr | \ ocaml -I +str str.cma ../utils/compilation_metrics.ml $(COMPILER) "$(NAME)" out.js | \ sh ../utils/aggregate.sh $(KIND) diff --git a/compiler/bin-wasm_of_ocaml/compile.ml b/compiler/bin-wasm_of_ocaml/compile.ml index d0f39f6c28..faa9bb98be 100644 --- a/compiler/bin-wasm_of_ocaml/compile.ml +++ b/compiler/bin-wasm_of_ocaml/compile.ml @@ -84,6 +84,7 @@ let preprocessor_variables () = | `Disabled | `Jspi -> "jspi" | `Cps -> "cps" | `Double_translation -> assert false) ) + ; "use-js-string", Wat_preprocess.Bool (Config.Flag.use_js_string ()) ] let with_runtime_files ~runtime_wasm_files f = @@ -126,6 +127,7 @@ let build_runtime ~runtime_file = [ "bindings" ; "Math" ; "js" + ; "str" ; "wasm:js-string" ; "wasm:text-encoder" ; "wasm:text-decoder" @@ -249,10 +251,10 @@ let generate_prelude ~out_file = Driver.optimize_for_wasm ~profile code in let context = Generate.start () in - let _ = + let _, generated_js = Generate.f ~context - ~unit_name:(Some "prelude") + ~unit_name:(Some "wasmoo_prelude") ~live_vars:variable_uses ~in_cps ~deadcode_sentinal @@ -260,14 +262,14 @@ let generate_prelude ~out_file = program in Generate.wasm_output ch ~opt_source_map_file:None ~context; - uinfo.provides + uinfo.provides, generated_js let build_prelude z = Fs.with_intermediate_file (Filename.temp_file "prelude" ".wasm") @@ fun prelude_file -> - let predefined_exceptions = generate_prelude ~out_file:prelude_file in + let info = generate_prelude ~out_file:prelude_file in Zip.add_file z ~name:"prelude.wasm" ~file:prelude_file; - predefined_exceptions + info let build_js_runtime ~primitives ?runtime_arguments () = let always_required_js, primitives = @@ -443,12 +445,17 @@ let run let z = Zip.open_out tmp_output_file in Zip.add_file z ~name:"runtime.wasm" ~file:tmp_wasm_file; Zip.add_entry z ~name:"runtime.js" ~contents:js_runtime; - let predefined_exceptions = build_prelude z in + let predefined_exceptions, fragments = build_prelude z in Link.add_info z ~predefined_exceptions ~build_info:(Build_info.create `Runtime) - ~unit_data:[] + ~unit_data: + [ { Link.unit_name = "wasmoo_prelude" + ; unit_info = Unit_info.empty + ; fragments + } + ] (); Zip.close_out z) else diff --git a/compiler/bin-wasm_of_ocaml/gen/gen.ml b/compiler/bin-wasm_of_ocaml/gen/gen.ml index 0310943921..0658671d23 100644 --- a/compiler/bin-wasm_of_ocaml/gen/gen.ml +++ b/compiler/bin-wasm_of_ocaml/gen/gen.ml @@ -27,7 +27,12 @@ let check_js_file fname = let default_flags = [] -let interesting_runtimes = [ [ "effects", `S "jspi" ]; [ "effects", `S "cps" ] ] +let interesting_runtimes = + [ [ "effects", `S "jspi"; "use-js-string", `B false ] + ; [ "effects", `S "cps"; "use-js-string", `B false ] + ; [ "effects", `S "jspi"; "use-js-string", `B true ] + ; [ "effects", `S "cps"; "use-js-string", `B true ] + ] let name_runtime standard l = let flags = diff --git a/compiler/bin-wasm_of_ocaml/link_wasm.ml b/compiler/bin-wasm_of_ocaml/link_wasm.ml index 07032da073..0cfb7d00cf 100644 --- a/compiler/bin-wasm_of_ocaml/link_wasm.ml +++ b/compiler/bin-wasm_of_ocaml/link_wasm.ml @@ -69,7 +69,14 @@ let options = in let build_t input_modules output_file variables allowed_imports common opt merge = let allowed_imports = - if List.is_empty allowed_imports then None else Some (List.concat allowed_imports) + match allowed_imports with + | [] -> Some [ "str" ] + | _ :: _ as l -> + let l = List.concat l in + if List.mem ~eq:String.equal "str" l then + Some l + else + Some ("str" :: l) in `Ok { input_modules diff --git a/compiler/lib-wasm/gc_target.ml b/compiler/lib-wasm/gc_target.ml index 686b62d096..d62bcf4176 100644 --- a/compiler/lib-wasm/gc_target.ml +++ b/compiler/lib-wasm/gc_target.ml @@ -35,14 +35,31 @@ module Type = struct ; typ = W.Array { mut = true; typ = Value value } }) - let string_type = - register_type "string" (fun () -> + let bytes_type = + register_type "bytes" (fun () -> return { supertype = None ; final = true ; typ = W.Array { mut = true; typ = Packed I8 } }) + let string_type = + register_type "string" (fun () -> + return + (if Config.Flag.use_js_string () + then + { supertype = None + ; final = true + ; typ = + W.Struct + [ { mut = false; typ = Value (Ref { nullable = true; typ = Any }) } ] + } + else + { supertype = None + ; final = true + ; typ = W.Array { mut = true; typ = Packed I8 } + })) + let float_type = register_type "float" (fun () -> return @@ -788,13 +805,48 @@ module Memory = struct wasm_array_set ~ty:Type.float_array_type (load a) (load i) (unbox_float (load v))) let bytes_length e = - let* ty = Type.string_type in + let* ty = Type.bytes_type in let* e = wasm_cast ty e in return (W.ArrayLen e) - let bytes_get e e' = wasm_array_get ~ty:Type.string_type e e' + let bytes_get e e' = wasm_array_get ~ty:Type.bytes_type e e' + + let bytes_set e e' e'' = wasm_array_set ~ty:Type.bytes_type e e' e'' - let bytes_set e e' e'' = wasm_array_set ~ty:Type.string_type e e' e'' + let string_value e = + let* string = Type.string_type in + let* e = wasm_struct_get string (wasm_cast string e) 0 in + return (W.ExternConvertAny e) + + let string_length e = + if Config.Flag.use_js_string () + then + let* f = + register_import + ~import_module:"wasm:js-string" + ~name:"length" + (Fun { W.params = [ Ref { nullable = true; typ = Extern } ]; result = [ I32 ] }) + in + let* e = string_value e in + return (W.Call (f, [ e ])) + else bytes_length e + + let string_get e e' = + if Config.Flag.use_js_string () + then + let* f = + register_import + ~import_module:"wasm:js-string" + ~name:"charCodeAt" + (Fun + { W.params = [ Ref { nullable = true; typ = Extern }; I32 ] + ; result = [ I32 ] + }) + in + let* e = string_value e in + let* e' = e' in + return (W.Call (f, [ e; e' ])) + else bytes_get e e' let field e idx = wasm_array_get e (Arith.const (Int32.of_int (idx + 1))) @@ -913,6 +965,17 @@ module Constant = struct | Const_named of string | Mutated + let translate_js_string s = + let* x = + register_import + ~import_module:"str" + ~name:s + (Global { mut = false; typ = Ref { nullable = false; typ = Extern } }) + in + let* ty = Type.js_type in + return + (Const_named ("str_" ^ s), W.StructNew (ty, [ AnyConvertExtern (GlobalGet x) ])) + let rec translate_rec c = match c with | Code.Int i -> return (Const, W.RefI31 (Const (I32 (Targetint.to_int32 i)))) @@ -971,34 +1034,29 @@ module Constant = struct | Utf (Utf8 s) -> s | Byte s -> byte_string s in - let* x = - register_import - ~import_module:"str" - ~name:s - (Global { mut = false; typ = Ref { nullable = false; typ = Extern } }) - in - let* ty = Type.js_type in - return - (Const_named ("str_" ^ s), W.StructNew (ty, [ AnyConvertExtern (GlobalGet x) ])) + translate_js_string s | String s -> - let* ty = Type.string_type in - if String.length s >= string_length_threshold - then - let name = Code.Var.fresh_n "string" in - let* () = register_data_segment name s in - return - ( Mutated - , W.ArrayNewData - (ty, name, Const (I32 0l), Const (I32 (Int32.of_int (String.length s)))) - ) + if Config.Flag.use_js_string () + then translate_js_string (byte_string s) else - let l = - String.fold_right - ~f:(fun c r -> W.Const (I32 (Int32.of_int (Char.code c))) :: r) - s - ~init:[] - in - return (Const_named ("str_" ^ s), W.ArrayNewFixed (ty, l)) + let* ty = Type.string_type in + if String.length s >= string_length_threshold + then + let name = Code.Var.fresh_n "string" in + let* () = register_data_segment name s in + return + ( Mutated + , W.ArrayNewData + (ty, name, Const (I32 0l), Const (I32 (Int32.of_int (String.length s)))) + ) + else + let l = + String.fold_right + ~f:(fun c r -> W.Const (I32 (Int32.of_int (Char.code c))) :: r) + s + ~init:[] + in + return (Const_named ("str_" ^ s), W.ArrayNewFixed (ty, l)) | Float f -> let* ty = Type.float_type in return (Const, W.StructNew (ty, [ Const (F64 (Int64.float_of_bits f)) ])) diff --git a/compiler/lib-wasm/generate.ml b/compiler/lib-wasm/generate.ml index d65a71d04d..5c0944c080 100644 --- a/compiler/lib-wasm/generate.ml +++ b/compiler/lib-wasm/generate.ml @@ -300,29 +300,30 @@ module Generate (Target : Target_sig.S) = struct seq (Memory.array_set x y z) Value.unit); register_tern_prim "caml_floatarray_unsafe_set" ~ty:(Int Normalized) (fun x y z -> seq (Memory.float_array_set x y z) Value.unit); - register_bin_prim "caml_string_unsafe_get" `Pure ~ty:(Int Normalized) Memory.bytes_get; + register_bin_prim "caml_string_unsafe_get" `Pure ~ty:(Int Normalized) Memory.string_get; register_bin_prim "caml_bytes_unsafe_get" `Mutable ~ty:(Int Normalized) Memory.bytes_get; - register_tern_prim - "caml_string_unsafe_set" - ~ty:(Int Normalized) - ~tz:(Int Unnormalized) - (fun x y z -> seq (Memory.bytes_set x y z) Value.unit); register_tern_prim "caml_bytes_unsafe_set" ~ty:(Int Normalized) ~tz:(Int Unnormalized) (fun x y z -> seq (Memory.bytes_set x y z) Value.unit); + let string_get context x y = + seq + (let* cond = Arith.uge y (Memory.string_length x) in + instr (W.Br_if (label_index context bound_error_pc, cond))) + (Memory.string_get x y) + in + register_bin_prim_ctx "caml_string_get" ~ty:(Int Normalized) string_get; let bytes_get context x y = seq (let* cond = Arith.uge y (Memory.bytes_length x) in instr (W.Br_if (label_index context bound_error_pc, cond))) (Memory.bytes_get x y) in - register_bin_prim_ctx "caml_string_get" ~ty:(Int Normalized) bytes_get; register_bin_prim_ctx "caml_bytes_get" ~ty:(Int Normalized) bytes_get; let bytes_set context x y z = seq @@ -331,17 +332,12 @@ module Generate (Target : Target_sig.S) = struct Memory.bytes_set x y z) Value.unit in - register_tern_prim_ctx - "caml_string_set" - ~ty:(Int Normalized) - ~tz:(Int Unnormalized) - bytes_set; register_tern_prim_ctx "caml_bytes_set" ~ty:(Int Normalized) ~tz:(Int Unnormalized) bytes_set; - register_un_prim "caml_ml_string_length" `Pure (fun x -> Memory.bytes_length x); + register_un_prim "caml_ml_string_length" `Pure (fun x -> Memory.string_length x); register_un_prim "caml_ml_bytes_length" `Pure (fun x -> Memory.bytes_length x); register_bin_prim "%int_add" @@ -1025,7 +1021,6 @@ module Generate (Target : Target_sig.S) = struct ( Extern ( "caml_string_get" | "caml_bytes_get" - | "caml_string_set" | "caml_bytes_set" | "caml_check_bound" | "caml_check_bound_gen" diff --git a/compiler/lib-wasm/link.ml b/compiler/lib-wasm/link.ml index f2c527323b..6d31387308 100644 --- a/compiler/lib-wasm/link.ml +++ b/compiler/lib-wasm/link.ml @@ -393,7 +393,7 @@ let generate_start_function ~to_link ~out_file = Filename.gen_file out_file @@ fun ch -> let context = Generate.start () in - Generate.add_init_function ~context ~to_link:("prelude" :: to_link); + Generate.add_init_function ~context ~to_link:("wasmoo_prelude" :: to_link); Generate.wasm_output ch ~opt_source_map_file:None ~context; if times () then Format.eprintf " generate start: %a@." Timer.print t1 @@ -644,11 +644,11 @@ let load_information files = match files with | [] -> assert false | runtime :: other_files -> - let build_info, predefined_exceptions, _unit_data = + let build_info, predefined_exceptions, unit_data = Zip.with_open_in runtime read_info in ( predefined_exceptions - , (runtime, (build_info, [])) + , (runtime, (build_info, unit_data)) :: List.map other_files ~f:(fun file -> let build_info, _predefined_exceptions, unit_data = Zip.with_open_in file read_info @@ -745,7 +745,8 @@ let link ~output_file ~linkall ~enable_source_maps ~files = || cmo_file || linkall || unit_info.force_link - || not (StringSet.is_empty (StringSet.inter requires unit_info.provides)) + || (not (StringSet.is_empty (StringSet.inter requires unit_info.provides))) + || String.equal unit_name "wasmoo_prelude" then ( StringSet.diff (StringSet.union unit_info.requires requires) diff --git a/compiler/lib-wasm/target_sig.ml b/compiler/lib-wasm/target_sig.ml index 428327a3df..45d6ce1fe7 100644 --- a/compiler/lib-wasm/target_sig.ml +++ b/compiler/lib-wasm/target_sig.ml @@ -80,6 +80,10 @@ module type S = sig val bytes_set : expression -> expression -> expression -> unit Code_generation.t + val string_length : expression -> expression + + val string_get : expression -> expression -> expression + val box_float : expression -> expression val unbox_float : expression -> expression diff --git a/compiler/lib-wasm/wat_preprocess.ml b/compiler/lib-wasm/wat_preprocess.ml index f753c4672d..7fbba2d23c 100644 --- a/compiler/lib-wasm/wat_preprocess.ml +++ b/compiler/lib-wasm/wat_preprocess.ml @@ -286,6 +286,9 @@ type st = ; mutable pos : pos ; variables : value StringMap.t ; buf : Buffer.t + ; mutable head : int + ; head_buf : Buffer.t + ; mutable id : int (* to generate distinct string id names *) } let value_type v : typ = @@ -412,6 +415,11 @@ let insert st s = let pred_position { loc; byte_loc } = { loc = { loc with pos_cnum = loc.pos_cnum - 1 }; byte_loc = byte_loc - 1 } +let generate_id st _ = + let id = Printf.sprintf "$js$string$%d$" st.id in + st.id <- st.id + 1; + id + let rec rewrite_list st l = List.iter ~f:(rewrite st) l and rewrite st elt = @@ -508,35 +516,116 @@ and rewrite st elt = then raise (Error (position_of_loc loc_value, "Expecting a string")); let s = parse_string loc_value value in write st pos; + if variable_is_set st "use-js-string" + then ( + Printf.bprintf + st.head_buf + "(import \"str\" %s (global %s$string externref)) " + value + name; + insert + st + (Printf.sprintf + "(global %s (ref eq) (struct.new $string (any.convert_extern (global.get \ + %s$string))))" + name + name)) + else + insert + st + (Format.asprintf + "(global %s (ref eq) (array.new_fixed $bytes %d%a))" + name + (String.length s) + (fun f s -> + String.iter + ~f:(fun c -> Format.fprintf f " (i32.const %d)" (Char.code c)) + s) + s); + skip st pos' + | { desc = List [ { desc = Atom "@string"; _ }; { desc = Atom value; loc = loc_value } ] + ; loc = pos, pos' + } -> + if not (is_string value) + then raise (Error (position_of_loc loc_value, "Expecting a string")); + let s = parse_string loc_value value in + let name = generate_id st s in + write st pos; + if variable_is_set st "use-js-string" + then ( + Printf.bprintf + st.head_buf + "(import \"str\" %s (global %s$string externref)) " + value + name; + insert + st + (Printf.sprintf + "(struct.new $string (any.convert_extern (global.get %s$string)))" + name)) + else + insert + st + (Format.asprintf + "(array.new_fixed $bytes %d%a)" + (String.length s) + (fun f s -> + String.iter + ~f:(fun c -> Format.fprintf f " (i32.const %d)" (Char.code c)) + s) + s); + skip st pos' + | { desc = + List + [ { desc = Atom "@jsstring"; _ } + ; { desc = Atom name; _ } + ; { desc = Atom value; _ } + ] + ; loc = pos, pos' + } -> + write st pos; + Printf.bprintf + st.head_buf + "(import \"str\" %s (global %s$string externref)) " + value + name; insert st - (Format.asprintf - "(global %s (ref eq) (array.new_fixed $bytes %d%a))" + (Printf.sprintf + "(global %s (ref eq) (struct.new $js (any.convert_extern (global.get \ + %s$string))))" name - (String.length s) - (fun f s -> - String.iter ~f:(fun c -> Format.fprintf f " (i32.const %d)" (Char.code c)) s) - s); + name); skip st pos' - | { desc = List [ { desc = Atom "@string"; _ }; { desc = Atom value; loc = loc_value } ] + | { desc = + List [ { desc = Atom "@jsstring"; _ }; { desc = Atom value; loc = loc_value } ] ; loc = pos, pos' } -> if not (is_string value) then raise (Error (position_of_loc loc_value, "Expecting a string")); let s = parse_string loc_value value in + let name = generate_id st s in write st pos; + Printf.bprintf + st.head_buf + "(import \"str\" %s (global %s$string externref)) " + value + name; insert st - (Format.asprintf - "(array.new_fixed $bytes %d%a)" - (String.length s) - (fun f s -> - String.iter ~f:(fun c -> Format.fprintf f " (i32.const %d)" (Char.code c)) s) - s); + (Printf.sprintf + "(struct.new $%s (any.convert_extern (global.get %s$string))))" + (if variable_is_set st "use-js-string" then "string" else "js") + name); skip st pos' - | { desc = List [ { desc = Atom "@string"; loc = _, pos } ]; loc = _, pos' } -> + | { desc = List [ { desc = Atom ("@string" | "@jsstring"); loc = _, pos } ] + ; loc = _, pos' + } -> raise (Error ((pos.loc, pos'.loc), Printf.sprintf "Expecting an id or a string.\n")) - | { desc = List ({ desc = Atom "@string"; _ } :: _ :: _ :: { loc; _ } :: _); _ } -> + | { desc = + List ({ desc = Atom ("@string" | "@jsstring"); _ } :: _ :: _ :: { loc; _ } :: _) + ; _ + } -> raise (Error (position_of_loc loc, Printf.sprintf "Expecting a closing parenthesis.\n")) | { desc = List [ { desc = Atom "@char"; _ }; { desc = Atom value; loc = loc_value } ] @@ -576,6 +665,9 @@ and rewrite st elt = insert st (Printf.sprintf " $%s " (parse_string export_loc export_name)); skip st pos'; rewrite_list st l + | { desc = List ({ desc = Atom "module"; loc = _, pos } :: _ as l); _ } -> + st.head <- pos.byte_loc; + rewrite_list st l | { desc = List l; _ } -> rewrite_list st l | _ -> () @@ -585,7 +677,7 @@ let ocaml_version = Scanf.sscanf Sys.ocaml_version "%d.%d.%d" (fun major minor patchlevel -> Version (major, minor, patchlevel)) -let default_settings = [ "name-wasm-functions", Bool true ] +let default_settings = [ "name-wasm-functions", Bool true; "use-js-string", Bool false ] let f ~variables ~filename ~contents:text = let variables = @@ -599,10 +691,23 @@ let f ~variables ~filename ~contents:text = Sedlexing.set_filename lexbuf filename; try let t, (pos, end_pos) = parse lexbuf in - let st = { text; pos; variables; buf = Buffer.create (String.length text) } in + let st = + { text + ; pos + ; variables + ; buf = Buffer.create (String.length text) + ; head_buf = Buffer.create 128 + ; head = 0 + ; id = 0 + } + in rewrite_list st t; write st end_pos; - Buffer.contents st.buf + let head = Buffer.contents st.head_buf in + let contents = Buffer.contents st.buf in + String.sub contents ~pos:0 ~len:st.head + ^ head + ^ String.sub contents ~pos:st.head ~len:(String.length contents - st.head) with Error (loc, msg) -> report_error loc msg type source = diff --git a/compiler/tests-wasm_of_ocaml/preprocess/dune b/compiler/tests-wasm_of_ocaml/preprocess/dune index bb37a89e75..547a267ef9 100644 --- a/compiler/tests-wasm_of_ocaml/preprocess/dune +++ b/compiler/tests-wasm_of_ocaml/preprocess/dune @@ -17,5 +17,26 @@ (action (diff tests.expected tests.output))) +(rule + (with-stdout-to + tests-js-string.output + (run + %{bin:wasm_of_ocaml} + pp + --enable + use-js-string + --enable + a + --disable + b + --set + c=1 + %{dep:tests.txt}))) + +(rule + (alias runtest) + (action + (diff tests-js-string.expected tests-js-string.output))) + (cram (deps %{bin:wasm_of_ocaml})) diff --git a/compiler/tests-wasm_of_ocaml/preprocess/tests-js-string.expected b/compiler/tests-wasm_of_ocaml/preprocess/tests-js-string.expected new file mode 100644 index 0000000000..2fc07cc905 --- /dev/null +++ b/compiler/tests-wasm_of_ocaml/preprocess/tests-js-string.expected @@ -0,0 +1,79 @@ +(import "str" "abcd" (global $s$string externref)) (import "str" "abcd" (global $js$string$0$$string externref)) (import "str" "\\\'\28\n" (global $js$string$1$$string externref)) (import "str" "abcd" (global $js$string$2$$string externref)) (import "str" "abcd" (global $js$string$3$$string externref)) (import "str" "0" (global $js$string$4$$string externref)) (import "str" "\n" (global $js$string$5$$string externref)) ;; conditional + a is true + b is false + a is true + + +;; nested conditionals + a is true and b is false + + +;; not + + b is false + +;; and + true + a is true + + + a is true and b is false + + + +;; or + + a is true + + a or b is true + a is true or b is false + + a or b is false + +;; strings + newline + quote + +;; string comparisons + c is 1 + + + c is not 2 + +;; version comparisons + + (4 1 1) = (4 1 1) + + (4 1 1) <> (4 1 0) + + (4 1 1) <> (4 1 2) + + (4 1 1) <= (4 1 1) + (4 1 1) <= (4 1 2) + (4 1 1) >= (4 1 0) + (4 1 1) >= (4 1 1) + + (4 1 1) > (4 1 0) + + + +;; version comparisons: lexicographic order + + + (4 1 1) < (4 1 2) + + (4 1 1) < (4 2 0) + (4 1 1) < (5 0 1) + + +;; strings +(global $s (ref eq) (struct.new $string (any.convert_extern (global.get $s$string)))) +(struct.new $string (any.convert_extern (global.get $js$string$0$$string))) +(struct.new $string (any.convert_extern (global.get $js$string$1$$string))) + (struct.new $string (any.convert_extern (global.get $js$string$2$$string))) + (struct.new $string (any.convert_extern (global.get $js$string$3$$string))) + +;; chars +(struct.new $string (any.convert_extern (global.get $js$string$4$$string))) +(struct.new $string (any.convert_extern (global.get $js$string$5$$string))) + diff --git a/dune b/dune index fa9ca7d14d..a6bf4376db 100644 --- a/dune +++ b/dune @@ -8,7 +8,10 @@ (:standard))) (binaries (tools/node_wrapper.exe as node) - (tools/node_wrapper.exe as node.exe))) + (tools/node_wrapper.exe as node.exe)) + (wasm_of_ocaml + (flags + (:standard --enable use-js-string)))) (with-effects (js_of_ocaml (compilation_mode separate) diff --git a/runtime/wasm/array.wat b/runtime/wasm/array.wat index 235a9aa52b..0d88c0632c 100644 --- a/runtime/wasm/array.wat +++ b/runtime/wasm/array.wat @@ -21,6 +21,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $float (struct (field f64))) (type $float_array (array (mut f64))) diff --git a/runtime/wasm/backtrace.wat b/runtime/wasm/backtrace.wat index 9b63e4e554..b2266f0db2 100644 --- a/runtime/wasm/backtrace.wat +++ b/runtime/wasm/backtrace.wat @@ -25,6 +25,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (func (export "caml_get_exception_raw_backtrace") (param (ref eq)) (result (ref eq)) diff --git a/runtime/wasm/bigarray.wat b/runtime/wasm/bigarray.wat index 59cc22cd7f..d2ff177365 100644 --- a/runtime/wasm/bigarray.wat +++ b/runtime/wasm/bigarray.wat @@ -72,6 +72,12 @@ (import "bindings" "dv_set_i8" (func $dv_set_i8 (param externref i32 i32))) (import "bindings" "littleEndian" (global $littleEndian i32)) + (import "bindings" "ta_blit_from_string" + (func $ta_blit_from_string + (param anyref) (param i32) (param (ref extern)) (param i32) + (param i32))) + (import "bindings" "ta_to_string" + (func $ta_to_string (param (ref extern)) (result (ref any)))) (import "fail" "caml_bound_error" (func $caml_bound_error)) (import "fail" "caml_raise_out_of_memory" (func $caml_raise_out_of_memory)) (import "fail" "caml_invalid_argument" @@ -121,9 +127,12 @@ (func $caml_deserialize_int_4 (param (ref eq)) (result i32))) (import "marshal" "caml_deserialize_int_8" (func $caml_deserialize_int_8 (param (ref eq)) (result i64))) + (import "jsstring" "jsstring_length" + (func $jsstring_length (param anyref) (result i32))) (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $float (struct (field f64))) (type $float_array (array (mut f64))) @@ -138,7 +147,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) @@ -2036,10 +2051,23 @@ (local.get $view) (local.get $p) (local.get $d) (i32.const 1)) (ref.i31 (i32.const 0))) - (export "caml_bytes_of_uint8_array" (func $caml_string_of_uint8_array)) - (func $caml_string_of_uint8_array (export "caml_string_of_uint8_array") +(@if use-js-string +(@then + (func (export "caml_string_of_uint8_array") (param (ref eq)) (result (ref eq)) ;; used to convert a typed array to a string + (local $a (ref extern)) + (local.set $a + (ref.as_non_null (extern.convert_any (call $unwrap (local.get 0))))) + (struct.new $string (call $ta_to_string (local.get $a)))) +) +(@else + (export "caml_string_of_uint8_array" (func $caml_bytes_of_uint8_array)) +)) + + (func $caml_bytes_of_uint8_array (export "caml_bytes_of_uint8_array") + (param (ref eq)) (result (ref eq)) + ;; used to convert a typed array to bytes (local $a (ref extern)) (local $len i32) (local $s (ref $bytes)) (local.set $a @@ -2051,8 +2079,30 @@ (local.get $len)) (local.get $s)) - (export "caml_uint8_array_of_bytes" (func $caml_uint8_array_of_string)) - (func $caml_uint8_array_of_string (export "caml_uint8_array_of_string") +(@if use-js-string +(@then + (func (export "caml_uint8_array_of_string") + (param (ref eq)) (result (ref eq)) + ;; Convert a string to a typed array + (local $ta (ref extern)) (local $len i32) + (local $s anyref) + (local.set $s + (struct.get $string 0 (ref.cast (ref $string) (local.get 0)))) + (local.set $len (call $jsstring_length (local.get $s))) + (local.set $ta + (call $ta_create + (i32.const 3) ;; Uint8Array + (local.get $len))) + (call $ta_blit_from_string + (local.get $s) (i32.const 0) (local.get $ta) (i32.const 0) + (local.get $len)) + (call $wrap (any.convert_extern (local.get $ta)))) +) +(@else + (export "caml_uint8_array_of_string" (func $caml_uint8_array_of_bytes)) +)) + + (func $caml_uint8_array_of_bytes (export "caml_uint8_array_of_bytes") (param (ref eq)) (result (ref eq)) ;; Convert bytes to a typed array (local $ta (ref extern)) (local $len i32) diff --git a/runtime/wasm/bigstring.wat b/runtime/wasm/bigstring.wat index 1cf4428dcb..08f62eb75c 100644 --- a/runtime/wasm/bigstring.wat +++ b/runtime/wasm/bigstring.wat @@ -58,10 +58,31 @@ (func $ta_blit_to_bytes (param (ref extern)) (param i32) (param (ref $bytes)) (param i32) (param i32))) + (import "bindings" "ta_blit_from_string" + (func $ta_blit_from_string + (param anyref) (param i32) (param (ref extern)) (param i32) + (param i32))) (import "hash" "caml_hash_mix_int" (func $caml_hash_mix_int (param i32) (param i32) (result i32))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) + (type $js (struct (field anyref))) + +(@if use-js-string +(@then + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + (func $string_val (param $s (ref eq)) (result externref) + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $s))))) +) +(@else + (func $string_get (param $s (ref $bytes)) (param $i i32) (result i32) + (array.get $bytes (local.get $s) (local.get $i))) + (func $string_val (param $s (ref eq)) (result (ref $bytes)) + (ref.cast (ref $bytes) (local.get $s))) +)) (func (export "caml_hash_mix_bigstring") (param $h i32) (param $b (ref eq)) (result i32) @@ -103,7 +124,7 @@ (local.set $h (call $caml_hash_mix_int (local.get $h) (local.get $w)))) (i32.xor (local.get $h) (local.get $len))) - (@string $buffer "buffer") + (@jsstring $buffer "buffer") (func (export "bigstring_to_array_buffer") (param $bs (ref eq)) (result (ref eq)) @@ -164,10 +185,16 @@ (local $i i32) (local $pos1 i32) (local $pos2 i32) (local $len i32) (local $c1 i32) (local $c2 i32) (local $v1 (ref extern)) +(@if use-js-string +(@then + (local $s2 externref) +) +(@else (local $s2 (ref $bytes)) +)) (local.set $v1 (call $caml_ba_get_view (local.get $s1))) (local.set $pos1 (i31.get_s (ref.cast (ref i31) (local.get $vpos1)))) - (local.set $s2 (ref.cast (ref $bytes) (local.get $vs2))) + (local.set $s2 (call $string_val (local.get $vs2))) (local.set $pos2 (i31.get_s (ref.cast (ref i31) (local.get $vpos2)))) (local.set $len (i31.get_s (ref.cast (ref i31) (local.get $vlen)))) (loop $loop @@ -177,7 +204,7 @@ (call $dv_get_ui8 (local.get $v1) (i32.add (local.get $pos1) (local.get $i)))) (local.set $c2 - (array.get_u $bytes (local.get $s2) + (call $string_get (local.get $s2) (i32.add (local.get $pos2) (local.get $i)))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br_if $loop (i32.eq (local.get $c1) (local.get $c2))) @@ -229,8 +256,32 @@ (br $loop)))) (ref.i31 (i32.const -1))) +(@if use-js-string +(@then + (func (export "caml_bigstring_blit_string_to_ba") + (param $str1 (ref eq)) (param $vpos1 (ref eq)) + (param $ba2 (ref eq)) (param $vpos2 (ref eq)) + (param $vlen (ref eq)) (result (ref eq)) + (local $pos1 i32) (local $pos2 i32) (local $len i32) + (local $s1 anyref) + (local $d2 (ref extern)) + (local.set $s1 + (struct.get $string 0 (ref.cast (ref $string) (local.get $str1)))) + (local.set $pos1 (i31.get_s (ref.cast (ref i31) (local.get $vpos1)))) + (local.set $d2 (call $caml_ba_get_data (local.get $ba2))) + (local.set $pos2 (i31.get_s (ref.cast (ref i31) (local.get $vpos2)))) + (local.set $len (i31.get_s (ref.cast (ref i31) (local.get $vlen)))) + (call $ta_blit_from_string + (local.get $s1) (local.get $pos1) + (local.get $d2) (local.get $pos2) + (local.get $len)) + (ref.i31 (i32.const 0))) +) +(@else (export "caml_bigstring_blit_string_to_ba" (func $caml_bigstring_blit_bytes_to_ba)) +)) + (func $caml_bigstring_blit_bytes_to_ba (export "caml_bigstring_blit_bytes_to_ba") (param $str1 (ref eq)) (param $vpos1 (ref eq)) diff --git a/runtime/wasm/blake2.wat b/runtime/wasm/blake2.wat index 25ad007838..0a0fd32e4a 100644 --- a/runtime/wasm/blake2.wat +++ b/runtime/wasm/blake2.wat @@ -7,6 +7,8 @@ (func $caml_string_of_jsbytes (param (ref eq)) (result (ref eq)))) (import "jslib" "caml_jsbytes_of_string" (func $caml_jsbytes_of_string (param (ref eq)) (result (ref eq)))) + (import "jsstring" "jsbytes_of_bytes" + (func $jsbytes_of_bytes (param (ref $bytes)) (result anyref))) (import "js" "blake2_js_for_wasm_create" (func $blake2_js_for_wasm_create (param (ref eq) anyref) (result anyref))) @@ -30,7 +32,7 @@ (local.get $hashlen) (call $unwrap (call $caml_jsbytes_of_string (local.get $key)))))) - (func $jsbytes_of_substring + (func $jsbytes_of_subbytes (param $vbuf (ref eq)) (param $vofs (ref eq)) (param $vlen (ref eq)) (result anyref i32 i32) (local $buf (ref $bytes)) (local $buf' (ref $bytes)) @@ -49,17 +51,27 @@ (local.set $buf (local.get $buf')) (local.set $ofs (i32.const 0)))) (tuple.make 3 - (call $unwrap (call $caml_jsbytes_of_string (local.get $buf))) + (call $jsbytes_of_bytes (local.get $buf)) (local.get $ofs) (local.get $len))) (func (export "caml_blake2_update") (param $ctx (ref eq)) (param $buf (ref eq)) (param $ofs (ref eq)) (param $len (ref eq)) (result (ref eq)) +(@if (and use-js-string (< ocaml_version (5 3 0))) +(@then (call $blake2_js_for_wasm_update (call $unwrap (local.get $ctx)) - (call $jsbytes_of_substring + (call $unwrap (local.get $buf)) + (i31.get_u (ref.cast (ref i31) (local.get $ofs))) + (i31.get_u (ref.cast (ref i31) (local.get $len)))) +) +(@else + (call $blake2_js_for_wasm_update + (call $unwrap (local.get $ctx)) + (call $jsbytes_of_subbytes (local.get $buf) (local.get $ofs) (local.get $len))) +)) (ref.i31 (i32.const 0))) (func (export "caml_blake2_final") @@ -70,7 +82,29 @@ (call $unwrap (local.get $ctx)) (local.get $hashlen))))) - (func (export "caml_blake2_string") (export "caml_blake2_bytes") +(@if use-js-string +(@then + (func (export "caml_blake2_string") + (param $hashlen (ref eq)) (param $key (ref eq)) (param $buf (ref eq)) + (param $ofs (ref eq)) (param $len (ref eq)) (result (ref eq)) + (local $ctx anyref) + (local.set $ctx + (call $blake2_js_for_wasm_create + (local.get $hashlen) + (call $unwrap (local.get $key)))) + (call $blake2_js_for_wasm_update + (local.get $ctx) + (call $unwrap (local.get $buf)) + (i31.get_u (ref.cast (ref i31) (local.get $ofs))) + (i31.get_u (ref.cast (ref i31) (local.get $len)))) + (return_call $wrap + (call $blake2_js_for_wasm_final + (local.get $ctx) (local.get $hashlen)))) +) +(@else + (export "caml_blake2_string" (func $caml_blake2_bytes)) +)) + (func $caml_blake2_bytes (export "caml_blake2_bytes") (param $hashlen (ref eq)) (param $key (ref eq)) (param $buf (ref eq)) (param $ofs (ref eq)) (param $len (ref eq)) (result (ref eq)) (local $ctx anyref) @@ -80,7 +114,7 @@ (call $unwrap (call $caml_jsbytes_of_string (local.get $key))))) (call $blake2_js_for_wasm_update (local.get $ctx) - (call $jsbytes_of_substring + (call $jsbytes_of_subbytes (local.get $buf) (local.get $ofs) (local.get $len))) (return_call $caml_string_of_jsbytes (call $wrap diff --git a/runtime/wasm/compare.wat b/runtime/wasm/compare.wat index b6a48a62b7..b922068daf 100644 --- a/runtime/wasm/compare.wat +++ b/runtime/wasm/compare.wat @@ -39,6 +39,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $float (struct (field f64))) (type $float_array (array (mut f64))) (type $js (struct (field anyref))) @@ -62,7 +63,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) diff --git a/runtime/wasm/custom.wat b/runtime/wasm/custom.wat index 526d36ca62..6f1f2c0769 100644 --- a/runtime/wasm/custom.wat +++ b/runtime/wasm/custom.wat @@ -22,11 +22,12 @@ (import "int64" "int64_ops" (global $int64_ops (ref $custom_operations))) (import "bigarray" "bigarray_ops" (global $bigarray_ops (ref $custom_operations))) - (import "string" "caml_bytes_equal" - (func $caml_bytes_equal + (import "string" "caml_string_equal" + (func $caml_string_equal (param (ref eq)) (param (ref eq)) (result (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $compare (func (param (ref eq)) (param (ref eq)) (param i32) (result i32))) (type $hash @@ -38,7 +39,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) @@ -109,14 +116,14 @@ (local.get $ops) (global.get $custom_operations)))) (func (export "caml_find_custom_operations") - (param $id (ref $bytes)) (result (ref null $custom_operations)) + (param $id (ref eq)) (result (ref null $custom_operations)) (local $l (ref null $custom_operations_list)) (block $not_found (local.set $l (br_on_null $not_found (global.get $custom_operations))) (loop $loop (if (i31.get_u (ref.cast (ref i31) - (call $caml_bytes_equal (local.get $id) + (call $caml_string_equal (local.get $id) (struct.get $custom_operations $id (struct.get $custom_operations_list $ops (local.get $l)))))) diff --git a/runtime/wasm/dune b/runtime/wasm/dune index a9305e7a41..56c2c6f035 100644 --- a/runtime/wasm/dune +++ b/runtime/wasm/dune @@ -17,6 +17,7 @@ --binaryen=-g --binaryen-opt=-O3 --set=effects=jspi + --disable=use-js-string --allowed-imports=bindings,Math,js,wasm:js-string,wasm:text-encoder,wasm:text-decoder %{target} %{read-lines:args}))) @@ -32,6 +33,39 @@ --binaryen=-g --binaryen-opt=-O3 --set=effects=cps + --disable=use-js-string + --allowed-imports=bindings,Math,js,wasm:js-string,wasm:text-encoder,wasm:text-decoder + %{target} + %{read-lines:args}))) + +(rule + (target runtime-jspi-use-js-string.wasm) + (deps + args + (glob_files *.wat)) + (action + (run + ../../compiler/bin-wasm_of_ocaml/wasmoo_link_wasm.exe + --binaryen=-g + --binaryen-opt=-O3 + --set=effects=jspi + --enable=use-js-string + --allowed-imports=bindings,Math,js,wasm:js-string,wasm:text-encoder,wasm:text-decoder + %{target} + %{read-lines:args}))) + +(rule + (target runtime-cps-use-js-string.wasm) + (deps + args + (glob_files *.wat)) + (action + (run + ../../compiler/bin-wasm_of_ocaml/wasmoo_link_wasm.exe + --binaryen=-g + --binaryen-opt=-O3 + --set=effects=cps + --enable=use-js-string --allowed-imports=bindings,Math,js,wasm:js-string,wasm:text-encoder,wasm:text-decoder %{target} %{read-lines:args}))) diff --git a/runtime/wasm/effect.wat b/runtime/wasm/effect.wat index 05bc0ad9c2..4ca192929c 100644 --- a/runtime/wasm/effect.wat +++ b/runtime/wasm/effect.wat @@ -44,6 +44,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $function_1 (func (param (ref eq) (ref eq)) (result (ref eq)))) (type $closure (sub (struct (;(field i32);) (field (ref $function_1))))) (type $function_3 diff --git a/runtime/wasm/fail.wat b/runtime/wasm/fail.wat index 04a6092a0e..84932e11aa 100644 --- a/runtime/wasm/fail.wat +++ b/runtime/wasm/fail.wat @@ -22,6 +22,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (tag $ocaml_exception (export "ocaml_exception") (param (ref eq))) (export "javascript_exception" (tag $javascript_exception)) @@ -60,7 +61,7 @@ (return_call $caml_raise_with_arg (array.get $block (global.get $caml_global_data) (global.get $FAILURE_EXN)) - (local.get 0))) + (local.get $arg))) (global $INVALID_EXN i32 (i32.const 3)) diff --git a/runtime/wasm/float.wat b/runtime/wasm/float.wat index b0bf76e609..1e682a9cad 100644 --- a/runtime/wasm/float.wat +++ b/runtime/wasm/float.wat @@ -31,9 +31,32 @@ (func $jsstring_of_bytes (param (ref $bytes)) (result anyref))) (import "jsstring" "bytes_of_jsstring" (func $bytes_of_jsstring (param anyref) (result (ref $bytes)))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) - (type $float (struct (field f64))) +(@if use-js-string +(@then + (import "wasm:js-string" "length" + (func $string_length (param externref) (result i32))) + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + + (func $string_val (param $s (ref eq)) (result externref) + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $s))))) +) +(@else + (func $string_length (param $s (ref $bytes)) (result i32) + (array.len (local.get $s))) + (func $string_get (param $s (ref $bytes)) (param $i i32) (result i32) + (array.get $bytes (local.get $s) (local.get $i))) + (func $string_val (param $s (ref eq)) (result (ref $bytes)) + (ref.cast (ref $bytes) (local.get $s))) +)) + + (type $float (struct (field f64))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $block (array (mut (ref eq)))) (type $chars (array i8)) @@ -189,24 +212,32 @@ (then (array.set $bytes (local.get $s) (i32.const 0) (local.get $style)))))) - (local.get $s)) + (return_call $caml_string_of_bytes (local.get $s))) (@string $format_error "format_float: bad format") (func $parse_format - (param $s (ref $bytes)) (result i32 i32 i32 i32) + (param $v (ref eq)) (result i32 i32 i32 i32) +(@if use-js-string +(@then + (local $s externref) +) +(@else + (local $s (ref $bytes)) +)) (local $i i32) (local $len i32) (local $c i32) (local $sign_style i32) (local $precision i32) (local $conversion i32) (local $uppercase i32) - (local.set $len (array.len (local.get $s))) + (local.set $s (call $string_val (local.get $v))) + (local.set $len (call $string_length (local.get $s))) (local.set $i (i32.const 1)) (block $return (block $bad_format (br_if $bad_format (i32.lt_u (local.get $len) (i32.const 2))) (br_if $bad_format - (i32.ne (array.get_u $bytes (local.get $s) (i32.const 0)) + (i32.ne (call $string_get (local.get $s) (i32.const 0)) (@char "%"))) - (local.set $c (array.get_u $bytes (local.get $s) (i32.const 1))) + (local.set $c (call $string_get (local.get $s) (i32.const 1))) (if (i32.eq (local.get $c) (@char "+")) (then (local.set $sign_style (i32.const 1)) @@ -217,13 +248,13 @@ (local.set $i (i32.add (local.get $i) (i32.const 1))))) (br_if $bad_format (i32.eq (local.get $i) (local.get $len))) (br_if $bad_format - (i32.ne (array.get_u $bytes (local.get $s) (local.get $i)) - (@char "."))) + (i32.ne (call $string_get (local.get $s) (local.get $i)) + (@char "."))) ;; '.' (loop $precision (local.set $i (i32.add (local.get $i) (i32.const 1))) (br_if $bad_format (i32.eq (local.get $i) (local.get $len))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.and (i32.ge_u (local.get $c) (@char "0")) (i32.le_u (local.get $c) (@char "9"))) (then @@ -259,8 +290,7 @@ (local $num anyref) (local.set $f (struct.get $float 0 (ref.cast (ref $float) (local.get 1)))) (local.set $b (i64.reinterpret_f64 (local.get $f))) - (local.set $format - (call $parse_format (ref.cast (ref $bytes) (local.get 0)))) + (local.set $format (call $parse_format (local.get 0))) (local.set $sign_style (tuple.extract 4 0 (local.get $format))) (local.set $precision (tuple.extract 4 1 (local.get $format))) (local.set $conversion (tuple.extract 4 2 (local.get $format))) @@ -328,23 +358,31 @@ (i32.sub (local.get $c) (i32.const 32))))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br_if $uppercase (i32.lt_u (local.get $i) (local.get $len)))))) - (local.get $s)) + (return_call $caml_string_of_bytes (local.get $s))) (@string $float_of_string "float_of_string") - (func $caml_float_of_hex (param $s (ref $bytes)) (param $i i32) (result f64) + (func $caml_float_of_hex +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $i i32) (result f64) (local $len i32) (local $c i32) (local $d i32) (local $m i64) (local $f f64) (local $negative i32) (local $dec_point i32) (local $exp i32) (local $adj i32) (local $n_bits i32) (local $m_bits i32) (local $x_bits i32) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (local.set $dec_point (i32.const -1)) (block $error (loop $parse (if (i32.lt_u (local.get $i) (local.get $len)) (then (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.eq (local.get $c) (@char ".")) (then @@ -357,7 +395,7 @@ (then (br_if $error (i32.eq (local.get $i) (local.get $len))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.eq (local.get $c) (@char "-")) (then @@ -365,7 +403,7 @@ (br_if $error (i32.eq (local.get $i) (local.get $len))) (local.set $c - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))))) @@ -374,7 +412,7 @@ (br_if $error (i32.eq (local.get $i) (local.get $len))) (local.set $c - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))))) @@ -394,7 +432,7 @@ (if (i32.ne (local.get $i) (local.get $len)) (then (local.set $c - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) @@ -474,24 +512,39 @@ (call $caml_failwith (global.get $float_of_string)) (f64.const 0)) - (func $on_whitespace (param $s (ref $bytes)) (param $i i32) (result i32) + (func $on_whitespace +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $i i32) (result i32) (local $c i32) - (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) + (local.set $c (call $string_get (local.get $s) (local.get $i))) (i32.or (i32.eq (local.get $c) (@char " ")) (i32.le_u (i32.sub (local.get $c) (i32.const 9)) (i32.const 4)))) (func (export "caml_float_of_string") (param (ref eq)) (result (ref eq)) - (local $s (ref $bytes)) (local $len i32) (local $i i32) (local $j i32) +(@if use-js-string +(@then + (local $s externref) +) +(@else + (local $s (ref $bytes)) +)) + (local $len i32) (local $i i32) (local $j i32) (local $s' (ref $bytes)) (local $negative i32) (local $c i32) (local $f f64) - (local.set $s (ref.cast (ref $bytes) (local.get 0))) - (local.set $len (array.len (local.get $s))) + (local.set $s (call $string_val (local.get 0))) + (local.set $len (call $string_length (local.get $s))) (loop $count (if (i32.lt_u (local.get $i) (local.get $len)) (then (if (i32.eq (@char "_") - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (then (local.set $j (i32.add (local.get $j) (i32.const 1))))) (local.set $i (i32.add (local.get $i) (i32.const 1))) @@ -507,7 +560,7 @@ (if (i32.lt_u (local.get $i) (local.get $len)) (then (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.ne (local.get $c) (@char "_")) (then @@ -517,7 +570,15 @@ (i32.add (local.get $j) (i32.const 1))))) (br $copy)))) (local.set $len (array.len (local.get $s'))) - (local.set $s (local.get $s')))) +(@if use-js-string +(@then + (local.set $s + (extern.convert_any (call $jsstring_of_bytes (local.get $s')))) +) +(@else + (local.set $s (local.get $s')) +)) + )) (local.set $i (i32.const 0)) (loop $skip_spaces (if (i32.lt_u (local.get $i) (local.get $len)) @@ -531,7 +592,7 @@ (br_if $error (call $on_whitespace (local.get $s) (i32.sub (local.get $len) (i32.const 1)))) - (local.set $c (array.get_u $bytes (local.get $s) (i32.const 0))) + (local.set $c (call $string_get (local.get $s) (i32.const 0))) (if (i32.eq (local.get $c) (@char "-")) (then (local.set $negative (i32.const 1)) @@ -541,11 +602,11 @@ (local.set $i (i32.const 1)))) (if (i32.lt_u (i32.add (local.get $i) (i32.const 2)) (local.get $len)) (then - (if (i32.eq (array.get_u $bytes (local.get $s) (local.get $i)) + (if (i32.eq (call $string_get (local.get $s) (local.get $i)) (@char "0")) (then (if (i32.eq (i32.and - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.add (local.get $i) (i32.const 1))) (i32.const 0xdf)) (@char "X")) @@ -558,17 +619,17 @@ (return (struct.new $float (local.get $f))))))))) (if (i32.eq (i32.add (local.get $i) (i32.const 3)) (local.get $len)) (then - (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) + (local.set $c (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "N")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "A")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "N")) (then @@ -578,12 +639,12 @@ (@char "I")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "N")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "F")) (then @@ -595,34 +656,34 @@ (local.get $negative)))))))))))) (if (i32.eq (i32.add (local.get $i) (i32.const 8)) (local.get $len)) (then - (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) + (local.set $c (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "I")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "N")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "F")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))) + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "I")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) (@char "N")) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) @@ -630,7 +691,7 @@ (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) (i32.const 0xdf)) @@ -638,7 +699,7 @@ (local.set $i (i32.add (local.get $i) (i32.const 1))) (local.set $c - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (i32.and (local.get $c) @@ -652,7 +713,15 @@ (local.get $negative)))) )))))))))))))))))) (local.set $f - (call $parse_float (call $jsstring_of_bytes (local.get $s)))) + (call $parse_float +(@if use-js-string +(@then + (any.convert_extern (local.get $s)) +) +(@else + (call $jsstring_of_bytes (local.get $s)) +)) + )) (br_if $error (f64.ne (local.get $f) (local.get $f))) (return (struct.new $float (local.get $f)))) (call $caml_failwith (global.get $float_of_string)) diff --git a/runtime/wasm/fs.wat b/runtime/wasm/fs.wat index 898dabc6a6..ede02efacd 100644 --- a/runtime/wasm/fs.wat +++ b/runtime/wasm/fs.wat @@ -50,6 +50,7 @@ (func $caml_string_concat (param (ref eq) (ref eq)) (result (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (func (export "caml_sys_getcwd") (param (ref eq)) (result (ref eq)) diff --git a/runtime/wasm/hash.wat b/runtime/wasm/hash.wat index a7a78c9e49..7528549c0c 100644 --- a/runtime/wasm/hash.wat +++ b/runtime/wasm/hash.wat @@ -25,6 +25,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $float (struct (field f64))) (type $js (struct (field anyref))) @@ -39,7 +40,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) @@ -120,7 +127,7 @@ (then (local.set $i (i32.const 0)))) (return_call $caml_hash_mix_int (local.get $h) (local.get $i))) - (func $caml_hash_mix_string (export "caml_hash_mix_string") + (func $caml_hash_mix_bytes (param $h i32) (param $s (ref $bytes)) (result i32) (local $i i32) (local $len i32) (local $w i32) (local.set $len (array.len (local.get $s))) @@ -168,6 +175,19 @@ (local.set $h (call $caml_hash_mix_int (local.get $h) (local.get $w)))) (i32.xor (local.get $h) (local.get $len))) + (func $caml_hash_mix_string + (param $h i32) (param $s (ref $string)) (result i32) + (return_call $jsstring_hash + (local.get $h) (struct.get $js 0 (local.get $s)))) + +(@if use-js-string +(@then + (export "caml_hash_mix_string" (func $caml_hash_mix_string)) +) +(@else + (export "caml_hash_mix_string" (func $caml_hash_mix_bytes)) +)) + (global $HASH_QUEUE_SIZE i32 (i32.const 256)) (global $MAX_FORWARD_DEREFERENCE i32 (i32.const 1000)) @@ -216,10 +236,10 @@ (i32.const 1)))) (local.set $num (i32.sub (local.get $num) (i32.const 1))) (br $loop))) - (drop (block $not_string (result (ref eq)) + (drop (block $not_bytes (result (ref eq)) (local.set $h - (call $caml_hash_mix_string (local.get $h) - (br_on_cast_fail $not_string (ref eq) (ref $bytes) + (call $caml_hash_mix_bytes (local.get $h) + (br_on_cast_fail $not_bytes (ref eq) (ref $bytes) (local.get $v)))) (local.set $num (i32.sub (local.get $num) (i32.const 1))) (br $loop))) @@ -323,6 +343,8 @@ (ref.i31 (i32.and (call $caml_hash_mix_final (local.get $h)) (i32.const 0x3FFFFFFF)))) +(@if use-js-string +(@then (func (export "caml_string_hash") (param (ref eq)) (param (ref eq)) (result (ref eq)) (local $h i32) @@ -330,7 +352,20 @@ (i32.and (call $caml_hash_mix_final (call $caml_hash_mix_string + (i31.get_s (ref.cast (ref i31) (local.get 0))) + (ref.cast (ref $string) (local.get 1)))) + (i32.const 0x3FFFFFFF)))) +) +(@else + (func (export "caml_string_hash") + (param (ref eq)) (param (ref eq)) (result (ref eq)) + (local $h i32) + (ref.i31 + (i32.and + (call $caml_hash_mix_final + (call $caml_hash_mix_bytes (i31.get_s (ref.cast (ref i31) (local.get 0))) (ref.cast (ref $bytes) (local.get 1)))) (i32.const 0x3FFFFFFF)))) +)) ) diff --git a/runtime/wasm/int32.wat b/runtime/wasm/int32.wat index bb3126fb53..ba52e1a71c 100644 --- a/runtime/wasm/int32.wat +++ b/runtime/wasm/int32.wat @@ -33,6 +33,7 @@ (func $caml_deserialize_int_4 (param (ref eq)) (result i32))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $compare (func (param (ref eq)) (param (ref eq)) (param i32) (result i32))) (type $hash @@ -44,7 +45,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) diff --git a/runtime/wasm/int64.wat b/runtime/wasm/int64.wat index 6b2a4fb964..06bd3303a6 100644 --- a/runtime/wasm/int64.wat +++ b/runtime/wasm/int64.wat @@ -18,11 +18,25 @@ (module (import "ints" "parse_sign_and_base" (func $parse_sign_and_base - (param (ref $bytes)) (result i32 i32 i32 i32))) +(@if use-js-string +(@then + (param externref) +) +(@else + (param (ref $bytes)) +)) + (result i32 i32 i32 i32))) (import "ints" "parse_digit" (func $parse_digit (param i32) (result i32))) (import "ints" "parse_int_format" (func $parse_int_format - (param (ref $bytes)) (result i32 i32 i32 i32 i32))) +(@if use-js-string +(@then + (param externref) +) +(@else + (param (ref $bytes)) +)) + (result i32 i32 i32 i32 i32))) (import "fail" "caml_failwith" (func $caml_failwith (param (ref eq)))) (import "marshal" "caml_serialize_int_8" (func $caml_serialize_int_8 (param (ref eq)) (param i64))) @@ -32,8 +46,31 @@ (global $lowercase_hex_table (ref $chars))) (import "ints" "uppercase_hex_table" (global $uppercase_hex_table (ref $chars))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) + +(@if use-js-string +(@then + (import "wasm:js-string" "length" + (func $string_length (param externref) (result i32))) + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + + (func $string_val (param $s (ref eq)) (result externref) + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $s))))) +) +(@else + (func $string_length (param $s (ref $bytes)) (result i32) + (array.len (local.get $s))) + (func $string_get (param $s (ref $bytes)) (param $i i32) (result i32) + (array.get $bytes (local.get $s) (local.get $i))) + (func $string_val (param $s (ref eq)) (result (ref $bytes)) + (ref.cast (ref $bytes) (local.get $s))) +)) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $compare (func (param (ref eq)) (param (ref eq)) (param i32) (result i32))) (type $hash @@ -45,7 +82,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) @@ -137,17 +180,24 @@ ;; package "integers". (func $caml_i64_of_digits (export "caml_i64_of_digits") (param $base i32) (param $signedness i32) (param $sign i32) - (param $s (ref $bytes)) (param $i i32) (param $errmsg (ref eq)) +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $i i32) (param $errmsg (ref eq)) (result i64) (local $len i32) (local $d i32) (local $c i32) (local $res i64) (local $threshold i64) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (if (i32.eqz (local.get $len)) (then (call $caml_failwith (local.get $errmsg)))) (local.set $threshold (i64.div_u (i64.const -1) (i64.extend_i32_u (local.get $base)))) (local.set $d - (call $parse_digit (array.get_u $bytes (local.get $s) (local.get $i)))) + (call $parse_digit (call $string_get (local.get $s) (local.get $i)))) (if (i32.ge_u (local.get $d) (local.get $base)) (then (call $caml_failwith (local.get $errmsg)))) (local.set $res (i64.extend_i32_u (local.get $d))) @@ -155,7 +205,7 @@ (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.lt_s (local.get $i) (local.get $len)) (then - (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) + (local.set $c (call $string_get (local.get $s) (local.get $i))) (br_if $loop (i32.eq (local.get $c) (@char "_"))) (local.set $d (call $parse_digit (local.get $c))) (if (i32.ge_u (local.get $d) (local.get $base)) @@ -186,10 +236,16 @@ (local.get $res)) (func (export "caml_int64_of_string") (param $v (ref eq)) (result (ref eq)) +(@if use-js-string +(@then + (local $s externref) +) +(@else (local $s (ref $bytes)) +)) (local $i i32) (local $signedness i32) (local $sign i32) (local $base i32) (local $t (tuple i32 i32 i32 i32)) - (local.set $s (ref.cast (ref $bytes) (local.get $v))) + (local.set $s (call $string_val (local.get $v))) (local.set $t (call $parse_sign_and_base (local.get $s))) (local.set $i (tuple.extract 4 0 (local.get $t))) (local.set $signedness (tuple.extract 4 1 (local.get $t))) @@ -227,13 +283,20 @@ (br_if $write (i64.ne (local.get $d) (i64.const 0)))) (if (local.get $negative) (then (array.set $bytes (local.get $s) (i32.const 0) (@char "-")))) - (local.get $s)) + (return_call $caml_string_of_bytes (local.get $s))) (type $chars (array i8)) (func (export "caml_int64_format") (param (ref eq)) (param (ref eq)) (result (ref eq)) (local $d i64) +(@if use-js-string +(@then + (local $fmt externref) +) +(@else + (local $fmt (ref $bytes)) +)) (local $s (ref $bytes)) (local $format (tuple i32 i32 i32 i32 i32)) (local $sign_style i32) (local $alternate i32) (local $signed i32) @@ -242,14 +305,14 @@ (local $i i32) (local $n i64) (local $chars (ref $chars)) - (local.set $s (ref.cast (ref $bytes) (local.get 0))) + (local.set $fmt (call $string_val (local.get 0))) (local.set $d (struct.get $int64 1 (ref.cast (ref $int64) (local.get 1)))) - (if (i32.eq (array.len (local.get $s)) (i32.const 2)) + (if (i32.eq (call $string_length (local.get $fmt)) (i32.const 2)) (then - (if (i32.eq (array.get_u $bytes (local.get $s) (i32.const 1)) + (if (i32.eq (call $string_get (local.get $fmt) (i32.const 1)) (@char "d")) (then (return_call $format_int64_default (local.get $d)))))) - (local.set $format (call $parse_int_format (local.get $s))) + (local.set $format (call $parse_int_format (local.get $fmt))) (local.set $sign_style (tuple.extract 5 0 (local.get $format))) (local.set $alternate (tuple.extract 5 1 (local.get $format))) (local.set $signed (tuple.extract 5 2 (local.get $format))) @@ -315,6 +378,6 @@ (array.set $bytes (local.get $s) (i32.const 1) (select (@char "X") (@char "x") (local.get $uppercase))))))))) - (local.get $s)) + (return_call $caml_string_of_bytes (local.get $s))) ) diff --git a/runtime/wasm/ints.wat b/runtime/wasm/ints.wat index a524ae9a77..eded6ca049 100644 --- a/runtime/wasm/ints.wat +++ b/runtime/wasm/ints.wat @@ -19,8 +19,31 @@ (import "fail" "caml_failwith" (func $caml_failwith (param (ref eq)))) (import "fail" "caml_invalid_argument" (func $caml_invalid_argument (param (ref eq)))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) + +(@if use-js-string +(@then + (import "wasm:js-string" "length" + (func $string_length (param externref) (result i32))) + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + + (func $string_val (param $s (ref eq)) (result externref) + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $s))))) +) +(@else + (func $string_length (param $s (ref $bytes)) (result i32) + (array.len (local.get $s))) + (func $string_get (param $s (ref $bytes)) (param $i i32) (result i32) + (array.get $bytes (local.get $s) (local.get $i))) + (func $string_val (param $s (ref eq)) (result (ref $bytes)) + (ref.cast (ref $bytes) (local.get $s))) +)) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (func (export "caml_format_int") (param (ref eq)) (param (ref eq)) (result (ref eq)) @@ -29,17 +52,24 @@ (i31.get_s (ref.cast (ref i31) (local.get 1))) (i32.const 1))) (func $parse_sign_and_base (export "parse_sign_and_base") - (param $s (ref $bytes)) (result i32 i32 i32 i32) +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (result i32 i32 i32 i32) (local $i i32) (local $len i32) (local $c i32) (local $signedness i32) (local $sign i32) (local $base i32) (local.set $i (i32.const 0)) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (local.set $signedness (i32.const 1)) (local.set $sign (i32.const 1)) (local.set $base (i32.const 10)) (if (i32.ne (local.get $len) (i32.const 0)) (then - (local.set $c (array.get_u $bytes (local.get $s) (i32.const 0))) + (local.set $c (call $string_get (local.get $s) (i32.const 0))) (if (i32.eq (local.get $c) (@char "-")) (then (local.set $sign (i32.const -1)) @@ -47,11 +77,11 @@ (else (if (i32.eq (local.get $c) (@char "+")) (then (local.set $i (i32.const 1)))))))) (if (i32.lt_s (i32.add (local.get $i) (i32.const 1)) (local.get $len)) - (then (if (i32.eq (array.get_u $bytes (local.get $s) (local.get $i)) + (then (if (i32.eq (call $string_get (local.get $s) (local.get $i)) (i32.const 48)) (then (local.set $c - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.add (local.get $i) (i32.const 1)))) (if (i32.or (i32.eq (local.get $c) (@char "X")) (i32.eq (local.get $c) (@char "x"))) @@ -96,13 +126,19 @@ (func $parse_int (export "parse_int") (param $v (ref eq)) (param $nbits i32) (param $errmsg (ref eq)) (result i32) +(@if use-js-string +(@then + (local $s externref) +) +(@else (local $s (ref $bytes)) +)) (local $i i32) (local $len i32) (local $d i32) (local $c i32) (local $signedness i32) (local $sign i32) (local $base i32) (local $res i32) (local $threshold i32) (local $t (tuple i32 i32 i32 i32)) - (local.set $s (ref.cast (ref $bytes) (local.get $v))) - (local.set $len (array.len (local.get $s))) + (local.set $s (call $string_val (local.get $v))) + (local.set $len (call $string_length (local.get $s))) (if (i32.eqz (local.get $len)) (then (call $caml_failwith (local.get $errmsg)))) (local.set $t (call $parse_sign_and_base (local.get $s))) @@ -114,7 +150,7 @@ (if (i32.ge_s (local.get $i) (local.get $len)) (then (call $caml_failwith (local.get $errmsg)))) (local.set $d - (call $parse_digit (array.get_u $bytes (local.get $s) (local.get $i)))) + (call $parse_digit (call $string_get (local.get $s) (local.get $i)))) (if (i32.ge_u (local.get $d) (local.get $base)) (then (call $caml_failwith (local.get $errmsg)))) (local.set $res (local.get $d)) @@ -122,7 +158,7 @@ (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.lt_s (local.get $i) (local.get $len)) (then - (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) + (local.set $c (call $string_get (local.get $s) (local.get $i))) (br_if $loop (i32.eq (local.get $c) (@char "_"))) (local.set $d (call $parse_digit (local.get $c))) (if (i32.ge_u (local.get $d) (local.get $base)) @@ -213,24 +249,31 @@ (br_if $write (local.get $d))) (if (local.get $negative) (then (array.set $bytes (local.get $s) (i32.const 0) (@char "-")))) - (local.get $s)) + (return_call $caml_string_of_bytes (local.get $s))) (@string $format_error "format_int: bad format") (func $parse_int_format (export "parse_int_format") - (param $s (ref $bytes)) (result i32 i32 i32 i32 i32) +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (result i32 i32 i32 i32 i32) (local $i i32) (local $len i32) (local $c i32) (local $sign_style i32) (local $alternate i32) (local $base i32) (local $signed i32) (local $uppercase i32) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (local.set $i (i32.const 1)) (block $return (block $bad_format (br_if $bad_format (i32.lt_u (local.get $len) (i32.const 2))) (br_if $bad_format - (i32.ne (array.get_u $bytes (local.get $s) (i32.const 0)) + (i32.ne (call $string_get (local.get $s) (i32.const 0)) (@char "%"))) - (local.set $c (array.get_u $bytes (local.get $s) (i32.const 1))) + (local.set $c (call $string_get (local.get $s) (i32.const 1))) (if (i32.eq (local.get $c) (@char "+")) (then (local.set $sign_style (i32.const 1)) @@ -244,7 +287,7 @@ (local.set $alternate (i32.const 1)) (local.set $i (i32.add (local.get $i) (i32.const 1))))) (br_if $bad_format (i32.eq (local.get $i) (local.get $len))) - (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) + (local.set $c (call $string_get (local.get $s) (local.get $i))) (if (i32.or (i32.or (i32.eq (local.get $c) (@char "L")) (i32.eq (local.get $c) (@char "l"))) (i32.eq (local.get $c) (@char "n"))) @@ -252,7 +295,7 @@ (local.set $i (i32.add (local.get $i) (i32.const 1))) (br_if $bad_format (i32.eq (local.get $i) (local.get $len))) (local.set $c - (array.get_u $bytes (local.get $s) (local.get $i))))) + (call $string_get (local.get $s) (local.get $i))))) (br_if $bad_format (i32.ne (i32.add (local.get $i) (i32.const 1)) (local.get $len))) (if (i32.or (i32.eq (local.get $c) (@char "d")) @@ -286,6 +329,13 @@ (func $format_int (export "format_int") (param (ref eq)) (param $d i32) (param $small i32) (result (ref eq)) +(@if use-js-string +(@then + (local $fmt externref) +) +(@else + (local $fmt (ref $bytes)) +)) (local $s (ref $bytes)) (local $format (tuple i32 i32 i32 i32 i32)) (local $sign_style i32) (local $alternate i32) (local $signed i32) @@ -294,13 +344,13 @@ (local $i i32) (local $n i32) (local $chars (ref $chars)) - (local.set $s (ref.cast (ref $bytes) (local.get 0))) - (if (i32.eq (array.len (local.get $s)) (i32.const 2)) + (local.set $fmt (call $string_val (local.get 0))) + (if (i32.eq (call $string_length (local.get $fmt)) (i32.const 2)) (then - (if (i32.eq (array.get_u $bytes (local.get $s) (i32.const 1)) + (if (i32.eq (call $string_get (local.get $fmt) (i32.const 1)) (@char "d")) (then (return_call $format_int_default (local.get $d)))))) - (local.set $format (call $parse_int_format (local.get $s))) + (local.set $format (call $parse_int_format (local.get $fmt))) (local.set $sign_style (tuple.extract 5 0 (local.get $format))) (local.set $alternate (tuple.extract 5 1 (local.get $format))) (local.set $signed (tuple.extract 5 2 (local.get $format))) @@ -372,5 +422,5 @@ (array.set $bytes (local.get $s) (i32.const 1) (select (@char "X") (@char "x") (local.get $uppercase))))))))) - (local.get $s)) + (return_call $caml_string_of_bytes (local.get $s))) ) diff --git a/runtime/wasm/io.wat b/runtime/wasm/io.wat index b72f1600b1..a242afa1ca 100644 --- a/runtime/wasm/io.wat +++ b/runtime/wasm/io.wat @@ -72,6 +72,10 @@ (func $dv_get_ui8 (param externref i32) (result i32))) (import "bindings" "dv_set_i8" (func $dv_set_i8 (param externref i32 i32))) + (import "bindings" "ta_blit_from_string" + (func $ta_blit_from_string + (param anyref) (param i32) (param (ref extern)) (param i32) + (param i32))) (import "custom" "custom_compare_id" (func $custom_compare_id (param (ref eq)) (param (ref eq)) (param i32) (result i32))) @@ -88,6 +92,8 @@ (func $caml_handle_sys_error (param externref))) (import "bigarray" "caml_ba_get_data" (func $caml_ba_get_data (param (ref eq)) (result (ref extern)))) + (import "string" "caml_bytes_of_string" + (func $caml_bytes_of_string (param (ref eq)) (result (ref eq)))) (import "bindings" "map_new" (func $map_new (result (ref extern)))) (import "bindings" "map_get" @@ -101,6 +107,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $offset_array (array (mut i64))) (type $compare @@ -114,7 +121,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) @@ -850,6 +863,7 @@ (if (i32.ge_u (local.get $len) (local.get $free)) (then (local.set $len (local.get $free)))) (local.set $buf (struct.get $channel $buffer (local.get $ch))) + (call $ta_set (local.get $buf) (call $ta_subarray (local.get $d) (local.get $pos) (i32.add (local.get $pos) (local.get $len))) @@ -860,6 +874,26 @@ (then (drop (call $caml_flush_partial (local.get $ch))))) (local.get $len)) + (func $caml_putblock_string + (param $ch (ref $channel)) (param $s anyref) (param $pos i32) + (param $len i32) (result i32) + (local $free i32) (local $curr i32) + (local $buf (ref extern)) + (local.set $curr (struct.get $channel $curr (local.get $ch))) + (local.set $free + (i32.sub (struct.get $channel $size (local.get $ch)) (local.get $curr))) + (if (i32.ge_u (local.get $len) (local.get $free)) + (then (local.set $len (local.get $free)))) + (local.set $buf (struct.get $channel $buffer (local.get $ch))) + (call $ta_blit_from_string + (local.get $s) (local.get $pos) + (local.get $buf) (local.get $curr) (local.get $len)) + (struct.set $channel $curr (local.get $ch) + (i32.add (local.get $curr) (local.get $len))) + (if (i32.ge_u (local.get $len) (local.get $free)) + (then (drop (call $caml_flush_partial (local.get $ch))))) + (local.get $len)) + (func (export "caml_really_putblock") (param $ch (ref eq)) (param $s (ref $bytes)) (param $pos i32) (param $len i32) @@ -889,8 +923,34 @@ (local.set $len (i32.sub (local.get $len) (local.get $written))) (br $loop))))) - (export "caml_ml_output_bytes" (func $caml_ml_output)) +(@if use-js-string +(@then (func $caml_ml_output (export "caml_ml_output") + (param $ch (ref eq)) (param $s (ref eq)) (param $vpos (ref eq)) + (param $vlen (ref eq)) (result (ref eq)) + (local $pos i32) (local $len i32) (local $written i32) + (local.set $pos (i31.get_u (ref.cast (ref i31) (local.get $vpos)))) + (local.set $len (i31.get_u (ref.cast (ref i31) (local.get $vlen)))) + (loop $loop + (if (i32.gt_u (local.get $len) (i32.const 0)) + (then + (local.set $written + (call $caml_putblock_string + (ref.cast (ref $channel) (local.get $ch)) + (struct.get $string 0 + (ref.cast (ref $string) (local.get $s))) + (local.get $pos) (local.get $len))) + (local.set $pos (i32.add (local.get $pos) (local.get $written))) + (local.set $len (i32.sub (local.get $len) (local.get $written))) + (br $loop)))) + (call $caml_flush_if_unbuffered (local.get $ch)) + (ref.i31 (i32.const 0))) +) +(@else + (export "caml_ml_output" (func $caml_ml_output_bytes)) +)) + + (func $caml_ml_output_bytes (export "caml_ml_output_bytes") (param $ch (ref eq)) (param $s (ref eq)) (param $vpos (ref eq)) (param $vlen (ref eq)) (result (ref eq)) (local $pos i32) (local $len i32) (local $written i32) diff --git a/runtime/wasm/jslib.wat b/runtime/wasm/jslib.wat index 23542f08e5..a7647a4e90 100644 --- a/runtime/wasm/jslib.wat +++ b/runtime/wasm/jslib.wat @@ -90,8 +90,18 @@ (func $jsstring_of_bytes (param (ref $bytes)) (result anyref))) (import "jsstring" "bytes_of_jsstring" (func $bytes_of_jsstring (param anyref) (result (ref $bytes)))) + (import "jsstring" "jsstring_of_string" + (func $jsstring_of_string (param (ref eq)) (result (ref eq)))) + (import "jsstring" "string_of_jsstring" + (func $string_of_jsstring (param (ref eq)) (result (ref eq)))) + (import "jsstring" "jsbytes_of_bytes" + (func $jsbytes_of_bytes (param (ref $bytes)) (result anyref))) + (import "jsstring" "bytes_of_jsbytes" + (func $bytes_of_jsbytes (param anyref) (result (ref $bytes)))) (import "jsstring" "jsstring_of_subbytes" (func $jsstring_of_subbytes (param (ref $bytes) i32 i32) (result anyref))) + (import "jsstring" "jsstring_of_substring" + (func $jsstring_of_substring (param (ref eq) i32 i32) (result (ref eq)))) (import "int32" "caml_copy_int32" (func $caml_copy_int32 (param i32) (result (ref eq)))) (import "int32" "Int32_val" @@ -105,6 +115,7 @@ (type $float (struct (field f64))) (type $float_array (array (mut f64))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $js (struct (field anyref))) (func $wrap (export "wrap") (param anyref) (result (ref eq)) @@ -261,7 +272,7 @@ (array.get $block (local.get $a) (local.get $i)))) (call $set (local.get $o) (call $unwrap - (call $caml_jsstring_of_bytes + (call $caml_jsstring_of_string (array.get $block (local.get $p) (i32.const 1)))) (call $unwrap (array.get $block (local.get $p) (i32.const 2)))) @@ -450,13 +461,33 @@ (local.get $acc))))))))) (return_call $unwrap (local.get $acc))) - (export "caml_js_from_string" (func $caml_jsstring_of_bytes)) - (func $caml_jsstring_of_bytes (export "caml_jsstring_of_string") + (export "caml_js_from_string" (func $jsstring_of_string)) + +(@if use-js-string +(@then + (export "caml_jsstring_of_string" (func $jsstring_of_string)) + (func $caml_jsstring_of_string (param (ref eq)) (result (ref eq)) + (return_call $jsstring_of_string (local.get 0))) +) +(@else + (func $caml_jsstring_of_string (export "caml_jsstring_of_string") (param (ref eq)) (result (ref eq)) (local $s (ref $bytes)) (local.set $s (ref.cast (ref $bytes) (local.get 0))) (return (struct.new $js (call $jsstring_of_bytes (local.get $s))))) +)) +(@if use-js-string +(@then + (func (export "caml_jsstring_of_substring") + (param $s (ref eq)) (param $i (ref eq)) (param $l (ref eq)) + (result (ref eq)) + (return_call $jsstring_of_substring + (local.get $s) + (i31.get_u (ref.cast (ref i31) (local.get $i))) + (i31.get_u (ref.cast (ref i31) (local.get $l))))) +) +(@else (func (export "caml_jsstring_of_substring") (param $s (ref eq)) (param $i (ref eq)) (param $l (ref eq)) (result (ref eq)) @@ -466,110 +497,50 @@ (ref.cast (ref $bytes) (local.get $s)) (i31.get_u (ref.cast (ref i31) (local.get $i))) (i31.get_u (ref.cast (ref i31) (local.get $l))))))) +)) + +(@if use-js-string +(@then + (func (export "caml_jsbytes_of_string") + (param (ref eq)) (result (ref eq)) + (local.get 0)) +) +(@else + (export "caml_jsbytes_of_string" (func $caml_jsbytes_of_bytes)) +)) - (func $caml_jsbytes_of_bytes (export "caml_jsbytes_of_string") + (func $caml_jsbytes_of_bytes (param (ref eq)) (result (ref eq)) (local $s (ref $bytes)) - (local $s' (ref $bytes)) - (local $l i32) (local $i i32) (local $n i32) (local $c i32) (local.set $s (ref.cast (ref $bytes) (local.get 0))) - (local.set $l (array.len (local.get $s))) - (local.set $i (i32.const 0)) - (local.set $n (i32.const 0)) - (loop $count - (if (i32.lt_u (local.get $i) (local.get $l)) - (then - (if (i32.ge_u (array.get_u $bytes (local.get $s) (local.get $i)) - (i32.const 128)) - (then (local.set $n (i32.add (local.get $n) (i32.const 1))))) - (local.set $i (i32.add (local.get $i) (i32.const 1))) - (br $count)))) - (if (i32.eqz (local.get $n)) - (then - (return - (struct.new $js - (call $jsstring_of_bytes (local.get $s)))))) - (local.set $s' - (array.new $bytes (i32.const 0) - (i32.add (local.get $i) (local.get $n)))) - (local.set $i (i32.const 0)) - (local.set $n (i32.const 0)) - (loop $fill - (if (i32.lt_u (local.get $i) (local.get $l)) - (then - (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) - (if (i32.lt_u (local.get $c) (i32.const 128)) - (then - (array.set $bytes - (local.get $s') (local.get $n) (local.get $c)) - (local.set $n (i32.add (local.get $n) (i32.const 1)))) - (else - (array.set $bytes (local.get $s') - (local.get $n) - (i32.or (i32.shr_u (local.get $c) (i32.const 6)) - (i32.const 0xC0))) - (array.set $bytes (local.get $s') - (i32.add (local.get $n) (i32.const 1)) - (i32.or (i32.const 0x80) - (i32.and (local.get $c) (i32.const 0x3F)))) - (local.set $n (i32.add (local.get $n) (i32.const 2))))) - (local.set $i (i32.add (local.get $i) (i32.const 1))) - (br $fill)))) - (return (struct.new $js (call $jsstring_of_bytes (local.get $s'))))) - + (return (struct.new $js (call $jsbytes_of_bytes (local.get $s))))) + +(@if use-js-string +(@then + (export "caml_js_to_string" (func $string_of_jsstring)) + (export "caml_string_of_jsstring" (func $string_of_jsstring)) + (func $caml_string_of_jsstring (param (ref eq)) (result (ref eq)) + (return_call $string_of_jsstring (local.get 0))) +) +(@else (export "caml_js_to_string" (func $caml_string_of_jsstring)) (func $caml_string_of_jsstring (export "caml_string_of_jsstring") (param $s (ref eq)) (result (ref eq)) (return_call $bytes_of_jsstring (struct.get $js 0 (ref.cast (ref $js) (local.get $s))))) +)) +(@if use-js-string +(@then (func (export "caml_string_of_jsbytes") (param $s (ref eq)) (result (ref eq)) - (local $l i32) (local $i i32) (local $n i32) (local $c i32) - (local $s' (ref $bytes)) (local $s'' (ref $bytes)) - (local.set $s' - (call $bytes_of_jsstring - (struct.get $js 0 (ref.cast (ref $js) (local.get $s))))) - (local.set $l (array.len (local.get $s'))) - (local.set $i (i32.const 0)) - (local.set $n (i32.const 0)) - (loop $count - (if (i32.lt_u (local.get $i) (local.get $l)) - (then - (if (i32.ge_u (array.get_u $bytes (local.get $s') (local.get $i)) - (i32.const 0xC0)) - (then (local.set $n (i32.add (local.get $n) (i32.const 1))))) - (local.set $i (i32.add (local.get $i) (i32.const 1))) - (br $count)))) - (if (i32.eqz (local.get $n)) (then (return (local.get $s')))) - (local.set $s'' - (array.new $bytes (i32.const 0) - (i32.sub (local.get $i) (local.get $n)))) - (local.set $i (i32.const 0)) - (local.set $n (i32.const 0)) - (loop $fill - (if (i32.lt_u (local.get $i) (local.get $l)) - (then - (local.set $c - (array.get_u $bytes (local.get $s') (local.get $i))) - (if (i32.lt_u (local.get $c) (i32.const 0xC0)) - (then - (array.set $bytes - (local.get $s'') (local.get $n) (local.get $c)) - (local.set $i (i32.add (local.get $i) (i32.const 1)))) - (else - (array.set $bytes (local.get $s'') - (local.get $n) - (i32.sub - (i32.add - (i32.shl (local.get $c) (i32.const 6)) - (array.get_u $bytes (local.get $s') - (i32.add (local.get $i) (i32.const 1)))) - (i32.const 0x3080))) - (local.set $i (i32.add (local.get $i) (i32.const 2))))) - (local.set $n (i32.add (local.get $n) (i32.const 1))) - (br $fill)))) - (local.get $s'')) + (local.get 0)) +) +(@else + (func (export "caml_string_of_jsbytes") (param $s (ref eq)) (result (ref eq)) + (return_call $bytes_of_jsbytes + (struct.get $js 0 (ref.cast (ref $js) (local.get $s))))) +)) (func (export "caml_list_to_js_array") (param (ref eq)) (result (ref eq)) @@ -645,7 +616,7 @@ (call $meth_call (local.get $exn) (call $unwrap - (call $caml_jsstring_of_bytes (global.get $toString))) + (call $caml_jsstring_of_string (global.get $toString))) (any.convert_extern (call $new_array (i32.const 0)))))))) (func (export "caml_js_error_option_of_exception") @@ -684,5 +655,13 @@ (func (export "caml_jsoo_flags_use_js_string") (param (ref eq)) (result (ref eq)) - (ref.i31 (i32.const 0))) + (ref.i31 +(@if use-js-string +(@then + (i32.const 1) +) +(@else + (i32.const 0) +)) + )) ) diff --git a/runtime/wasm/jslib_js_of_ocaml.wat b/runtime/wasm/jslib_js_of_ocaml.wat index 5f3c4c14e0..48a2e3f2a3 100644 --- a/runtime/wasm/jslib_js_of_ocaml.wat +++ b/runtime/wasm/jslib_js_of_ocaml.wat @@ -33,6 +33,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $js (struct (field anyref))) (func (export "caml_js_html_escape") (param (ref eq)) (result (ref eq)) (return_call $wrap @@ -42,13 +43,13 @@ (return_call $wrap (call $caml_js_html_entities_js (call $unwrap (local.get 0))))) - (@string $console "console") + (@jsstring $console "console") (func (export "caml_js_get_console") (param (ref eq)) (result (ref eq)) (return_call $caml_js_get (call $caml_js_global (ref.i31 (i32.const 0))) (global.get $console))) - (@string $XMLHttpRequest "XMLHttpRequest") + (@jsstring $XMLHttpRequest "XMLHttpRequest") (func (export "caml_xmlhttprequest_create") (param (ref eq)) (result (ref eq)) (return_call $caml_js_new diff --git a/runtime/wasm/jsstring.wat b/runtime/wasm/jsstring.wat index 4180f7649e..13fd87ff11 100644 --- a/runtime/wasm/jsstring.wat +++ b/runtime/wasm/jsstring.wat @@ -20,11 +20,16 @@ (func $compare_strings (param externref externref) (result i32))) (import "wasm:js-string" "test" (func $is_string (param externref) (result i32))) + (import "wasm:js-string" "length" + (func $string_length (param externref) (result i32))) + (import "wasm:js-string" "substring" + (func $string_sub (param externref i32 i32) (result (ref extern)))) (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (param (ref null $wstring)) (param i32) (param i32) (result (ref extern)))) - + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) (import "wasm:text-decoder" "decodeStringFromUTF8Array" (func $decodeStringFromUTF8Array (param (ref null $bytes)) (param i32) (param i32) @@ -43,8 +48,13 @@ (func $write_string (param anyref) (result i32))) (import "bindings" "append_string" (func $append_string (param anyref) (param anyref) (result anyref))) + (import "js" "caml_utf16_of_utf8" + (func $utf16_of_utf8 (param anyref) (result anyref))) + (import "js" "caml_utf8_of_utf16" + (func $utf8_of_utf16 (param anyref) (result anyref))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $wstring (array (mut i16))) (global $text_converters_available (mut i32) (i32.const 0)) @@ -96,10 +106,12 @@ (func (export "jsstring_test") (param $s anyref) (result i32) (return_call $is_string (extern.convert_any (local.get $s)))) + (func (export "jsstring_length") (param $s anyref) (result i32) + (return_call $string_length (extern.convert_any (local.get $s)))) + (export "jsstring_hash" (func $hash_string)) (func $jsstring_of_subbytes (export "jsstring_of_subbytes") - (export "jsstring_of_substring") ;; compatibility with zarith stubs (param $s (ref $bytes)) (param $pos i32) (param $len i32) (result anyref) (local $i i32) (local $c i32) @@ -133,16 +145,160 @@ (return_call $jsstring_of_subbytes_fallback (local.get $s) (local.get $pos) (local.get $len))) - (func (export "jsstring_of_bytes") (param $s (ref $bytes)) (result anyref) + (func $jsstring_of_bytes (export "jsstring_of_bytes") + (param $s (ref $bytes)) (result anyref) (return_call $jsstring_of_subbytes (local.get $s) (i32.const 0) (array.len (local.get $s)))) - (func (export "bytes_of_jsstring") (param $s anyref) (result (ref $bytes)) + (func $bytes_of_jsstring (export "bytes_of_jsstring") + (param $s anyref) (result (ref $bytes)) (if (global.get $text_converters_available) (then (return_call $encodeStringToUTF8Array (extern.convert_any (local.get $s))))) - (return_call $string_of_jsstring_fallback (local.get $s))) + (return_call $bytes_of_jsstring_fallback (local.get $s))) + + (func $string_is_ascii (param $vs (ref eq)) (result i32) + (local $s externref) (local $len i32) (local $i i32) + (local.set $s + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $vs))))) + (local.set $len (call $string_length (local.get $s))) + (loop $loop + (if (i32.lt_u (local.get $i) (local.get $len)) + (then + (if (i32.ge_u (call $string_get (local.get $s) (local.get $i)) + (i32.const 128)) + (then (return (i32.const 0)))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $loop)))) + (i32.const 1)) + + (func $jsstring_of_string (export "jsstring_of_string") + (param $s (ref eq)) (result (ref eq)) + (if (result (ref eq)) (call $string_is_ascii (local.get $s)) + (then + (local.get $s)) + (else + (return + (struct.new $string + (call $utf16_of_utf8 + (struct.get $string 0 + (ref.cast (ref $string) (local.get $s))))))))) + + (func (export "jsstring_of_substring") + (param $s (ref eq)) (param $i i32) (param $l i32) (result (ref eq)) + (return_call $jsstring_of_string + (struct.new $string + (any.convert_extern + (call $string_sub + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $s)))) + (local.get $i) + (i32.add (local.get $i) (local.get $l))))))) + + (func (export "string_of_jsstring") + (param $s (ref eq)) (result (ref eq)) + (if (result (ref eq)) (call $string_is_ascii (local.get $s)) + (then + (local.get $s)) + (else + (return + (struct.new $string + (call $utf8_of_utf16 + (struct.get $string 0 + (ref.cast (ref $string) (local.get $s))))))))) + + (func (export "jsbytes_of_bytes") (param $s (ref $bytes)) (result anyref) + (local $s' (ref $bytes)) + (local $l i32) (local $i i32) (local $n i32) (local $c i32) + (local.set $l (array.len (local.get $s))) + (local.set $i (i32.const 0)) + (local.set $n (i32.const 0)) + (loop $count + (if (i32.lt_u (local.get $i) (local.get $l)) + (then + (if (i32.ge_u (array.get_u $bytes (local.get $s) (local.get $i)) + (i32.const 128)) + (then (local.set $n (i32.add (local.get $n) (i32.const 1))))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $count)))) + (if (i32.eqz (local.get $n)) + (then + (return_call $jsstring_of_bytes (local.get $s)))) + (local.set $s' + (array.new $bytes (i32.const 0) + (i32.add (local.get $i) (local.get $n)))) + (local.set $i (i32.const 0)) + (local.set $n (i32.const 0)) + (loop $fill + (if (i32.lt_u (local.get $i) (local.get $l)) + (then + (local.set $c (array.get_u $bytes (local.get $s) (local.get $i))) + (if (i32.lt_u (local.get $c) (i32.const 128)) + (then + (array.set $bytes + (local.get $s') (local.get $n) (local.get $c)) + (local.set $n (i32.add (local.get $n) (i32.const 1)))) + (else + (array.set $bytes (local.get $s') + (local.get $n) + (i32.or (i32.shr_u (local.get $c) (i32.const 6)) + (i32.const 0xC0))) + (array.set $bytes (local.get $s') + (i32.add (local.get $n) (i32.const 1)) + (i32.or (i32.const 0x80) + (i32.and (local.get $c) (i32.const 0x3F)))) + (local.set $n (i32.add (local.get $n) (i32.const 2))))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $fill)))) + (return_call $jsstring_of_bytes (local.get $s'))) + + (func (export "bytes_of_jsbytes") (param $s anyref) (result (ref $bytes)) + (local $l i32) (local $i i32) (local $n i32) (local $c i32) + (local $s' (ref $bytes)) (local $s'' (ref $bytes)) + (local.set $s' (call $bytes_of_jsstring (local.get $s))) + (local.set $l (array.len (local.get $s'))) + (local.set $i (i32.const 0)) + (local.set $n (i32.const 0)) + (loop $count + (if (i32.lt_u (local.get $i) (local.get $l)) + (then + (if (i32.ge_u (array.get_u $bytes (local.get $s') (local.get $i)) + (i32.const 0xC0)) + (then (local.set $n (i32.add (local.get $n) (i32.const 1))))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $count)))) + (if (i32.eqz (local.get $n)) (then (return (local.get $s')))) + (local.set $s'' + (array.new $bytes (i32.const 0) + (i32.sub (local.get $i) (local.get $n)))) + (local.set $i (i32.const 0)) + (local.set $n (i32.const 0)) + (loop $fill + (if (i32.lt_u (local.get $i) (local.get $l)) + (then + (local.set $c + (array.get_u $bytes (local.get $s') (local.get $i))) + (if (i32.lt_u (local.get $c) (i32.const 0xC0)) + (then + (array.set $bytes + (local.get $s'') (local.get $n) (local.get $c)) + (local.set $i (i32.add (local.get $i) (i32.const 1)))) + (else + (array.set $bytes (local.get $s'') + (local.get $n) + (i32.sub + (i32.add + (i32.shl (local.get $c) (i32.const 6)) + (array.get_u $bytes (local.get $s') + (i32.add (local.get $i) (i32.const 1)))) + (i32.const 0x3080))) + (local.set $i (i32.add (local.get $i) (i32.const 2))))) + (local.set $n (i32.add (local.get $n) (i32.const 1))) + (br $fill)))) + (local.get $s'')) ;; Fallback implementation of string conversion functions @@ -210,7 +366,7 @@ (struct (field $s (ref $bytes)) (field $next (ref null $stack)))) (global $stack (mut (ref null $stack)) (ref.null $stack)) - (func $string_of_jsstring_fallback (param $s anyref) (result (ref $bytes)) + (func $bytes_of_jsstring_fallback (param $s anyref) (result (ref $bytes)) (local $ofs i32) (local $len i32) (local $s' (ref $bytes)) (local $s'' (ref $bytes)) (local $item (ref $stack)) @@ -257,4 +413,106 @@ (local.set $s (array.new $bytes (i32.const 0) (local.get $len))) (call $read_from_buffer (local.get $s) (i32.const 0) (local.get $len)) (global.set $stack (struct.new $stack (local.get $s) (global.get $stack)))) + + (func $utf16_to_utf8 + (param $s externref) (param $l i32) (param $b (ref $wstring)) (result i32) + (local $i i32) (local $j i32) (local $c i32) (local $d i32) + (loop $loop + (if (i32.lt_u (local.get $i) (local.get $l)) + (then + (local.set $c (call $string_get (local.get $s) (local.get $i))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (if (i32.lt_u (local.get $c) (i32.const 0x80)) + (then + (array.set $wstring + (local.get $b) (local.get $j) (local.get $c)) + (local.set $j (i32.add (local.get $j) (i32.const 1))) + (br $loop))) + (if (i32.lt_u (local.get $c) (i32.const 0x800)) + (then + (array.set $wstring + (local.get $b) (local.get $j) + (i32.or (i32.const 0xC0) + (i32.shr_u (local.get $c) (i32.const 6)))) + (array.set $wstring + (local.get $b) + (i32.add (local.get $j) (i32.const 1)) + (i32.or (i32.const 0x80) + (i32.and (local.get $c) (i32.const 0x3F)))) + (local.set $j (i32.add (local.get $j) (i32.const 2))) + (br $loop))) + (if (i32.and + (i32.ge_u (local.get $c) (i32.const 0xD800)) + (i32.lt_u (local.get $c) (i32.const 0xE000))) + (then + (if (i32.and + (i32.lt_u (local.get $c) (i32.const 0xDC00)) + (i32.lt_u (local.get $i) (local.get $l))) + (then + (local.set $d + (call $string_get (local.get $s) (local.get $i))) + (if (i32.and + (i32.ge_u (local.get $c) (i32.const 0xDC00)) + (i32.lt_u (local.get $c) (i32.const 0xE000))) + (then + (local.set $i + (i32.add (local.get $i) (i32.const 1))) + (local.set $c + (i32.sub + (i32.add + (i32.shl + (local.get $c) + (i32.const 10)) + (local.get $d)) + (i32.const 0x35fdc00))) + (array.set $wstring + (local.get $b) (local.get $j) + (i32.or (i32.const 0xE0) + (i32.shr_u (local.get $c) + (i32.const 18)))) + (array.set $wstring + (local.get $b) + (i32.add (local.get $j) (i32.const 1)) + (i32.or (i32.const 0x80) + (i32.and + (i32.shr_u (local.get $c) + (i32.const 12)) + (i32.const 0x3F)))) + (array.set $wstring + (local.get $b) + (i32.add (local.get $j) (i32.const 2)) + (i32.or (i32.const 0x80) + (i32.and + (i32.shr_u (local.get $c) + (i32.const 6)) + (i32.const 0x3F)))) + (array.set $wstring + (local.get $b) + (i32.add (local.get $j) (i32.const 3)) + (i32.or (i32.const 0x80) + (i32.and (local.get $c) + (i32.const 0x3F)))) + (local.set $j + (i32.add (local.get $j) (i32.const 4))) + (br $loop))))) + ;; replacement character + (local.set $c (i32.const 0xFFFD)))) + (array.set $wstring + (local.get $b) (local.get $j) + (i32.or (i32.const 0xE0) + (i32.shr_u (local.get $c) (i32.const 12)))) + (array.set $wstring + (local.get $b) + (i32.add (local.get $j) (i32.const 1)) + (i32.or (i32.const 0x80) + (i32.and (i32.shr_u (local.get $c) (i32.const 6)) + (i32.const 0x3F)))) + (array.set $wstring + (local.get $b) + (i32.add (local.get $j) (i32.const 2)) + (i32.or (i32.const 0x80) + (i32.and (local.get $c) (i32.const 0x3F)))) + (local.set $j (i32.add (local.get $j) (i32.const 3))) + (br $loop)))) + (local.get $j)) ) diff --git a/runtime/wasm/lexing.wat b/runtime/wasm/lexing.wat index 5016d8a379..060b75d5bf 100644 --- a/runtime/wasm/lexing.wat +++ b/runtime/wasm/lexing.wat @@ -20,15 +20,38 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) - (func $get (param $a (ref eq)) (param $i i32) (result i32) - (local $s (ref $bytes)) - (local.set $s (ref.cast (ref $bytes) (local.get $a))) +(@if use-js-string +(@then + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + + (func $string_val (param $s (ref eq)) (result externref) + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $s))))) +) +(@else + (func $string_get (param $s (ref $bytes)) (param $i i32) (result i32) + (array.get $bytes (local.get $s) (local.get $i))) + (func $string_val (param $s (ref eq)) (result (ref $bytes)) + (ref.cast (ref $bytes) (local.get $s))) +)) + + (func $get +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $i i32) (result i32) (local.set $i (i32.add (local.get $i) (local.get $i))) (i32.extend16_s - (i32.or (array.get_u $bytes (local.get $s) (local.get $i)) + (i32.or (call $string_get (local.get $s) (local.get $i)) (i32.shl - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.add (local.get $i) (i32.const 1))) (i32.const 8))))) @@ -65,12 +88,23 @@ (local $buffer (ref $bytes)) (local $vpos (ref eq)) (local $action (ref eq)) (local $pos i32) (local $base i32) (local $backtrk i32) +(@if use-js-string +(@then + (local $lex_base externref) + (local $lex_backtrk externref) + (local $lex_check externref) + (local $lex_check_code externref) + (local $lex_trans externref) + (local $lex_default externref) +) +(@else (local $lex_base (ref $bytes)) (local $lex_backtrk (ref $bytes)) (local $lex_check (ref $bytes)) (local $lex_check_code (ref $bytes)) (local $lex_trans (ref $bytes)) (local $lex_default (ref $bytes)) +)) (local.set $tbl (ref.cast (ref $block) (local.get $vtbl))) (local.set $lexbuf (ref.cast (ref $block) (local.get $vlexbuf))) (local.set $state @@ -91,22 +125,22 @@ (else (local.set $state (i32.sub (i32.const -1) (local.get $state))))) (local.set $lex_base - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_base)))) (local.set $lex_backtrk - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_backtrk)))) (local.set $lex_check - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_check)))) (local.set $lex_check_code - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_check_code)))) (local.set $lex_trans - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_trans)))) (local.set $lex_default - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_default)))) (loop $loop (local.set $base (call $get (local.get $lex_base) (local.get $state))) @@ -182,7 +216,14 @@ (br $loop))) (func $run_mem - (param $s (ref $bytes)) (param $i i32) (param $lexbuf (ref $block)) +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $i i32) (param $lexbuf (ref $block)) (param $curr_pos (ref eq)) (local $dst i32) (local $src i32) (local $mem (ref $block)) @@ -190,11 +231,11 @@ (ref.cast (ref $block) (array.get $block (local.get $lexbuf) (global.get $lex_mem)))) (loop $loop - (local.set $dst (array.get_u $bytes (local.get $s) (local.get $i))) + (local.set $dst (call $string_get (local.get $s) (local.get $i))) (if (i32.eq (local.get $dst) (i32.const 0xff)) (then (return))) (local.set $src - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.add (local.get $i) (i32.const 1)))) (local.set $i (i32.add (local.get $i) (i32.const 2))) (array.set $block (local.get $mem) @@ -208,7 +249,14 @@ (br $loop))) (func $run_tag - (param $s (ref $bytes)) (param $i i32) (param $lexbuf (ref $block)) +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $i i32) (param $lexbuf (ref $block)) (return_call $run_mem (local.get $s) (local.get $i) (local.get $lexbuf) (ref.i31 (i32.const -1)))) @@ -224,6 +272,21 @@ (local $vpos (ref eq)) (local $action (ref eq)) (local $pos i32) (local $base i32) (local $backtrk i32) (local $pc_off i32) (local $base_code i32) +(@if use-js-string +(@then + (local $lex_code externref) + (local $lex_base externref) + (local $lex_base_code externref) + (local $lex_backtrk externref) + (local $lex_backtrk_code externref) + (local $lex_check externref) + (local $lex_check_code externref) + (local $lex_trans externref) + (local $lex_trans_code externref) + (local $lex_default externref) + (local $lex_default_code externref) +) +(@else (local $lex_code (ref $bytes)) (local $lex_base (ref $bytes)) (local $lex_base_code (ref $bytes)) @@ -235,6 +298,7 @@ (local $lex_trans_code (ref $bytes)) (local $lex_default (ref $bytes)) (local $lex_default_code (ref $bytes)) +)) (local.set $tbl (ref.cast (ref $block) (local.get $vtbl))) (local.set $lexbuf (ref.cast (ref $block) (local.get $vlexbuf))) (local.set $state @@ -255,37 +319,37 @@ (else (local.set $state (i32.sub (i32.const -1) (local.get $state))))) (local.set $lex_code - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_code)))) (local.set $lex_base - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_base)))) (local.set $lex_base_code - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_base_code)))) (local.set $lex_backtrk - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_backtrk)))) (local.set $lex_backtrk_code - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_backtrk_code)))) (local.set $lex_check - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_check)))) (local.set $lex_check_code - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_check_code)))) (local.set $lex_trans - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_trans)))) (local.set $lex_trans_code - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_trans_code)))) (local.set $lex_default - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_default)))) (local.set $lex_default_code - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tbl) (global.get $lex_default_code)))) (loop $loop (local.set $base (call $get (local.get $lex_base) (local.get $state))) diff --git a/runtime/wasm/marshal.wat b/runtime/wasm/marshal.wat index 4aa53e5936..0fe44401f8 100644 --- a/runtime/wasm/marshal.wat +++ b/runtime/wasm/marshal.wat @@ -48,11 +48,34 @@ (func $caml_init_custom_operations)) (import "custom" "caml_find_custom_operations" (func $caml_find_custom_operations - (param (ref $bytes)) (result (ref null $custom_operations)))) + (param (ref eq)) (result (ref null $custom_operations)))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) + (import "string" "caml_bytes_of_string" + (func $caml_bytes_of_string (param (ref eq)) (result (ref eq)))) + (import "jsstring" "jsstring_test" + (func $jsstring_test (param anyref) (result i32))) + (import "jsstring" "jsstring_length" + (func $jsstring_length (param anyref) (result i32))) + (import "string" "caml_blit_string" + (func $caml_blit_string + (param (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) + (result (ref eq)))) (@string $input_val_from_string "input_value_from_string") +(@if use-js-string +(@then + (func (export "caml_input_value_from_string") + (param $vstr (ref eq)) (param $vofs (ref eq)) (result (ref eq)) + ;; It would be better to parse the header and extract just the + ;; relevant substring + (return_call $caml_input_value_from_bytes + (call $caml_bytes_of_string (local.get $vstr)) (local.get $vofs))) +) +(@else (export "caml_input_value_from_string" (func $caml_input_value_from_bytes)) +)) (func $caml_input_value_from_bytes (export "caml_input_value_from_bytes") (param $vstr (ref eq)) (param $vofs (ref eq)) (result (ref eq)) (local $str (ref $bytes)) @@ -132,6 +155,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $float (struct (field f64))) (type $float_array (array (mut f64))) (type $js (struct (field anyref))) @@ -150,7 +174,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) @@ -427,8 +457,8 @@ (br_on_null $unknown (call $caml_find_custom_operations - (call $readstr - (local.get $s))))) + (call $caml_string_of_bytes + (call $readstr (local.get $s)))))) (block $no_length (if (i32.eq (local.get $code) (global.get $CODE_CUSTOM_FIXED)) (then @@ -664,7 +694,7 @@ ;; read_string (local.set $str (array.new $bytes (i32.const 0) (local.get $len))) (call $readblock (local.get $s) (local.get $str)) - (local.set $v (local.get $str)) + (local.set $v (call $caml_string_of_bytes (local.get $str))) (call $register_object (local.get $s) (local.get $v)) (br $done)) ;; read_block @@ -995,6 +1025,19 @@ (struct.get $extern_state $buf (local.get $s)) (local.get $pos) (local.get $str) (i32.const 0) (local.get $len))) + (func $writestring + (param $s (ref $extern_state)) (param $str anyref) (param $len i32) + (local $pos i32) + (local.set $len (call $jsstring_length (local.get $str))) + (local.set $pos + (call $reserve_extern_output (local.get $s) (local.get $len))) + (drop + (call $caml_blit_string ;; ZZZ lower level func? + (struct.new $js (local.get $str)) (ref.i31 (i32.const 0)) + (struct.get $extern_state $buf (local.get $s)) + (ref.i31 (local.get $pos)) + (ref.i31 (local.get $len))))) + (func $writefloat (param $s (ref $extern_state)) (param $f f64) (local $pos i32) (local $buf (ref $bytes)) (local $d i64) (local $i i32) @@ -1121,7 +1164,7 @@ (i32.or (local.get $tag) (i32.shl (local.get $sz) (i32.const 10))))))) - (func $extern_string (param $s (ref $extern_state)) (param $v (ref $bytes)) + (func $extern_bytes (param $s (ref $extern_state)) (param $v (ref $bytes)) (local $len i32) (local.set $len (array.len (local.get $v))) (if (i32.lt_u (local.get $len) (i32.const 0x20)) @@ -1137,6 +1180,22 @@ (local.get $len)))))) (call $writeblock (local.get $s) (local.get $v))) + (func $extern_string (param $s (ref $extern_state)) (param $v anyref) + (local $len i32) + (local.set $len (call $jsstring_length (local.get $v))) + (if (i32.lt_u (local.get $len) (i32.const 0x20)) + (then + (call $write (local.get $s) + (i32.add (global.get $PREFIX_SMALL_STRING) (local.get $len)))) + (else (if (i32.lt_u (local.get $len) (i32.const 0x100)) + (then + (call $writecode8 (local.get $s) (global.get $CODE_STRING8) + (local.get $len))) + (else + (call $writecode32 (local.get $s) (global.get $CODE_STRING32) + (local.get $len)))))) + (call $writestring (local.get $s) (local.get $v) (local.get $len))) + (func $extern_float (param $s (ref $extern_state)) (param $v f64) (call $write (local.get $s) (global.get $CODE_DOUBLE_LITTLE)) (call $writefloat (local.get $s) (local.get $v))) @@ -1163,6 +1222,10 @@ (local $fixed_length (ref $fixed_length)) (local $pos i32) (local $buf (ref $bytes)) (local $r (tuple i32 i32)) +(@if use-js-string +(@then + (local $id anyref) +)) (local.set $ops (struct.get $custom 0 (local.get $v))) (block $abstract (local.set $serialize @@ -1174,8 +1237,18 @@ (struct.get $custom_operations $fixed_length (local.get $ops)))) (call $write (local.get $s) (global.get $CODE_CUSTOM_FIXED)) +(@if use-js-string +(@then + (local.set $id + (struct.get $string 0 + (struct.get $custom_operations $id (local.get $ops)))) + (call $writestring (local.get $s) (local.get $id) + (call $jsstring_length (local.get $id))) +) +(@else (call $writeblock (local.get $s) (struct.get $custom_operations $id (local.get $ops))) +)) (call $write (local.get $s) (i32.const 0)) (local.set $r (call_ref $serialize @@ -1196,8 +1269,18 @@ (return (local.get $r))) ;; variable length (call $write (local.get $s) (global.get $CODE_CUSTOM_LEN)) +(@if use-js-string +(@then + (local.set $id + (struct.get $string 0 + (struct.get $custom_operations $id (local.get $ops)))) + (call $writestring (local.get $s) (local.get $id) + (call $jsstring_length (local.get $id))) +) +(@else (call $writeblock (local.get $s) (struct.get $custom_operations $id (local.get $ops))) +)) (call $write (local.get $s) (i32.const 0)) (local.set $pos (call $reserve_extern_output (local.get $s) (i32.const 12))) @@ -1227,6 +1310,7 @@ (local $hd i32) (local $tag i32) (local $sz i32) (local $pos i32) (local $r (tuple i32 i32)) + (local $js anyref) (loop $loop (block $next_item (drop (block $not_int (result (ref eq)) @@ -1286,7 +1370,7 @@ (local.set $str (br_on_cast_fail $not_string (ref eq) (ref $bytes) (local.get $v))) - (call $extern_string (local.get $s) (local.get $str)) + (call $extern_bytes (local.get $s) (local.get $str)) (local.set $sz (array.len (local.get $str))) (call $extern_size (local.get $s) (i32.add (i32.const 1) @@ -1328,9 +1412,25 @@ (then (call $caml_invalid_argument (global.get $func_value)))) (if (call $caml_is_continuation (local.get $v)) (then (call $caml_invalid_argument (global.get $cont_value)))) - (if (ref.test (ref $js) (local.get $v)) - (then (call $caml_invalid_argument (global.get $js_value)))) - (call $caml_invalid_argument (global.get $abstract_value))) + (drop (block $not_js (result (ref eq)) + (local.set $js + (struct.get $js 0 + (br_on_cast_fail $not_js (ref eq) (ref $js) + (local.get $v)))) + (if (call $jsstring_test (local.get $js)) + (then + (call $extern_string (local.get $s) (local.get $js)) + (local.set $sz (call $jsstring_length (local.get $js))) + (call $extern_size (local.get $s) + (i32.add (i32.const 1) + (i32.shr_u (local.get $sz) (i32.const 2))) + (i32.add (i32.const 1) + (i32.shr_u (local.get $sz) (i32.const 3)))) + (br $next_item))) + (call $caml_invalid_argument (global.get $js_value)) + (ref.i31 (i32.const 0)))) + (call $caml_invalid_argument (global.get $abstract_value)) + ) ;; next_item (block $done (local.set $item (br_on_null $done (local.get $sp))) @@ -1398,8 +1498,18 @@ (struct.get $extern_state $size_64 (local.get $s))) (tuple.make 3 (local.get $len) (local.get $header) (local.get $s))) +(@if use-js-string +(@then (func (export "caml_output_value_to_string") - (export "caml_output_value_to_bytes") + (param $v (ref eq)) (param $flags (ref eq)) (result (ref eq)) + (return_call $caml_string_of_bytes + (call $caml_output_value_to_bytes (local.get $v) (local.get $flags)))) +) +(@else + (export "caml_output_value_to_string" (func $caml_output_value_to_bytes)) +)) + + (func $caml_output_value_to_bytes (export "caml_output_value_to_bytes") (param $v (ref eq)) (param $flags (ref eq)) (result (ref eq)) (local $r (tuple i32 (ref $bytes) (ref $extern_state))) (local $blk (ref $output_block)) (local $pos i32) (local $len i32) diff --git a/runtime/wasm/md5.wat b/runtime/wasm/md5.wat index 671de14964..6969ecd80c 100644 --- a/runtime/wasm/md5.wat +++ b/runtime/wasm/md5.wat @@ -21,6 +21,10 @@ (param (ref eq)) (param (ref $bytes)) (param i32) (param i32) (result i32))) (import "fail" "caml_raise_end_of_file" (func $caml_raise_end_of_file)) + (import "string" "caml_bytes_of_string" + (func $caml_bytes_of_string (param (ref eq)) (result (ref eq)))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) (type $bytes (array (mut i8))) (type $int_array (array (mut i32))) @@ -32,7 +36,19 @@ (field (ref $int_array)) ;; buffer (field (ref $bytes)))) ;; intermediate buffer - (func (export "caml_md5_string") (export "caml_md5_bytes") +(@if use-js-string +(@then + (func (export "caml_md5_string") + (param (ref eq)) (param (ref eq)) (param (ref eq)) (result (ref eq)) + (return_call $caml_md5_bytes + (call $caml_bytes_of_string (local.get 0)) + (local.get 1) (local.get 2))) +) +(@else + (export "caml_md5_string" (func $caml_md5_bytes)) +)) + + (func $caml_md5_bytes (export "caml_md5_bytes") (param (ref eq)) (param (ref eq)) (param (ref eq)) (result (ref eq)) (local $ctx (ref $context)) (local.set $ctx (call $MD5Init)) @@ -484,7 +500,7 @@ (local.get $input) (local.get $input_pos) (local.get $input_len))))) - (func $MD5Final (param $ctx (ref $context)) (result (ref $bytes)) + (func $MD5Final (param $ctx (ref $context)) (result (ref eq)) (local $in_buf i32) (local $i i32) (local $len i64) (local $w (ref $int_array)) (local $buffer (ref $bytes)) (local $res (ref $bytes)) @@ -547,5 +563,5 @@ (i32.shl (local.get $i) (i32.const 3)))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (br_if $loop (i32.lt_u (local.get $i) (i32.const 16)))) - (local.get $res)) + (return_call $caml_string_of_bytes (local.get $res))) ) diff --git a/runtime/wasm/obj.wat b/runtime/wasm/obj.wat index 5e06a4a5ed..535a0f9fa6 100644 --- a/runtime/wasm/obj.wat +++ b/runtime/wasm/obj.wat @@ -29,8 +29,13 @@ (func $caml_cps_trampoline (param (ref eq) (ref eq)) (result (ref eq)))) )) + (import "jsstring" "jsstring_test" + (func $jsstring_test (param anyref) (result i32))) + (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) + (type $js (struct (field anyref))) (type $float (struct (field f64))) (type $float_array (array (mut f64))) (type $function_1 (func (param (ref eq) (ref eq)) (result (ref eq)))) @@ -239,6 +244,8 @@ (struct.get $float 0 (br_on_cast_fail $not_float (ref eq) (ref $float) (local.get 0))))))) + (if (ref.test (ref $js) (local.get 0)) + (then (return (local.get 0)))) (call $caml_dup_custom (local.get 0))) (func (export "caml_obj_with_tag") @@ -282,6 +289,13 @@ (then (return (ref.i31 (global.get $closure_tag))))) (if (call $caml_is_continuation (local.get $v)) (then (return (ref.i31 (global.get $cont_tag))))) + (drop (block $not_string (result (ref eq)) + (if (call $jsstring_test + (struct.get $js 0 + (br_on_cast_fail $not_string (ref eq) (ref $js) + (local.get $v)))) + (then (return (ref.i31 (global.get $string_tag))))) + (ref.i31 (i32.const 0)))) (ref.i31 (global.get $abstract_tag))) (func (export "caml_obj_make_forward") diff --git a/runtime/wasm/parsing.wat b/runtime/wasm/parsing.wat index 686f411145..47670d534a 100644 --- a/runtime/wasm/parsing.wat +++ b/runtime/wasm/parsing.wat @@ -29,19 +29,64 @@ (import "float" "caml_format_float" (func $caml_format_float (param (ref eq)) (param (ref eq)) (result (ref eq)))) + (import "string" "caml_string_length" + (func $caml_string_length (param (ref eq)) (result i32))) (type $float (struct (field f64))) (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) - (func $get (param $a (ref eq)) (param $i i32) (result i32) - (local $s (ref $bytes)) - (local.set $s (ref.cast (ref $bytes) (local.get $a))) +(@if use-js-string +(@then + (import "wasm:js-string" "substring" + (func $string_substring (param externref i32 i32) (result (ref extern)))) + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + + (func $string_sub + (param $s externref) (param $i i32) (param $l i32) (result (ref eq)) + (struct.new $string + (any.convert_extern + (call $string_substring + (local.get $s) + (local.get $i) + (i32.add (local.get $i) (local.get $l)))))) + + (func $string_val (param $s (ref eq)) (result externref) + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $s))))) +) +(@else + (func $string_sub + (param $s (ref $bytes)) (param $i i32) (param $l i32) (result (ref eq)) + (local $s' (ref $bytes)) + (local.set $s' (array.new $bytes (i32.const 0) (local.get $l))) + (array.copy $bytes $bytes + (local.get $s') (i32.const 0) + (local.get $s) (local.get $i) + (local.get $l)) + (local.get $s')) + (func $string_get (param $s (ref $bytes)) (param $i i32) (result i32) + (array.get $bytes (local.get $s) (local.get $i))) + (func $string_val (param $s (ref eq)) (result (ref $bytes)) + (ref.cast (ref $bytes) (local.get $s))) +)) + + (func $get +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $i i32) (result i32) (local.set $i (i32.add (local.get $i) (local.get $i))) (i32.extend16_s - (i32.or (array.get_u $bytes (local.get $s) (local.get $i)) + (i32.or (call $string_get (local.get $s) (local.get $i)) (i32.shl - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.add (local.get $i) (i32.const 1))) (i32.const 8))))) @@ -100,11 +145,19 @@ (global $tbl_names_const i32 (i32.const 15)) (global $tbl_names_block i32 (i32.const 16)) - (func $strlen (param $s (ref $bytes)) (param $p i32) (result i32) + (func $strlen +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $p i32) (result i32) (local $i i32) (local.set $i (local.get $p)) (loop $loop - (if (i32.ne (array.get_u $bytes (local.get $s) (local.get $i)) + (if (i32.ne (call $string_get (local.get $s) (local.get $i)) (i32.const 0)) (then (local.set $i (i32.add (local.get $i) (i32.const 1))) @@ -115,11 +168,17 @@ (func $token_name (param $vnames (ref eq)) (param $number i32) (result (ref eq)) - (local $names (ref $bytes)) (local $i i32) (local $len i32) - (local $name (ref $bytes)) - (local.set $names (ref.cast (ref $bytes) (local.get $vnames))) +(@if use-js-string +(@then + (local $names externref) +) +(@else + (local $names (ref $bytes)) +)) + (local $i i32) (local $len i32) + (local.set $names (call $string_val (local.get $vnames))) (loop $loop - (if (i32.eqz (array.get_u $bytes (local.get $names) (local.get $i))) + (if (i32.eqz (call $string_get (local.get $names) (local.get $i))) (then (return (global.get $unknown_token)))) (if (i32.ne (local.get $number) (i32.const 0)) (then @@ -130,19 +189,14 @@ (local.set $number (i32.sub (local.get $number) (i32.const 1))) (br $loop)))) (local.set $len (call $strlen (local.get $names) (local.get $i))) - (local.set $name (array.new $bytes (i32.const 0) (local.get $len))) - (array.copy $bytes $bytes - (local.get $name) (i32.const 0) - (local.get $names) (local.get $i) (local.get $len)) - (local.get $name)) + (return_call $string_sub + (local.get $names) (local.get $i) (local.get $len))) - (func $output (param (ref eq)) - (local $s (ref $bytes)) - (local.set $s (ref.cast (ref $bytes) (local.get 0))) + (func $output (param $s (ref eq)) (drop (call $caml_ml_output (global.get $caml_stderr) (local.get $s) (ref.i31 (i32.const 0)) - (ref.i31 (array.len (local.get $s)))))) + (ref.i31 (call $caml_string_length (local.get $s)))))) (func $output_nl (drop @@ -151,6 +205,8 @@ (ref.i31 (i32.const 0)) (ref.i31 (i32.const 1)))) (drop (call $caml_ml_flush (global.get $caml_stderr)))) + (@string $int_format "%d") + (func $output_int (param i32) (call $output (call $caml_format_int (@string "%d") (ref.i31 (local.get 0))))) @@ -218,6 +274,19 @@ (local $errflag i32) (local $tables (ref $block)) (local $env (ref $block)) (local $cmd i32) (local $arg (ref $block)) +(@if use-js-string +(@then + (local $tbl_defred externref) + (local $tbl_sindex externref) + (local $tbl_check externref) + (local $tbl_rindex externref) + (local $tbl_table externref) + (local $tbl_len externref) + (local $tbl_lhs externref) + (local $tbl_gindex externref) + (local $tbl_dgoto externref) +) +(@else (local $tbl_defred (ref $bytes)) (local $tbl_sindex (ref $bytes)) (local $tbl_check (ref $bytes)) @@ -227,33 +296,34 @@ (local $tbl_lhs (ref $bytes)) (local $tbl_gindex (ref $bytes)) (local $tbl_dgoto (ref $bytes)) +)) (local.set $tables (ref.cast (ref $block) (local.get $vtables))) (local.set $tbl_defred - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_defred)))) (local.set $tbl_sindex - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_sindex)))) (local.set $tbl_check - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_check)))) (local.set $tbl_rindex - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_rindex)))) (local.set $tbl_table - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_table)))) (local.set $tbl_len - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_len)))) (local.set $tbl_lhs - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_lhs)))) (local.set $tbl_gindex - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_gindex)))) (local.set $tbl_dgoto - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $tables) (global.get $tbl_dgoto)))) (local.set $env (ref.cast (ref $block) (local.get $venv))) (local.set $cmd (i31.get_s (ref.cast (ref i31) (local.get $vcmd)))) @@ -454,8 +524,7 @@ (br $next))))))) (if (global.get $caml_parser_trace) (then - (call $output - (global.get $discarding_state)) + (call $output (global.get $discarding_state)) (call $output_int (local.get $state1)) (call $output_nl))) (if (i32.le_s (local.get $sp) diff --git a/runtime/wasm/printexc.wat b/runtime/wasm/printexc.wat index c39df76412..839aea26f8 100644 --- a/runtime/wasm/printexc.wat +++ b/runtime/wasm/printexc.wat @@ -21,8 +21,19 @@ (import "ints" "caml_format_int" (func $caml_format_int (param (ref eq)) (param (ref eq)) (result (ref eq)))) + (import "string" "caml_string_length" + (func $caml_string_length (param (ref eq)) (result i32))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) + (import "string" "caml_blit_string" + (func $caml_blit_string + (param (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) + (result (ref eq)))) + (import "jsstring" "jsstring_test" + (func $jsstring_test (param anyref) (result i32))) (type $block (array (mut (ref eq)))) + (type $string (struct (field anyref))) (type $bytes (array (mut i8))) (type $buffer @@ -41,26 +52,41 @@ (struct.set $buffer 0 (local.get $buf) (i32.add (local.get $pos) (i32.const 1)))))) - (func $add_string (param $buf (ref $buffer)) (param $v (ref eq)) + (func $add_string (param $buf (ref $buffer)) (param $s (ref eq)) (local $pos i32) (local $len i32) (local $data (ref $bytes)) - (local $s (ref $bytes)) (local.set $pos (struct.get $buffer 0 (local.get $buf))) (local.set $data (struct.get $buffer 1 (local.get $buf))) - (local.set $s (ref.cast (ref $bytes) (local.get $v))) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $caml_string_length (local.get $s))) (if (i32.gt_u (i32.add (local.get $pos) (local.get $len)) (array.len (local.get $data))) (then (local.set $len (i32.sub (array.len (local.get $data)) (local.get $pos))))) - (array.copy $bytes $bytes - (local.get $data) (local.get $pos) - (local.get $s) (i32.const 0) - (local.get $len)) + (drop (call $caml_blit_string + (local.get $s) (ref.i31 (i32.const 0)) + (local.get $data) (ref.i31 (local.get $pos)) + (ref.i31 (local.get $len)))) (struct.set $buffer 0 (local.get $buf) (i32.add (local.get $pos) (local.get $len)))) +(@if use-js-string +(@then + (func $is_string (param $v (ref eq)) (result i32) + (drop (block $not_string (result (ref eq)) + (return_call $jsstring_test + (struct.get $string 0 + (br_on_cast_fail $not_string (ref eq) (ref $string) + (local.get $v)))))) + (i32.const 0)) +) +(@else + (func $is_string (param $v (ref eq)) (result i32) + (ref.test (ref $bytes) (local.get $v))) +)) + + (@string $int_format "%d") + (func (export "caml_format_exception") (param (ref eq)) (result (ref eq)) (local $exn (ref $block)) (local $buf (ref $buffer)) @@ -119,7 +145,7 @@ (call $caml_format_int (@string "%d") (ref.cast (ref i31) (local.get $v))))) - (else (if (ref.test (ref $bytes) (local.get $v)) + (else (if (call $is_string (local.get $v)) (then (call $add_char (local.get $buf) (@char "\"")) (call $add_string (local.get $buf) (local.get $v)) @@ -140,7 +166,7 @@ (local.get $s) (i32.const 0) (struct.get $buffer 1 (local.get $buf)) (i32.const 0) (struct.get $buffer 0 (local.get $buf))) - (local.get $s)) + (call $caml_string_of_bytes (local.get $s))) (else (array.get $block (local.get $exn) (i32.const 1))))) ) diff --git a/runtime/wasm/runtime.js b/runtime/wasm/runtime.js index 742122d572..cc92ac7199 100644 --- a/runtime/wasm/runtime.js +++ b/runtime/wasm/runtime.js @@ -141,7 +141,27 @@ return (((h + (h << 2)) | 0) + (0xe6546b64 | 0)) | 0; } function hash_string(h, s) { - for (var i = 0; i < s.length; i++) h = hash_int(h, s.charCodeAt(i)); + const len = s.length; + for (var i = 0; i + 4 <= len; i += 4) { + var w = + s.charCodeAt(i) | + (s.charCodeAt(i + 1) << 8) | + (s.charCodeAt(i + 2) << 16) | + (s.charCodeAt(i + 3) << 24); + h = hash_int(h, w); + } + w = 0; + switch (len & 3) { + case 3: + // biome-ignore lint/suspicious/noFallthroughSwitchClause: + w = s.charCodeAt(i + 2) << 16; + case 2: + // biome-ignore lint/suspicious/noFallthroughSwitchClause: + w |= s.charCodeAt(i + 1) << 8; + case 1: + w |= s.charCodeAt(i); + h = hash_int(h, w); + } return h ^ s.length; } @@ -280,6 +300,17 @@ dv_set_i16: call.bind(DV.setInt16), dv_set_i8: call.bind(DV.setInt8), littleEndian: new Uint8Array(new Uint32Array([1]).buffer)[0], + ta_blit_from_string: (s, p1, a, p2, l) => { + for (let i = 0; i < l; i++) a[p2 + i] = s.charCodeAt(p1 + i); + }, + ta_to_string: (a) => { + let len = a.length; + if (len <= 4096) return String.fromCharCode(...a); + var s = ""; + for (let i = 0; 0 < len; i += 1024, len -= 1024) + s += String.fromCharCode(...a.subarray(i, i + Math.min(len, 1024))); + return s; + }, wrap_callback: (f) => function (...args) { if (args.length === 0) { @@ -553,10 +584,15 @@ }; const string_ops = { test: (v) => +(typeof v === "string"), - compare: (s1, s2) => (s1 < s2 ? -1 : +(s1 > s2)), + compare: (s1, s2) => (s1 === s2 ? 0 : s1 < s2 ? -1 : 1), decodeStringFromUTF8Array: () => "", encodeStringToUTF8Array: () => 0, fromCharCodeArray: () => "", + length: (s) => s.length, + charCodeAt: (s, i) => s.charCodeAt(i), + concat: (s1, s2) => s1.concat(s2), + equals: (s1, s2) => +(s1 === s2), + substring: (s, i, j) => s.substring(i, j), }; const imports = Object.assign( { diff --git a/runtime/wasm/stdlib.wat b/runtime/wasm/stdlib.wat index 62ff000f26..2afbc215d1 100644 --- a/runtime/wasm/stdlib.wat +++ b/runtime/wasm/stdlib.wat @@ -35,6 +35,8 @@ (import "string" "caml_string_concat" (func $caml_string_concat (param (ref eq)) (param (ref eq)) (result (ref eq)))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) (import "printexc" "caml_format_exception" (func $caml_format_exception (param (ref eq)) (result (ref eq)))) (import "sys" "ocaml_exit" (tag $ocaml_exit)) @@ -45,10 +47,11 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $assoc (struct - (field (ref $bytes)) + (field (ref eq)) (field (mut (ref eq))) (field (mut (ref null $assoc))))) @@ -115,9 +118,7 @@ (return (ref.i31 (i32.const 0)))) (array.set $assoc_array (global.get $named_value_table) (local.get $h) - (struct.new $assoc - (ref.cast (ref $bytes) (local.get 0)) - (local.get 1) (local.get $r))) + (struct.new $assoc (local.get 0) (local.get 1) (local.get $r))) (ref.i31 (i32.const 0))) ;; Used only for testing (tests-jsoo/bin), but inconvenient to pull out diff --git a/runtime/wasm/str.wat b/runtime/wasm/str.wat index e1cbe8ab8c..44cd7bb87b 100644 --- a/runtime/wasm/str.wat +++ b/runtime/wasm/str.wat @@ -19,8 +19,35 @@ (import "fail" "caml_invalid_argument" (func $caml_invalid_argument (param (ref eq)))) (import "fail" "caml_failwith" (func $caml_failwith (param (ref eq)))) + (import "string" "caml_string_of_bytes" + (func $caml_string_of_bytes (param (ref eq)) (result (ref eq)))) + (import "string" "caml_blit_string" + (func $caml_blit_string + (param (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) + (result (ref eq)))) + +(@if use-js-string +(@then + (import "wasm:js-string" "length" + (func $string_length (param externref) (result i32))) + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + + (func $string_val (param $s (ref eq)) (result externref) + (extern.convert_any + (struct.get $string 0 (ref.cast (ref $string) (local.get $s))))) +) +(@else + (func $string_length (param $s (ref $bytes)) (result i32) + (array.len (local.get $s))) + (func $string_get (param $s (ref $bytes)) (param $i i32) (result i32) + (array.get $bytes (local.get $s) (local.get $i))) + (func $string_val (param $s (ref eq)) (result (ref $bytes)) + (ref.cast (ref $bytes) (local.get $s))) +)) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $block (array (mut (ref eq)))) (type $char_table (array i8)) @@ -67,24 +94,48 @@ (i32.shr_u (local.get $c) (i32.const 3))) (i32.and (local.get $c) (i32.const 7))))) - (func $in_bitset (param $s (ref $bytes)) (param $c i32) (result i32) + (func $in_bitset +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $c i32) (result i32) (i32.and (i32.const 1) (i32.shr_u - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.shr_u (local.get $c) (i32.const 3))) (i32.and (local.get $c) (i32.const 7))))) (func $re_match - (param $vre (ref eq)) (param $s (ref $bytes)) (param $pos i32) - (param $accept_partial_match i32) (result (ref eq)) + (param $vre (ref eq)) +(@if use-js-string +(@then + (param $s externref) +) +(@else + (param $s (ref $bytes)) +)) + (param $pos i32) (param $accept_partial_match i32) (result (ref eq)) (local $res (ref $block)) - (local $s' (ref $bytes)) (local $set (ref $bytes)) +(@if use-js-string +(@then + (local $s' externref) + (local $set externref) + (local $normtable externref) +) +(@else + (local $s' (ref $bytes)) + (local $set (ref $bytes)) + (local $normtable (ref $bytes)) +)) (local $len i32) (local $instr i32) (local $arg i32) (local $i i32) (local $j i32) (local $l i32) (local $re (ref $block)) (local $prog (ref $block)) (local $cpool (ref $block)) - (local $normtable (ref $bytes)) (local $numgroups i32) (local $numregisters i32) (local $group_start (ref $int_array)) @@ -94,7 +145,7 @@ (local $stack (ref null $stack)) (local $u (ref $undo)) (local $p (ref $pos)) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (local.set $re (ref.cast (ref $block) (local.get $vre))) (local.set $prog (ref.cast (ref $block) @@ -103,8 +154,7 @@ (ref.cast (ref $block) (array.get $block (local.get $re) (i32.const 2)))) (local.set $normtable - (ref.cast (ref $bytes) - (array.get $block (local.get $re) (i32.const 3)))) + (call $string_val (array.get $block (local.get $re) (i32.const 3)))) (local.set $numgroups (i31.get_s (ref.cast (ref i31) @@ -166,7 +216,7 @@ (i32.shr_u (local.get $instr) (i32.const 8))) (br_if $backtrack (i32.ne (local.get $arg) - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $pos)))) (local.set $pos (i32.add (local.get $pos) (i32.const 1))) @@ -178,9 +228,9 @@ (i32.shr_u (local.get $instr) (i32.const 8))) (br_if $backtrack (i32.ne (local.get $arg) - (array.get_u $bytes + (call $string_get (local.get $normtable) - (array.get_u $bytes + (call $string_get (local.get $s) (local.get $pos))))) (local.set $pos (i32.add (local.get $pos) (i32.const 1))) @@ -189,11 +239,12 @@ (local.set $arg (i32.shr_u (local.get $instr) (i32.const 8))) (local.set $s' - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $cpool) - (i32.add (local.get $arg) (i32.const 1))))) + (i32.add (local.get $arg) + (i32.const 1))))) (local.set $i (i32.const 0)) - (local.set $l (array.len (local.get $s'))) + (local.set $l (call $string_length (local.get $s'))) (loop $loop (if (i32.lt_u (local.get $i) (local.get $l)) (then @@ -202,9 +253,9 @@ (local.get $pos) (local.get $len))) (br_if $backtrack (i32.ne - (array.get_u $bytes (local.get $s') + (call $string_get (local.get $s') (local.get $i)) - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos)))) (local.set $pos (i32.add (local.get $pos) (i32.const 1))) @@ -216,11 +267,12 @@ (local.set $arg (i32.shr_u (local.get $instr) (i32.const 8))) (local.set $s' - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $cpool) - (i32.add (local.get $arg) (i32.const 1))))) + (i32.add (local.get $arg) + (i32.const 1))))) (local.set $i (i32.const 0)) - (local.set $l (array.len (local.get $s'))) + (local.set $l (call $string_length (local.get $s'))) (loop $loop (if (i32.lt_u (local.get $i) (local.get $l)) (then @@ -229,11 +281,11 @@ (local.get $pos) (local.get $len))) (br_if $backtrack (i32.ne - (array.get_u $bytes (local.get $s') + (call $string_get (local.get $s') (local.get $i)) - (array.get_u $bytes + (call $string_get (local.get $normtable) - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos))))) (local.set $pos (i32.add (local.get $pos) (i32.const 1))) @@ -249,11 +301,11 @@ (br_if $backtrack (i32.eqz (call $in_bitset - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $cpool) (i32.add (local.get $arg) (i32.const 1)))) - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos))))) (local.set $pos (i32.add (local.get $pos) (i32.const 1))) @@ -262,7 +314,7 @@ (br_if $continue (i32.eqz (local.get $pos))) (br_if $continue (i32.eq (@char "\n") - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.sub (local.get $pos) (i32.const 1))))) (br $backtrack)) ;; EOL @@ -270,7 +322,7 @@ (i32.eq (local.get $pos) (local.get $len))) (br_if $continue (i32.eq (@char "\n") - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos)))) (br $backtrack)) ;; WORDBOUNDARY @@ -280,7 +332,7 @@ (i32.eq (local.get $pos) (local.get $len))) (br_if $continue (call $is_word_letter - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos)))) (br $backtrack)) (else @@ -288,7 +340,7 @@ (then (br_if $continue (call $is_word_letter - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.sub (local.get $pos) (i32.const 1))))) (br $backtrack)) @@ -296,11 +348,11 @@ (br_if $continue (i32.ne (call $is_word_letter - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (i32.sub (local.get $pos) (i32.const 1)))) (call $is_word_letter - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos))))) (br $backtrack)))))) ;; BEGGROUP @@ -348,9 +400,9 @@ (i32.eq (local.get $pos) (local.get $len))) (br_if $backtrack (i32.ne - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $i)) - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos)))) (local.set $pos (i32.add (local.get $pos) (i32.const 1))) @@ -363,10 +415,11 @@ (if (i32.lt_u (local.get $pos) (local.get $len)) (then (if (call $in_bitset - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $cpool) - (i32.add (local.get $arg) (i32.const 1)))) - (array.get_u $bytes (local.get $s) + (i32.add (local.get $arg) + (i32.const 1)))) + (call $string_get (local.get $s) (local.get $pos))) (then (local.set $pos @@ -375,14 +428,14 @@ ;; SIMPLESTAR (local.set $arg (i32.shr_u (local.get $instr) (i32.const 8))) (local.set $set - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $cpool) - (i32.add (local.get $arg) (i32.const 1))))) + (i32.add (local.get $arg) (i32.const 1))))) (loop $loop (if (i32.lt_u (local.get $pos) (local.get $len)) (then (if (call $in_bitset (local.get $set) - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos))) (then (local.set $pos @@ -393,20 +446,20 @@ (br_if $prefix_match (i32.eq (local.get $pos) (local.get $len))) (local.set $arg (i32.shr_u (local.get $instr) (i32.const 8))) (local.set $set - (ref.cast (ref $bytes) + (call $string_val (array.get $block (local.get $cpool) - (i32.add (local.get $arg) (i32.const 1))))) + (i32.add (local.get $arg) (i32.const 1))))) (br_if $backtrack (i32.eqz (call $in_bitset (local.get $set) - (array.get_u $bytes (local.get $s) (local.get $pos))))) + (call $string_get (local.get $s) (local.get $pos))))) (loop $loop (local.set $pos (i32.add (local.get $pos) (i32.const 1))) (if (i32.lt_u (local.get $pos) (local.get $len)) (then (br_if $loop (call $in_bitset (local.get $set) - (array.get_u $bytes (local.get $s) + (call $string_get (local.get $s) (local.get $pos))))))) (br $continue)) ;; GOTO @@ -513,12 +566,18 @@ (param $re (ref eq)) (param $vs (ref eq)) (param $vpos (ref eq)) (result (ref eq)) ;; ZZZ startchars +(@if use-js-string +(@then + (local $s externref) +) +(@else (local $s (ref $bytes)) +)) (local $pos i32) (local $len i32) (local $res (ref eq)) - (local.set $s (ref.cast (ref $bytes) (local.get $vs))) + (local.set $s (call $string_val (local.get $vs))) (local.set $pos (i31.get_s (ref.cast (ref i31) (local.get $vpos)))) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (if (i32.gt_u (local.get $pos) (local.get $len)) (then (call $caml_invalid_argument (global.get $search_forward)))) (loop $loop @@ -538,15 +597,20 @@ (param $re (ref eq)) (param $vs (ref eq)) (param $vpos (ref eq)) (result (ref eq)) ;; ZZZ startchars +(@if use-js-string +(@then + (local $s externref) +) +(@else (local $s (ref $bytes)) +)) (local $pos i32) (local $len i32) (local $res (ref eq)) - (local.set $s (ref.cast (ref $bytes) (local.get $vs))) + (local.set $s (call $string_val (local.get $vs))) (local.set $pos (i31.get_s (ref.cast (ref i31) (local.get $vpos)))) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (if (i32.gt_u (local.get $pos) (local.get $len)) - (then - (call $caml_invalid_argument (global.get $search_backward)))) + (then (call $caml_invalid_argument (global.get $search_backward)))) (loop $loop (local.set $res (call $re_match @@ -564,12 +628,18 @@ (param $re (ref eq)) (param $vs (ref eq)) (param $vpos (ref eq)) (result (ref eq)) ;; ZZZ startchars +(@if use-js-string +(@then + (local $s externref) +) +(@else (local $s (ref $bytes)) +)) (local $pos i32) (local $len i32) (local $res (ref eq)) - (local.set $s (ref.cast (ref $bytes) (local.get $vs))) + (local.set $s (call $string_val (local.get $vs))) (local.set $pos (i31.get_s (ref.cast (ref i31) (local.get $vpos)))) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (if (i32.gt_u (local.get $pos) (local.get $len)) (then (call $caml_invalid_argument (global.get $string_match)))) (local.set $res @@ -586,15 +656,20 @@ (param $re (ref eq)) (param $vs (ref eq)) (param $vpos (ref eq)) (result (ref eq)) ;; ZZZ startchars +(@if use-js-string +(@then + (local $s externref) +) +(@else (local $s (ref $bytes)) +)) (local $pos i32) (local $len i32) (local $res (ref eq)) - (local.set $s (ref.cast (ref $bytes) (local.get $vs))) + (local.set $s (call $string_val (local.get $vs))) (local.set $pos (i31.get_s (ref.cast (ref i31) (local.get $vpos)))) - (local.set $len (array.len (local.get $s))) + (local.set $len (call $string_length (local.get $s))) (if (i32.gt_u (local.get $pos) (local.get $len)) - (then - (call $caml_invalid_argument (global.get $string_partial_match)))) + (then (call $caml_invalid_argument (global.get $string_partial_match)))) (local.set $res (call $re_match (local.get $re) (local.get $s) (local.get $pos) (i32.const 1))) @@ -607,23 +682,27 @@ (@string $unmatched_group "Str.replace: reference to unmatched group") (func (export "re_replacement_text") - (param $vrepl (ref eq)) (param $vgroups (ref eq)) (param $vorig (ref eq)) + (param $vrepl (ref eq)) (param $vgroups (ref eq)) (param $orig (ref eq)) (result (ref eq)) +(@if use-js-string +(@then + (local $repl externref) +) +(@else (local $repl (ref $bytes)) +)) (local $groups (ref $block)) - (local $orig (ref $bytes)) (local $res (ref $bytes)) (local $i i32) (local $j i32) (local $l i32) (local $len i32) (local $c i32) (local $start i32) (local $end i32) - (local.set $repl (ref.cast (ref $bytes) (local.get $vrepl))) - (local.set $l (array.len (local.get $repl))) + (local.set $repl (call $string_val (local.get $vrepl))) + (local.set $l (call $string_length (local.get $repl))) (local.set $groups (ref.cast (ref $block) (local.get $vgroups))) - (local.set $orig (ref.cast (ref $bytes) (local.get $vorig))) (loop $loop (if (i32.lt_u (local.get $i) (local.get $l)) (then (local.set $c - (array.get_u $bytes (local.get $repl) (local.get $i))) + (call $string_get (local.get $repl) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.ne (local.get $c) (@char "\\")) (then @@ -632,7 +711,7 @@ (if (i32.eq (local.get $i) (local.get $l)) (then (call $caml_failwith (global.get $illegal_backslash)))) (local.set $c - (array.get_u $bytes (local.get $repl) (local.get $i))) + (call $string_get (local.get $repl) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.eq (local.get $c) (@char "\\")) (then @@ -669,7 +748,7 @@ (if (i32.lt_u (local.get $i) (local.get $l)) (then (local.set $c - (array.get_u $bytes (local.get $repl) (local.get $i))) + (call $string_get (local.get $repl) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.ne (local.get $c) (@char "\\")) (then @@ -678,7 +757,7 @@ (local.set $j (i32.add (local.get $j) (i32.const 1))) (br $loop))) (local.set $c - (array.get_u $bytes (local.get $repl) (local.get $i))) + (call $string_get (local.get $repl) (local.get $i))) (local.set $i (i32.add (local.get $i) (i32.const 1))) (if (i32.eq (local.get $c) (@char "\\")) (then @@ -711,11 +790,12 @@ (array.get $block (local.get $groups) (i32.add (local.get $c) (i32.const 2)))))) (local.set $len (i32.sub (local.get $end) (local.get $start))) - (array.copy $bytes $bytes - (local.get $res) (local.get $j) - (local.get $orig) (local.get $start) - (local.get $len)) + (drop + (call $caml_blit_string + (local.get $orig) (ref.i31 (local.get $start)) + (local.get $res) (ref.i31 (local.get $j)) + (ref.i31 (local.get $len)))) (local.set $j (i32.add (local.get $j) (local.get $len))) (br $loop)))) - (local.get $res)) + (call $caml_string_of_bytes (local.get $res))) ) diff --git a/runtime/wasm/string.wat b/runtime/wasm/string.wat index 66183061b4..413b76b77e 100644 --- a/runtime/wasm/string.wat +++ b/runtime/wasm/string.wat @@ -19,11 +19,115 @@ (import "fail" "caml_bound_error" (func $caml_bound_error)) (import "fail" "caml_invalid_argument" (func $caml_invalid_argument (param $arg (ref eq)))) + (import "wasm:js-string" "equals" + (func $string_equals (param externref externref) (result i32))) + (import "wasm:js-string" "compare" + (func $string_compare (param externref externref) (result i32))) + (import "wasm:js-string" "length" + (func $string_length (param externref) (result i32))) + (import "wasm:js-string" "charCodeAt" + (func $string_get (param externref i32) (result i32))) + (import "wasm:js-string" "concat" + (func $string_concat (param externref externref) (result (ref extern)))) + (import "jsstring" "jsbytes_of_bytes" + (func $jsbytes_of_bytes (param (ref $bytes)) (result anyref))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) - (export "caml_bytes_equal" (func $caml_string_equal)) +(@if use-js-string +(@then (func $caml_string_equal (export "caml_string_equal") + (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) + (ref.i31 + (call $string_equals + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p1)))) + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p2))))))) + + (func (export "caml_string_notequal") + (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) + (return + (ref.i31 (i32.eqz (i31.get_u (ref.cast (ref i31) + (call $caml_string_equal (local.get $p1) (local.get $p2)))))))) + + (func (export "caml_string_compare") + (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) + (ref.i31 + (call $string_compare + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p1)))) + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p2))))))) + + (func (export "caml_string_lessequal") + (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) + (ref.i31 + (i32.le_s + (call $string_compare + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p1)))) + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p2))))) + (i32.const 0)))) + + (func (export "caml_string_lessthan") + (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) + (ref.i31 + (i32.lt_s + (call $string_compare + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p1)))) + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p2))))) + (i32.const 0)))) + + (func (export "caml_string_greaterequal") + (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) + (ref.i31 + (i32.ge_s + (call $string_compare + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p1)))) + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p2))))) + (i32.const 0)))) + + (func (export "caml_string_greaterthan") + (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) + (ref.i31 + (i32.gt_s + (call $string_compare + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p1)))) + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $p2))))) + (i32.const 0)))) +) +(@else + (export "caml_string_equal" (func $caml_bytes_equal)) + (export "caml_string_notequal" (func $caml_bytes_notequal)) + (export "caml_string_compare" (func $caml_bytes_compare)) + (export "caml_string_lessequal" (func $caml_bytes_lessequal)) + (export "caml_string_lessthan" (func $caml_bytes_lessthan)) + (export "caml_string_greaterequal" (func $caml_bytes_greaterequal)) + (export "caml_string_greaterthan" (func $caml_bytes_greaterthan)) +)) + + (func $caml_bytes_equal (export "caml_bytes_equal") (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) (local $s1 (ref $bytes)) (local $s2 (ref $bytes)) (local $len i32) (local $i i32) @@ -45,14 +149,13 @@ (br $loop)))) (ref.i31 (i32.const 1))) - (export "caml_bytes_notequal" (func $caml_string_notequal)) - (func $caml_string_notequal (export "caml_string_notequal") + (func $caml_bytes_notequal (export "caml_bytes_notequal") (param $p1 (ref eq)) (param $p2 (ref eq)) (result (ref eq)) (return (ref.i31 (i32.eqz (i31.get_u (ref.cast (ref i31) - (call $caml_string_equal (local.get $p1) (local.get $p2)))))))) + (call $caml_bytes_equal (local.get $p1) (local.get $p2)))))))) - (func $string_compare + (func $bytes_compare (param $p1 (ref eq)) (param $p2 (ref eq)) (result i32) (local $s1 (ref $bytes)) (local $s2 (ref $bytes)) (local $l1 i32) (local $l2 i32) (local $len i32) (local $i i32) @@ -85,39 +188,61 @@ (then (return (i32.const 1)))) (i32.const 0)) - (export "caml_bytes_compare" (func $caml_string_compare)) - (func $caml_string_compare (export "caml_string_compare") + (func $caml_bytes_compare (export "caml_bytes_compare") (param (ref eq)) (param (ref eq)) (result (ref eq)) - (ref.i31 (call $string_compare (local.get 0) (local.get 1)))) + (ref.i31 (call $bytes_compare (local.get 0) (local.get 1)))) - (export "caml_bytes_lessequal" (func $caml_string_lessequal)) - (func $caml_string_lessequal (export "caml_string_lessequal") + (func $caml_bytes_lessequal (export "caml_bytes_lessequal") (param (ref eq)) (param (ref eq)) (result (ref eq)) - (ref.i31 (i32.le_s (call $string_compare (local.get 0) (local.get 1)) + (ref.i31 (i32.le_s (call $bytes_compare (local.get 0) (local.get 1)) (i32.const 0)))) - (export "caml_bytes_lessthan" (func $caml_string_lessthan)) - (func $caml_string_lessthan (export "caml_string_lessthan") + (func $caml_bytes_lessthan (export "caml_bytes_lessthan") (param (ref eq)) (param (ref eq)) (result (ref eq)) - (ref.i31 (i32.lt_s (call $string_compare (local.get 0) (local.get 1)) + (ref.i31 (i32.lt_s (call $bytes_compare (local.get 0) (local.get 1)) (i32.const 0)))) - (export "caml_bytes_greaterequal" (func $caml_string_greaterequal)) - (func $caml_string_greaterequal (export "caml_string_greaterequal") + (func $caml_bytes_greaterequal (export "caml_bytes_greaterequal") (param (ref eq)) (param (ref eq)) (result (ref eq)) - (ref.i31 (i32.ge_s (call $string_compare (local.get 0) (local.get 1)) + (ref.i31 (i32.ge_s (call $bytes_compare (local.get 0) (local.get 1)) (i32.const 0)))) - (export "caml_bytes_greaterthan" (func $caml_string_greaterthan)) - (func $caml_string_greaterthan (export "caml_string_greaterthan") + (func $caml_bytes_greaterthan (export "caml_bytes_greaterthan") (param (ref eq)) (param (ref eq)) (result (ref eq)) - (ref.i31 (i32.gt_s (call $string_compare (local.get 0) (local.get 1)) + (ref.i31 (i32.gt_s (call $bytes_compare (local.get 0) (local.get 1)) (i32.const 0)))) +(@if use-js-string +(@then + (func (export "caml_bytes_of_string") (param $v (ref eq)) (result (ref eq)) + (local $s externref) (local $b (ref $bytes)) (local $l i32) (local $i i32) + (local.set $s + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $v))))) + (local.set $l (call $string_length (local.get $s))) + (local.set $b (array.new $bytes (i32.const 0) (local.get $l))) + ;; loop from JS ? + (loop $loop + (if (i32.lt_u (local.get $i) (local.get $l)) + (then + (array.set $bytes (local.get $b) (local.get $i) + (call $string_get (local.get $s) (local.get $i))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $loop)))) + (local.get $b)) + + (func (export "caml_string_of_bytes") (param $b (ref eq)) (result (ref eq)) + (return + (struct.new $string + (call $jsbytes_of_bytes (ref.cast (ref $bytes) (local.get $b)))))) +) +(@else (export "caml_bytes_of_string" (func $caml_string_of_bytes)) (func $caml_string_of_bytes (export "caml_string_of_bytes") (param $v (ref eq)) (result (ref eq)) (local.get $v)) +)) (@string $Bytes_create "Bytes.create") @@ -129,8 +254,39 @@ (then (call $caml_invalid_argument (global.get $Bytes_create)))) (array.new $bytes (i32.const 0) (local.get $l))) - (export "caml_blit_bytes" (func $caml_blit_string)) - (func $caml_blit_string (export "caml_blit_string") +(@if use-js-string +(@then + (func (export "caml_blit_string") + (param $v1 (ref eq)) (param $vi1 (ref eq)) + (param $v2 (ref eq)) (param $vi2 (ref eq)) + (param $vn (ref eq)) (result (ref eq)) + (local $s externref) (local $b (ref $bytes)) + (local $i1 i32) (local $i2 i32) (local $n i32) (local $i i32) + (local.set $s + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $v1))))) + (local.set $i1 (i31.get_s (ref.cast (ref i31) (local.get $vi1)))) + (local.set $b (ref.cast (ref $bytes) (local.get $v2))) + (local.set $i2 (i31.get_s (ref.cast (ref i31) (local.get $vi2)))) + (local.set $n (i31.get_s (ref.cast (ref i31) (local.get $vn)))) + ;; loop from JS?? + (loop $loop + (if (i32.lt_u (local.get $i) (local.get $n)) + (then + (array.set $bytes (local.get $b) + (i32.add (local.get $i2) (local.get $i)) + (call $string_get + (local.get $s) (i32.add (local.get $i1) (local.get $i)))) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $loop)))) + (ref.i31 (i32.const 0))) +) +(@else + (export "caml_blit_string" (func $caml_blit_bytes)) +)) + + (func $caml_blit_bytes (export "caml_blit_bytes") (param $v1 (ref eq)) (param $i1 (ref eq)) (param $v2 (ref eq)) (param $i2 (ref eq)) (param $n (ref eq)) (result (ref eq)) @@ -152,7 +308,111 @@ (i31.get_u (ref.cast (ref i31) (local.get $len)))) (ref.i31 (i32.const 0))) +(@if use-js-string +(@then + (func (export "caml_string_get16") + (param $v (ref eq)) (param $i (ref eq)) (result (ref eq)) + (local $s externref) (local $p i32) + (local.set $s + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $v))))) + (local.set $p (i31.get_s (ref.cast (ref i31) (local.get $i)))) + (if (i32.lt_s (local.get $p) (i32.const 0)) + (then (call $caml_bound_error))) + (if (i32.ge_u (i32.add (local.get $p) (i32.const 1)) + (call $string_length (local.get $s))) + (then (call $caml_bound_error))) + (ref.i31 (i32.or + (call $string_get (local.get $s) (local.get $p)) + (i32.shl (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 1))) + (i32.const 8))))) + + (func (export "caml_string_get32") + (param $v (ref eq)) (param $i (ref eq)) (result i32) + (local $s externref) (local $p i32) + (local.set $s + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $v))))) + (local.set $p (i31.get_s (ref.cast (ref i31) (local.get $i)))) + (if (i32.lt_s (local.get $p) (i32.const 0)) + (then (call $caml_bound_error))) + (if (i32.ge_u (i32.add (local.get $p) (i32.const 3)) + (call $string_length (local.get $s))) + (then (call $caml_bound_error))) + (i32.or + (i32.or + (call $string_get (local.get $s) (local.get $p)) + (i32.shl (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 1))) + (i32.const 8))) + (i32.or + (i32.shl (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 2))) + (i32.const 16)) + (i32.shl (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 3))) + (i32.const 24))))) + + (func (export "caml_string_get64") + (param $v (ref eq)) (param $i (ref eq)) (result i64) + (local $s externref) (local $p i32) + (local.set $s + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $v))))) + (local.set $p (i31.get_s (ref.cast (ref i31) (local.get $i)))) + (if (i32.lt_s (local.get $p) (i32.const 0)) + (then (call $caml_bound_error))) + (if (i32.ge_u (i32.add (local.get $p) (i32.const 7)) + (call $string_length (local.get $s))) + (then (call $caml_bound_error))) + (i64.or + (i64.or + (i64.or + (i64.extend_i32_u + (call $string_get (local.get $s) (local.get $p))) + (i64.shl (i64.extend_i32_u + (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 1)))) + (i64.const 8))) + (i64.or + (i64.shl (i64.extend_i32_u + (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 2)))) + (i64.const 16)) + (i64.shl (i64.extend_i32_u + (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 3)))) + (i64.const 24)))) + (i64.or + (i64.or + (i64.shl (i64.extend_i32_u + (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 4)))) + (i64.const 32)) + (i64.shl (i64.extend_i32_u + (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 5)))) + (i64.const 40))) + (i64.or + (i64.shl (i64.extend_i32_u + (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 6)))) + (i64.const 48)) + (i64.shl (i64.extend_i32_u + (call $string_get (local.get $s) + (i32.add (local.get $p) (i32.const 7)))) + (i64.const 56)))))) +) +(@else (export "caml_string_get16" (func $caml_bytes_get16)) + (export "caml_string_get32" (func $caml_bytes_get32)) + (export "caml_string_get64" (func $caml_bytes_get64)) +)) + (func $caml_bytes_get16 (export "caml_bytes_get16") (param $v (ref eq)) (param $i (ref eq)) (result (ref eq)) (local $s (ref $bytes)) (local $p i32) @@ -169,7 +429,6 @@ (i32.add (local.get $p) (i32.const 1))) (i32.const 8))))) - (export "caml_string_get32" (func $caml_bytes_get32)) (func $caml_bytes_get32 (export "caml_bytes_get32") (param $v (ref eq)) (param $i (ref eq)) (result i32) (local $s (ref $bytes)) (local $p i32) @@ -194,7 +453,6 @@ (i32.add (local.get $p) (i32.const 3))) (i32.const 24))))) - (export "caml_string_get64" (func $caml_bytes_get64)) (func $caml_bytes_get64 (export "caml_bytes_get64") (param $v (ref eq)) (param $i (ref eq)) (result i64) (local $s (ref $bytes)) (local $p i32) @@ -317,6 +575,22 @@ (i32.wrap_i64 (i64.shr_u (local.get $v) (i64.const 56)))) (ref.i31 (i32.const 0))) +(@if use-js-string +(@then + (func (export "caml_string_concat") + (param $s1 (ref eq)) (param $s2 (ref eq)) (result (ref eq)) + (return + (struct.new $string + (any.convert_extern + (call $string_concat + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $s1)))) + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $s2))))))))) +) +(@else (func (export "caml_string_concat") (param $vs1 (ref eq)) (param $vs2 (ref eq)) (result (ref eq)) (local $s1 (ref $bytes)) (local $s2 (ref $bytes)) @@ -336,4 +610,19 @@ (local.get $s) (local.get $l1) (local.get $s2) (i32.const 0) (local.get $l2)) (local.get $s)) +)) + +(@if use-js-string +(@then + (func (export "caml_string_length") (param $s (ref eq)) (result i32) + (return_call $string_length + (extern.convert_any + (struct.get $string 0 + (ref.cast (ref $string) (local.get $s)))))) +) +(@else + (func (export "caml_string_length") (param $s (ref eq)) (result i32) + (array.len (ref.cast (ref $bytes) (local.get $s)))) +)) + ) diff --git a/runtime/wasm/sync.wat b/runtime/wasm/sync.wat index 1b498d4a93..21042d6173 100644 --- a/runtime/wasm/sync.wat +++ b/runtime/wasm/sync.wat @@ -25,6 +25,7 @@ (import "custom" "custom_next_id" (func $custom_next_id (result i64))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $compare (func (param (ref eq)) (param (ref eq)) (param i32) (result i32))) (type $hash @@ -36,7 +37,13 @@ (type $dup (func (param (ref eq)) (result (ref eq)))) (type $custom_operations (struct +(@if use-js-string +(@then + (field $id (ref $string)) +) +(@else (field $id (ref $bytes)) +)) (field $compare (ref null $compare)) (field $compare_ext (ref null $compare)) (field $hash (ref null $hash)) diff --git a/runtime/wasm/sys.wat b/runtime/wasm/sys.wat index 7a2a582499..05be31a031 100644 --- a/runtime/wasm/sys.wat +++ b/runtime/wasm/sys.wat @@ -56,6 +56,7 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $float (struct (field f64))) (tag $ocaml_exit (export "ocaml_exit")) diff --git a/runtime/wasm/unix.wat b/runtime/wasm/unix.wat index 0b4a9229ae..561a8c013c 100644 --- a/runtime/wasm/unix.wat +++ b/runtime/wasm/unix.wat @@ -118,6 +118,7 @@ (func $Int64_val (param (ref eq)) (result i64))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $block (array (mut (ref eq)))) (type $float (struct (field f64))) (type $float_array (array (mut f64))) diff --git a/runtime/wasm/weak.wat b/runtime/wasm/weak.wat index 1f704b8071..12fe4f0b91 100644 --- a/runtime/wasm/weak.wat +++ b/runtime/wasm/weak.wat @@ -34,6 +34,7 @@ (import "jslib" "wrap" (func $wrap (param anyref) (result (ref eq)))) (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) + (type $string (struct (field anyref))) (type $js (struct (field anyref))) ;; A weak array is a an abstract value composed of possibly some diff --git a/tools/ci_setup.ml b/tools/ci_setup.ml index cb91554ba4..671f44ceee 100644 --- a/tools/ci_setup.ml +++ b/tools/ci_setup.ml @@ -37,6 +37,7 @@ let forked_packages = ; "bonsai_web_test" ; "core" ; "core_kernel" + ; "core_unix" ; "ocaml_intrinsics_kernel" ; "ppx_expect" ; "ppx_inline_test" @@ -54,6 +55,9 @@ let dune_workspace = (_ (env-vars (TESTING_FRAMEWORK inline-test)) (js_of_ocaml (enabled_if false)) + (wasm_of_ocaml + (flags + (:standard --enable use-js-string))) (flags :standard -alert -all -warn-error -7-8-27-30-32-34-37-49-52-55 -w -7-27-30-32-34-37-49-52-55-58-67-69))) |} @@ -293,12 +297,28 @@ let sync_exec f l = let l = List.map f l in List.iter (fun f -> f ()) l +let branch nm = + if is_forked nm then + match nm with + | "async_js" + | "base" + | "core" + | "core_kernel" + | "core_unix" + | "time_now" + | "zarith_stubs_js" -> Some "js-strings" + | _ -> Some "wasm-v0.18" + else + None + let pin nm = + let branch = Option.value ~default:"wasm-v0.18" (branch nm) in exec_async (Printf.sprintf - "opam pin add -n %s https://github.com/ocaml-wasm/%s.git#wasm-v0.18" + "opam pin add -n %s https://github.com/ocaml-wasm/%s.git#%s" nm - nm) + nm + branch) let pin_packages () = sync_exec pin (StringSet.elements do_pin) @@ -354,7 +374,7 @@ let () = sync_exec (fun () -> exec_async "opam install uri --deps-only") [ () ]; sync_exec (fun nm -> - let branch = if is_forked nm then Some "wasm-v0.18" else None in + let branch = branch nm in let commit = if is_forked nm then None