diff --git a/AGENTS.md b/AGENTS.md
index a1a7f2ce9c8..9b5974bc0ff 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -72,6 +72,15 @@ JS IR
JavaScript Code
```
+### Platform-specific compiler modules
+
+The Dune `browser` profile builds the playground compiler. Platform-dependent
+modules are stored below `platform/native/` and `platform/playground/` in their
+owning compiler directory. Rules in that directory's `dune` file copy the
+selected implementation into the build directory as an ordinary `.ml` module;
+all other profiles select the native source. Generated module paths in errors
+or stack traces therefore map back to one of those two source directories.
+
### Key Directory Structure
```
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 76551587577..eee2c3a2e70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
- Add the `-check-lam` compiler option, enable Lambda invariant checking in compiler tests, and remove build-profile-dependent checking. https://github.com/rescript-lang/rescript/pull/8534
- Replace `-bs-diagnose` with `-debug-ir` and make IR diagnostic artifacts deterministic, compilation-local, and easy to clean. https://github.com/rescript-lang/rescript/pull/8535
+- Replace CPPO-based browser conditionals with Dune-selected native and playground compiler implementations. https://github.com/rescript-lang/rescript/pull/8541
# 13.0.0-alpha.5
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2802119dedb..46078febc0b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -297,6 +297,20 @@ The "Playground bundle" is a JS version of the ReScript compiler; including all
The ReScript source code is compiled with a tool called [JSOO (js_of_ocaml)](https://ocsigen.org/js_of_ocaml/latest/manual/overview), which uses OCaml bytecode to compile to JavaScript and is part of the bigger OCaml ecosystem.
+### Platform-specific compiler modules
+
+Within `compiler/`, the Dune `browser` profile specifically means the
+playground compiler. A few modules have implementations under
+`platform/native/` and `platform/playground/`; mutually exclusive rules in the
+owning `dune` file copy the appropriate implementation into the build directory
+as an ordinary `.ml` module. Other profiles select the native implementation.
+
+Consequently, a generated filename such as `ext_platform_primitives.ml` may
+appear in a compiler stack trace even though it is not present in the source
+tree. Its source is the corresponding file below `platform/native/` or
+`platform/playground/` in the same compiler directory. Keep both
+implementations API-compatible when changing one of these modules.
+
### Building the Bundle
The entry point of the JSOO bundle is located in `compiler/jsoo/jsoo_playground_main.ml`, the compiler and its relevant runtime cmij files can be built via make:
@@ -310,9 +324,9 @@ Note that building the cmijs is based on the dependencies defined in `packages/p
After a successful compilation, you will find following files in your project:
-- `playground/compiler.js` -> This is the ReScript compiler, which binds the ReScript API to the `window` object.
-- `playground/packages/compiler-builtins` -> The compiler base cmij containing all the relevant core modules (`Js`, `Belt`, `Pervasives`, etc.)
-- `playground/packages/*` -> Contains third party deps with cmij.js files (as defined in `packages/playground/rescript.json`)
+- `packages/playground/compiler.js` -> This is the ReScript compiler, which binds the ReScript API to the `window` object.
+- `packages/playground/packages/compiler-builtins` -> The compiler base cmij containing all the relevant core modules (`Js`, `Belt`, `Pervasives`, etc.)
+- `packages/playground/packages/*` -> Contains third party deps with cmij.js files (as defined in `packages/playground/rescript.json`)
You can now use the `compiler.js` file either directly by using a `` and `` inside a html file, use a browser bundler infrastructure to optimize it, or use `nodejs` to run it on a command line:
diff --git a/compiler/core/build_artifact_stubs.c b/compiler/core/build_artifact_stubs.c
new file mode 100644
index 00000000000..2a578fd06ca
--- /dev/null
+++ b/compiler/core/build_artifact_stubs.c
@@ -0,0 +1,33 @@
+#include "caml/memory.h"
+#include "caml/mlvalues.h"
+#include "caml/osdeps.h"
+#include "caml/signals.h"
+
+#ifdef _WIN32
+#include
+
+CAMLprim value caml_stale_file(value path)
+{
+ CAMLparam1(path);
+ struct _utimbuf times;
+ char *os_path = caml_stat_strdup(String_val(path));
+ times.modtime = 0;
+ caml_enter_blocking_section();
+ _utime(os_path, ×);
+ caml_leave_blocking_section();
+ caml_stat_free(os_path);
+ CAMLreturn(Val_unit);
+}
+#else
+#include
+
+CAMLprim value caml_stale_file(value path)
+{
+ CAMLparam1(path);
+ struct timeval times[2] = {{0, 0}, {0, 0}};
+ char *os_path = caml_stat_strdup_to_os(String_val(path));
+ utimes(os_path, times);
+ caml_stat_free(os_path);
+ CAMLreturn(Val_unit);
+}
+#endif
diff --git a/compiler/core/dune b/compiler/core/dune
index 261b0deda02..7029e11d807 100644
--- a/compiler/core/dune
+++ b/compiler/core/dune
@@ -1,20 +1,44 @@
-(library
- (name core)
- (wrapped false)
- (instrumentation
- (backend bisect_ppx))
- (flags
- (:standard -w +a-4-9-27-30-40-41-42-48-70))
- (libraries depends ext flow_parser frontend gentype yojson))
+(env
+ (_
+ (flags
+ (:standard -w +a-4-9-27-30-40-41-42-48-70))))
+
+; The browser profile builds the playground compiler; these rules generate modules from platform/{native,playground}.
+
+(rule
+ (target build_artifact.ml)
+ (enabled_if
+ (= %{profile} browser))
+ (action
+ (copy platform/playground/build_artifact.ml build_artifact.ml)))
+
+(rule
+ (target build_artifact.ml)
+ (enabled_if
+ (<> %{profile} browser))
+ (action
+ (copy platform/native/build_artifact.ml build_artifact.ml)))
(rule
(target js_name_of_module_id.ml)
- (deps js_name_of_module_id.cppo.ml)
+ (enabled_if
+ (= %{profile} browser))
(action
- (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target})))
+ (copy platform/playground/js_name_of_module_id.ml js_name_of_module_id.ml)))
(rule
- (target lam_compile_main.ml)
- (deps lam_compile_main.cppo.ml)
+ (target js_name_of_module_id.ml)
+ (enabled_if
+ (<> %{profile} browser))
(action
- (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target})))
+ (copy platform/native/js_name_of_module_id.ml js_name_of_module_id.ml)))
+
+(library
+ (name core)
+ (wrapped false)
+ (instrumentation
+ (backend bisect_ppx))
+ (foreign_stubs
+ (language c)
+ (names build_artifact_stubs))
+ (libraries depends ext flow_parser frontend gentype yojson))
diff --git a/compiler/core/js_name_of_module_id.cppo.ml b/compiler/core/js_name_of_module_id.cppo.ml
deleted file mode 100644
index a954db439c6..00000000000
--- a/compiler/core/js_name_of_module_id.cppo.ml
+++ /dev/null
@@ -1,167 +0,0 @@
-(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * In addition to the permissions granted to you by the LGPL, you may combine
- * or link a "work that uses the Library" with a publicly distributed version
- * of this file to produce a combined library or application, then distribute
- * that combined work under the terms of your choosing, with no requirement
- * to comply with the obligations normally placed on you by section 4 of the
- * LGPL version 3 (or the corresponding section of a later version of the LGPL
- * should you choose to use a later version).
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
-(*
-let (=) (x : int) (y:float) = assert false
-*)
-
-#ifdef BROWSER
-
-let string_of_module_id_in_browser (x : Lam_module_ident.t) =
- match x.kind with
- | External {name} -> name
- | Runtime | Ml ->
- "./stdlib/" ^ x.id.name ^ ".mjs"
-
-let string_of_module_id
- (id : Lam_module_ident.t)
- ~output_dir:(_:string)
- (_module_system : Js_packages_info.module_system)
- = string_of_module_id_in_browser id
-
-#else
-
-let (//) = Filename.concat
-
-
-let fix_path_for_windows : string -> string =
- if Ext_sys.is_windows_or_cygwin then Ext_string.replace_backward_slash
- else fun s -> s
-
-let runtime_suffix_of_module_system (module_system : Js_packages_info.module_system)
- =
- match module_system with
- | Commonjs -> ".cjs"
- | Esmodule -> ".mjs"
-
-(* dependency is runtime module *)
-let get_runtime_module_path
- (dep_module_id : Lam_module_ident.t)
- (current_package_info : Js_packages_info.t)
- (module_system : Js_packages_info.module_system) =
- let current_info_query =
- Js_packages_info.query_package_infos current_package_info
- module_system in
- let js_file =
- Ext_namespace.js_name_of_modulename dep_module_id.id.name
- Upper (runtime_suffix_of_module_system module_system) in
- match current_info_query with
- | Package_not_found -> assert false
- | Package_script ->
- Js_packages_info.runtime_package_path module_system js_file
- | Package_found pkg ->
- let dep_path =
- "lib" // Js_packages_info.runtime_dir_of_module_system module_system in
- if Js_packages_info.is_runtime_package current_package_info then
- Ext_path.node_rebase_file
- ~from:pkg.rel_path
- ~to_:dep_path
- js_file
- (* TODO: we assume that both [x] and [path] could only be relative path
- which is guaranteed by [-bs-package-output]
- *)
- else
- Js_packages_info.runtime_package_path module_system js_file
-
-(* [output_dir] is decided by the command line argument *)
-let string_of_module_id
- (dep_module_id : Lam_module_ident.t)
- ~(output_dir : string )
- (module_system : Js_packages_info.module_system)
- : string =
- let current_package_info = Js_packages_state.get_packages_info () in
- fix_path_for_windows (
- match dep_module_id.kind with
- | External {name} -> name (* the literal string for external package *)
- (* This may not be enough,
- 1. For cross packages, we may need settle
- down a single js package
- 2. We may need es6 path for dead code elimination
- But frankly, very few JS packages have no dependency,
- so having plugin may sound not that bad
- *)
- | Runtime ->
- get_runtime_module_path dep_module_id current_package_info module_system
- | Ml ->
- let current_info_query =
- Js_packages_info.query_package_infos
- current_package_info
- module_system
- in
- match Lam_compile_env.get_package_path_from_cmj dep_module_id with
- | (package_path, dep_package_info, case) ->
-
-
- let dep_info_query =
- Js_packages_info.query_package_infos dep_package_info module_system
- in
- match dep_info_query, current_info_query with
- | Package_not_found , _ ->
- Bs_exception.error (Missing_ml_dependency dep_module_id.id.name)
- | Package_script , Package_found _ ->
- Bs_exception.error (Dependency_script_module_dependent_not dep_module_id.id.name)
- | (Package_script | Package_found _ ), Package_not_found -> assert false
-
- | Package_found ({suffix} as pkg), Package_script
- ->
- let js_file =
- Ext_namespace.js_name_of_modulename dep_module_id.id.name case suffix in
- pkg.pkg_rel_path // js_file
- | Package_found ({suffix } as dep_pkg),
- Package_found cur_pkg ->
- let js_file =
- Ext_namespace.js_name_of_modulename dep_module_id.id.name case suffix in
-
- if Js_packages_info.same_package_by_name current_package_info dep_package_info then
- Ext_path.node_rebase_file
- ~from:cur_pkg.rel_path
- ~to_:dep_pkg.rel_path
- js_file
- (* TODO: we assume that both [x] and [path] could only be relative path
- which is guaranteed by [-bs-package-output]
- *)
- else
- if Js_packages_info.is_runtime_package dep_package_info then
- get_runtime_module_path dep_module_id current_package_info module_system
- else dep_pkg.pkg_rel_path // js_file
- | Package_script, Package_script
- ->
- let js_file =
- Ext_namespace.js_name_of_modulename dep_module_id.id.name case Literals.suffix_js in
- match Config_util.find_opt js_file with
- | Some file ->
- let basename = Filename.basename file in
- let dirname = Filename.dirname file in
- Ext_path.node_rebase_file
- ~from:(
- Ext_path.absolute_cwd_path
- output_dir)
- ~to_:(
- Ext_path.absolute_cwd_path
-
- dirname)
- basename
- | None ->
- Bs_exception.error (Js_not_found js_file))
-
-#endif
diff --git a/compiler/core/lam_compile_main.cppo.ml b/compiler/core/lam_compile_main.cppo.ml
deleted file mode 100644
index 884c06afb02..00000000000
--- a/compiler/core/lam_compile_main.cppo.ml
+++ /dev/null
@@ -1,399 +0,0 @@
-(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
- * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * In addition to the permissions granted to you by the LGPL, you may combine
- * or link a "work that uses the Library" with a publicly distributed version
- * of this file to produce a combined library or application, then distribute
- * that combined work under the terms of your choosing, with no requirement
- * to comply with the obligations normally placed on you by section 4 of the
- * LGPL version 3 (or the corresponding section of a later version of the LGPL
- * should you choose to use a later version).
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
-
-
-
-
-
-
-
-
-(* module E = Js_exp_make *)
-(* module S = Js_stmt_make *)
-
-
-let compile_group output_prefix (meta : Lam_stats.t)
- (x : Lam_group.t) : Js_output.t =
- match x with
- (*
- We need
-
- 2. [E.builtin_dot] for javascript builtin
- 3. [E.mldot]
- *)
- (* ATTENTION: check {!Lam_compile_global} for consistency *)
- (* Special handling for values in [Pervasives] *)
- (*
- we delegate [stdout, stderr, and stdin] into [caml_io] module,
- the motivation is to help dead code eliminatiion, it's helpful
- to make those parts pure (not a function call), then it can be removed
- if unused
- *)
-
- (* QUICK hack to make hello world example nicer,
- Note the arity of [print_endline] is already analyzed before,
- so it should be safe
- *)
-
- | Single (kind, id, lam) ->
- (* let lam = Optimizer.simplify_lets [] lam in *)
- (* can not apply again, it's wrong USE it with care*)
- (* ([Js_stmt_make.comment (Gen_of_env.query_type id env )], None) ++ *)
- Lam_compile.compile_lambda ~output_prefix { continuation = Declare (kind, id);
- jmp_table = Lam_compile_context.empty_handler_map;
- switch_depth = 0;
- loop_stack = [];
- loop_label_counter = ref 0;
- meta
- } lam
-
- | Recursive id_lams ->
- Lam_compile.compile_recursive_lets ~output_prefix
- { continuation = EffectCall Not_tail;
- jmp_table = Lam_compile_context.empty_handler_map;
- switch_depth = 0;
- loop_stack = [];
- loop_label_counter = ref 0;
- meta
- }
- id_lams
- | Nop lam -> (* TODO: Side effect callls, log and see statistics *)
- Lam_compile.compile_lambda ~output_prefix {continuation = EffectCall Not_tail;
- jmp_table = Lam_compile_context.empty_handler_map;
- switch_depth = 0;
- loop_stack = [];
- loop_label_counter = ref 0;
- meta
- } lam
-
-;;
-
-(** Also need analyze its depenency is pure or not *)
-let no_side_effects (rest : Lam_group.t list) : string option =
- Ext_list.find_opt rest (fun x ->
- match x with
- | Single(kind,id,body) ->
- begin
- match kind with
- | Strict | Variable ->
- if not @@ Lam_analysis.no_side_effects body
- then Some (Printf.sprintf "%s" id.name)
- else None
- | _ -> None
- end
- | Recursive bindings ->
- Ext_list.find_opt bindings (fun (id,lam) ->
- if not @@ Lam_analysis.no_side_effects lam
- then Some (Printf.sprintf "%s" id.Ident.name )
- else None
- )
- | Nop lam ->
- if not @@ Lam_analysis.no_side_effects lam
- then
- (* (Lam_util.string_of_lambda lam) *)
- Some ""
- else None (* TODO :*))
-
-
-(** Actually simplify_lets is kind of global optimization since it requires you to know whether
- it's used or not
-*)
-let compile
- (output_prefix : string)
- export_idents
- (lam : Lambda.lambda) =
- let debug_ir = !Js_config.debug_ir in
- let diagnostics =
- if debug_ir then Some (Ir_diagnostics.create ~output_prefix) else None
- in
- let d pass lam =
- (match diagnostics with
- | Some diagnostics ->
- Ir_diagnostics.dump_lam diagnostics ~pass lam;
- Ext_log.dwarn ~__POS__ "START CHECKING PASS %s@." pass
- | None -> ());
- if !Js_config.check_lam || debug_ir then begin
- ignore @@ Lam_check.check ~file:!Location.input_name ~pass lam;
- if debug_ir then Ext_log.dwarn ~__POS__ "FINISH CHECKING PASS %s@." pass
- end;
- lam
- in
- let j pass program =
- Ext_option.iter diagnostics (fun diagnostics ->
- Ir_diagnostics.dump_js diagnostics ~pass program);
- program
- in
- let export_ident_sets = Set_ident.of_list export_idents in
- (* To make toplevel happy - reentrant for js-demo *)
- let () =
- if debug_ir then begin
- Ext_list.iter export_idents
- (fun id -> Ext_log.dwarn ~__POS__ "export idents: %s/%d" id.name id.stamp)
- end;
- Lam_compile_env.reset ()
- in
- let lam, may_required_modules = Lam_convert.convert export_ident_sets lam in
-
-
- let lam = d "initial" lam in
- let lam = Lam_pass_deep_flatten.deep_flatten lam in
- let lam = d "flatten0" lam in
- let meta : Lam_stats.t =
- Lam_stats.make
- ~export_idents
- ~export_ident_sets in
- let () = Lam_pass_collect.collect_info meta lam in
- let lam =
- let lam =
- lam
- |> d "flatten1"
- |> Lam_pass_exits.simplify_exits
- |> d "simplify_exits"
- |> (fun lam ->
- Lam_pass_collect.collect_info meta lam;
- if debug_ir then
- Ext_log.dwarn ~__POS__ "Before simplify_alias: %a@." Lam_stats.print
- meta;
- lam)
- |> Lam_pass_remove_alias.simplify_alias meta
- |> d "simplify_alias"
- |> Lam_pass_deep_flatten.deep_flatten
- |> d "flatten2"
- in (* Inling happens*)
-
- let () = Lam_pass_collect.collect_info meta lam in
- let lam = Lam_pass_remove_alias.simplify_alias meta lam in
- let lam = Lam_pass_deep_flatten.deep_flatten lam in
- let () = Lam_pass_collect.collect_info meta lam in
- let lam =
- lam
- |> d "alpha_before"
- |> Lam_pass_alpha_conversion.alpha_conversion meta
- |> d "alpha_after"
- |> Lam_pass_exits.simplify_exits in
- let () = Lam_pass_collect.collect_info meta lam in
-
-
- lam
- |> d "simplify_alias_before"
- |> Lam_pass_remove_alias.simplify_alias meta
- |> d "alpha_conversion"
- |> Lam_pass_alpha_conversion.alpha_conversion meta
- |> d "before-simplify_lets"
- (* we should investigate a better way to put different passes : )*)
- |> Lam_pass_lets_dce.simplify_lets
-
- |> d "before-simplify-exits"
- (* |> (fun lam -> Lam_pass_collect.collect_info meta lam
- ; Lam_pass_remove_alias.simplify_alias meta lam) *)
- (* |> Lam_group_pass.scc_pass
- |> d "scc" *)
- |> Lam_pass_exits.simplify_exits
- |> d "simplify_lets"
- |> (fun lam ->
- if debug_ir then
- Ext_log.dwarn ~__POS__ "Before coercion: %a@." Lam_stats.print meta;
- lam)
- in
-
- let ({Lam_coercion.groups = groups } as coerced_input , meta) =
- Lam_coercion.coerce_and_group_big_lambda meta lam
- in
-
-let () =
- if debug_ir then begin
- Ext_log.dwarn ~__POS__ "After coercion: %a@." Lam_stats.print meta;
- Ext_option.iter diagnostics (fun diagnostics ->
- Ir_diagnostics.dump_groups diagnostics coerced_input.groups)
- end
-in
-let maybe_pure = no_side_effects groups in
-let () =
- if debug_ir then
- Ext_log.dwarn ~__POS__ "\n@[[TIME:]Pre-compile: %f@]@."
- (Sys.time () *. 1000.)
-in
-let body =
- Ext_list.map groups (fun group -> compile_group output_prefix meta group)
- |> Js_output.concat
- |> Js_output.output_as_block
-in
-let () =
- if debug_ir then
- Ext_log.dwarn ~__POS__ "\n@[[TIME:]Post-compile: %f@]@."
- (Sys.time () *. 1000.)
-in
-(* The file is not big at all compared with [cmo] *)
-(* Ext_marshal.to_file (Ext_path.chop_extension filename ^ ".mj") js; *)
-let meta_exports = meta.exports in
-let export_set = Set_ident.of_list meta_exports in
-let js : J.program =
- {
- exports = meta_exports ;
- export_set;
- block = body}
-in
-js
-|> j "initial"
-|> Js_pass_flatten.program
-|> j "flatten"
-|> Js_pass_external_shadow.program
-|> j "external_shadow"
-|> Js_pass_tailcall_inline.tailcall_inline
-|> j "inline_and_shake"
-|> Js_pass_record_rest.program
-|> j "record_rest"
-|> Js_pass_flatten_and_mark_dead.program
-|> j "flatten_and_mark_dead"
-(* |> Js_inline_and_eliminate.inline_and_shake *)
-(* |> j "inline_and_shake" *)
-|> (fun js -> ignore @@ Js_pass_scope.program js ; js )
-|> Js_shake.shake_program
-|> j "shake"
-|> ( fun (program: J.program) ->
- let external_module_ids : Lam_module_ident.t list =
- if !Js_config.all_module_aliases then []
- else
- let hard_deps =
- Js_fold_basic.calculate_hard_dependencies program.block in
- Lam_compile_env.populate_required_modules
- may_required_modules hard_deps ;
- Ext_list.sort_via_array (Lam_module_ident.Hash_set.to_list hard_deps)
- (fun id1 id2 ->
- Ext_string.compare (Lam_module_ident.name id1) (Lam_module_ident.name id2)
- )
- in
- Warnings.check_fatal();
- let effect_ =
- Lam_stats_export.get_dependent_module_effect
- maybe_pure external_module_ids in
- let v : Js_cmj_format.t =
- Lam_stats_export.export_to_cmj
- meta
- effect_
- coerced_input.export_map
- (if Ext_char.is_lower_case (Filename.basename output_prefix).[0] then Little else Upper)
- in
- (if not !Clflags.dont_write_files then
- Js_cmj_format.to_file
- ~check_exists:(not !Js_config.force_cmj)
- (output_prefix ^ Literals.suffix_cmj) v);
- {J.program = program ; side_effect = effect_ ; modules = external_module_ids }
- )
-;;
-
-let (//) = Filename.concat
-
-let remove_stale_source_map ?(remove_stale_map = true) target_file =
- if remove_stale_map && not !Clflags.dont_write_files then
- Misc.remove_file (target_file ^ ".map")
-
-let dump_deps_program_with_source_map ?(remove_stale_map = true) ~target_file
- ~output_prefix module_system lambda_output chan =
- let builder =
- Js_source_map.make ~generated_file:target_file
- ~source_root:!Js_config.source_map_root
- ~sources_content:!Js_config.source_map_sources_content
- in
- Js_source_map.with_builder builder (fun () ->
- Js_dump_program.pp_deps_program ~output_prefix module_system lambda_output
- (Ext_pp.from_channel chan));
- match !Js_config.source_map with
- | Linked ->
- let json = Js_source_map.json builder in
- output_string chan (Js_source_map.linked_comment ~map_file:(target_file ^ ".map"));
- Ext_io.write_file (target_file ^ ".map") json
- | Hidden -> Ext_io.write_file (target_file ^ ".map") (Js_source_map.json builder)
- | Inline ->
- output_string chan
- (Js_source_map.inline_comment ~json:(Js_source_map.json builder));
- remove_stale_source_map ~remove_stale_map target_file
- | No_source_map -> ()
-
-let lambda_as_module
- (lambda_output : J.deps_program)
- (output_prefix : string)
- : unit =
- let package_info = Js_packages_state.get_packages_info () in
- if Js_packages_info.is_empty package_info && !Js_config.js_stdout then begin
- match !Js_config.source_map with
- | Inline ->
- let target_file =
- Ext_namespace.change_ext_ns_suffix
- (Filename.basename output_prefix)
- Literals.suffix_js
- in
- dump_deps_program_with_source_map ~remove_stale_map:false ~target_file
- ~output_prefix Commonjs lambda_output stdout
- | _ ->
- Js_dump_program.dump_deps_program ~output_prefix Commonjs lambda_output
- stdout
- end else
- Js_packages_info.iter package_info (fun {module_system; path; suffix} ->
- let basename =
- Ext_namespace.change_ext_ns_suffix (Filename.basename output_prefix) suffix
- in
- let target_file =
- (Ext_path.package_dir () //
- path //
- basename
- (* #913 only generate little-case js file *)
- ) in
- let output_chan chan =
- match !Js_config.source_map with
- | No_source_map ->
- Js_dump_program.dump_deps_program ~output_prefix module_system
- lambda_output chan;
- remove_stale_source_map target_file
- | Linked | Inline | Hidden ->
- dump_deps_program_with_source_map ~target_file ~output_prefix
- module_system lambda_output chan
- in
- (if not !Clflags.dont_write_files then
- Ext_pervasives.with_file_as_chan
- target_file output_chan );
- if !Warnings.has_warnings then begin
- Warnings.has_warnings := false ;
-#ifndef BROWSER
- (* 5206: When there were warnings found during the compilation, we want the file
- to be rebuilt on the next "rescript build" so that the warnings keep being shown.
- Set the timestamp of the ast file to 1970-01-01 to make this rebuild happen.
- (Do *not* set the timestamp of the JS output file instead
- as that does not play well with every bundler.) *)
- let ast_file = output_prefix ^ Literals.suffix_ast in
- if Sys.file_exists ast_file then begin
- Bs_hash_stubs.set_as_old_file ast_file
- end
-#endif
- end
- )
-
-
-
-(* We can use {!Env.current_unit = "Pervasives"} to tell if it is some specific module,
- We need handle some definitions in standard libraries in a special way, most are io specific,
- includes {!Pervasives.stdin, Pervasives.stdout, Pervasives.stderr}
-
- However, use filename instead of {!Env.current_unit} is more honest, since node-js module system is coupled with the file name
-*)
diff --git a/compiler/core/lam_compile_main.ml b/compiler/core/lam_compile_main.ml
new file mode 100644
index 00000000000..4db6c659179
--- /dev/null
+++ b/compiler/core/lam_compile_main.ml
@@ -0,0 +1,348 @@
+(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
+ * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In addition to the permissions granted to you by the LGPL, you may combine
+ * or link a "work that uses the Library" with a publicly distributed version
+ * of this file to produce a combined library or application, then distribute
+ * that combined work under the terms of your choosing, with no requirement
+ * to comply with the obligations normally placed on you by section 4 of the
+ * LGPL version 3 (or the corresponding section of a later version of the LGPL
+ * should you choose to use a later version).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
+
+(* module E = Js_exp_make *)
+(* module S = Js_stmt_make *)
+
+let compile_group output_prefix (meta : Lam_stats.t) (x : Lam_group.t) :
+ Js_output.t =
+ match x with
+ (*
+ We need
+
+ 2. [E.builtin_dot] for javascript builtin
+ 3. [E.mldot]
+ *)
+ (* ATTENTION: check {!Lam_compile_global} for consistency *)
+ (* Special handling for values in [Pervasives] *)
+ (*
+ we delegate [stdout, stderr, and stdin] into [caml_io] module,
+ the motivation is to help dead code eliminatiion, it's helpful
+ to make those parts pure (not a function call), then it can be removed
+ if unused
+ *)
+
+ (* QUICK hack to make hello world example nicer,
+ Note the arity of [print_endline] is already analyzed before,
+ so it should be safe
+ *)
+ | Single (kind, id, lam) ->
+ (* let lam = Optimizer.simplify_lets [] lam in *)
+ (* can not apply again, it's wrong USE it with care*)
+ (* ([Js_stmt_make.comment (Gen_of_env.query_type id env )], None) ++ *)
+ Lam_compile.compile_lambda ~output_prefix
+ {
+ continuation = Declare (kind, id);
+ jmp_table = Lam_compile_context.empty_handler_map;
+ switch_depth = 0;
+ loop_stack = [];
+ loop_label_counter = ref 0;
+ meta;
+ }
+ lam
+ | Recursive id_lams ->
+ Lam_compile.compile_recursive_lets ~output_prefix
+ {
+ continuation = EffectCall Not_tail;
+ jmp_table = Lam_compile_context.empty_handler_map;
+ switch_depth = 0;
+ loop_stack = [];
+ loop_label_counter = ref 0;
+ meta;
+ }
+ id_lams
+ | Nop lam ->
+ (* TODO: Side effect callls, log and see statistics *)
+ Lam_compile.compile_lambda ~output_prefix
+ {
+ continuation = EffectCall Not_tail;
+ jmp_table = Lam_compile_context.empty_handler_map;
+ switch_depth = 0;
+ loop_stack = [];
+ loop_label_counter = ref 0;
+ meta;
+ }
+ lam
+
+(** Also need analyze its depenency is pure or not *)
+let no_side_effects (rest : Lam_group.t list) : string option =
+ Ext_list.find_opt rest (fun x ->
+ match x with
+ | Single (kind, id, body) -> (
+ match kind with
+ | Strict | Variable ->
+ if not @@ Lam_analysis.no_side_effects body then
+ Some (Printf.sprintf "%s" id.name)
+ else None
+ | _ -> None)
+ | Recursive bindings ->
+ Ext_list.find_opt bindings (fun (id, lam) ->
+ if not @@ Lam_analysis.no_side_effects lam then
+ Some (Printf.sprintf "%s" id.Ident.name)
+ else None)
+ | Nop lam ->
+ if not @@ Lam_analysis.no_side_effects lam then
+ (* (Lam_util.string_of_lambda lam) *)
+ Some ""
+ else None (* TODO :*))
+
+(** Actually simplify_lets is kind of global optimization since it requires you to know whether
+ it's used or not
+*)
+let compile (output_prefix : string) export_idents (lam : Lambda.lambda) =
+ let debug_ir = !Js_config.debug_ir in
+ let diagnostics =
+ if debug_ir then Some (Ir_diagnostics.create ~output_prefix) else None
+ in
+ let d pass lam =
+ (match diagnostics with
+ | Some diagnostics ->
+ Ir_diagnostics.dump_lam diagnostics ~pass lam;
+ Ext_log.dwarn ~__POS__ "START CHECKING PASS %s@." pass
+ | None -> ());
+ if !Js_config.check_lam || debug_ir then (
+ ignore @@ Lam_check.check ~file:!Location.input_name ~pass lam;
+ if debug_ir then Ext_log.dwarn ~__POS__ "FINISH CHECKING PASS %s@." pass);
+ lam
+ in
+ let j pass program =
+ Ext_option.iter diagnostics (fun diagnostics ->
+ Ir_diagnostics.dump_js diagnostics ~pass program);
+ program
+ in
+ let export_ident_sets = Set_ident.of_list export_idents in
+ (* To make toplevel happy - reentrant for js-demo *)
+ let () =
+ if debug_ir then
+ Ext_list.iter export_idents (fun id ->
+ Ext_log.dwarn ~__POS__ "export idents: %s/%d" id.name id.stamp);
+ Lam_compile_env.reset ()
+ in
+ let lam, may_required_modules = Lam_convert.convert export_ident_sets lam in
+
+ let lam = d "initial" lam in
+ let lam = Lam_pass_deep_flatten.deep_flatten lam in
+ let lam = d "flatten0" lam in
+ let meta : Lam_stats.t = Lam_stats.make ~export_idents ~export_ident_sets in
+ let () = Lam_pass_collect.collect_info meta lam in
+ let lam =
+ let lam =
+ lam |> d "flatten1" |> Lam_pass_exits.simplify_exits |> d "simplify_exits"
+ |> (fun lam ->
+ Lam_pass_collect.collect_info meta lam;
+ if debug_ir then
+ Ext_log.dwarn ~__POS__ "Before simplify_alias: %a@." Lam_stats.print
+ meta;
+ lam)
+ |> Lam_pass_remove_alias.simplify_alias meta
+ |> d "simplify_alias" |> Lam_pass_deep_flatten.deep_flatten
+ |> d "flatten2"
+ in
+ (* Inling happens*)
+
+ let () = Lam_pass_collect.collect_info meta lam in
+ let lam = Lam_pass_remove_alias.simplify_alias meta lam in
+ let lam = Lam_pass_deep_flatten.deep_flatten lam in
+ let () = Lam_pass_collect.collect_info meta lam in
+ let lam =
+ lam |> d "alpha_before"
+ |> Lam_pass_alpha_conversion.alpha_conversion meta
+ |> d "alpha_after" |> Lam_pass_exits.simplify_exits
+ in
+ let () = Lam_pass_collect.collect_info meta lam in
+
+ lam |> d "simplify_alias_before"
+ |> Lam_pass_remove_alias.simplify_alias meta
+ |> d "alpha_conversion"
+ |> Lam_pass_alpha_conversion.alpha_conversion meta
+ |> d "before-simplify_lets"
+ (* we should investigate a better way to put different passes : )*)
+ |> Lam_pass_lets_dce.simplify_lets
+ |> d "before-simplify-exits"
+ (* |> (fun lam -> Lam_pass_collect.collect_info meta lam
+ ; Lam_pass_remove_alias.simplify_alias meta lam) *)
+ (* |> Lam_group_pass.scc_pass
+ |> d "scc" *)
+ |> Lam_pass_exits.simplify_exits
+ |> d "simplify_lets"
+ |> fun lam ->
+ if debug_ir then
+ Ext_log.dwarn ~__POS__ "Before coercion: %a@." Lam_stats.print meta;
+ lam
+ in
+
+ let ({Lam_coercion.groups} as coerced_input), meta =
+ Lam_coercion.coerce_and_group_big_lambda meta lam
+ in
+
+ let () =
+ if debug_ir then (
+ Ext_log.dwarn ~__POS__ "After coercion: %a@." Lam_stats.print meta;
+ Ext_option.iter diagnostics (fun diagnostics ->
+ Ir_diagnostics.dump_groups diagnostics coerced_input.groups))
+ in
+ let maybe_pure = no_side_effects groups in
+ let () =
+ if debug_ir then
+ Ext_log.dwarn ~__POS__ "\n@[[TIME:]Pre-compile: %f@]@."
+ (Sys.time () *. 1000.)
+ in
+ let body =
+ Ext_list.map groups (fun group -> compile_group output_prefix meta group)
+ |> Js_output.concat |> Js_output.output_as_block
+ in
+ let () =
+ if debug_ir then
+ Ext_log.dwarn ~__POS__ "\n@[[TIME:]Post-compile: %f@]@."
+ (Sys.time () *. 1000.)
+ in
+ (* The file is not big at all compared with [cmo] *)
+ (* Ext_marshal.to_file (Ext_path.chop_extension filename ^ ".mj") js; *)
+ let meta_exports = meta.exports in
+ let export_set = Set_ident.of_list meta_exports in
+ let js : J.program = {exports = meta_exports; export_set; block = body} in
+ js |> j "initial" |> Js_pass_flatten.program |> j "flatten"
+ |> Js_pass_external_shadow.program |> j "external_shadow"
+ |> Js_pass_tailcall_inline.tailcall_inline |> j "inline_and_shake"
+ |> Js_pass_record_rest.program |> j "record_rest"
+ |> Js_pass_flatten_and_mark_dead.program |> j "flatten_and_mark_dead"
+ (* |> Js_inline_and_eliminate.inline_and_shake *)
+ (* |> j "inline_and_shake" *)
+ |> (fun js ->
+ ignore @@ Js_pass_scope.program js;
+ js)
+ |> Js_shake.shake_program |> j "shake"
+ |> fun (program : J.program) ->
+ let external_module_ids : Lam_module_ident.t list =
+ if !Js_config.all_module_aliases then []
+ else
+ let hard_deps = Js_fold_basic.calculate_hard_dependencies program.block in
+ Lam_compile_env.populate_required_modules may_required_modules hard_deps;
+ Ext_list.sort_via_array (Lam_module_ident.Hash_set.to_list hard_deps)
+ (fun id1 id2 ->
+ Ext_string.compare
+ (Lam_module_ident.name id1)
+ (Lam_module_ident.name id2))
+ in
+ Warnings.check_fatal ();
+ let effect_ =
+ Lam_stats_export.get_dependent_module_effect maybe_pure external_module_ids
+ in
+ let v : Js_cmj_format.t =
+ Lam_stats_export.export_to_cmj meta effect_ coerced_input.export_map
+ (if Ext_char.is_lower_case (Filename.basename output_prefix).[0] then
+ Little
+ else Upper)
+ in
+ if not !Clflags.dont_write_files then
+ Js_cmj_format.to_file ~check_exists:(not !Js_config.force_cmj)
+ (output_prefix ^ Literals.suffix_cmj)
+ v;
+ {J.program; side_effect = effect_; modules = external_module_ids}
+
+let ( // ) = Filename.concat
+
+let remove_stale_source_map ?(remove_stale_map = true) target_file =
+ if remove_stale_map && not !Clflags.dont_write_files then
+ Misc.remove_file (target_file ^ ".map")
+
+let dump_deps_program_with_source_map ?(remove_stale_map = true) ~target_file
+ ~output_prefix module_system lambda_output chan =
+ let builder =
+ Js_source_map.make ~generated_file:target_file
+ ~source_root:!Js_config.source_map_root
+ ~sources_content:!Js_config.source_map_sources_content
+ in
+ Js_source_map.with_builder builder (fun () ->
+ Js_dump_program.pp_deps_program ~output_prefix module_system lambda_output
+ (Ext_pp.from_channel chan));
+ match !Js_config.source_map with
+ | Linked ->
+ let json = Js_source_map.json builder in
+ output_string chan
+ (Js_source_map.linked_comment ~map_file:(target_file ^ ".map"));
+ Ext_io.write_file (target_file ^ ".map") json
+ | Hidden ->
+ Ext_io.write_file (target_file ^ ".map") (Js_source_map.json builder)
+ | Inline ->
+ output_string chan
+ (Js_source_map.inline_comment ~json:(Js_source_map.json builder));
+ remove_stale_source_map ~remove_stale_map target_file
+ | No_source_map -> ()
+
+let lambda_as_module (lambda_output : J.deps_program) (output_prefix : string) :
+ unit =
+ let package_info = Js_packages_state.get_packages_info () in
+ if Js_packages_info.is_empty package_info && !Js_config.js_stdout then
+ match !Js_config.source_map with
+ | Inline ->
+ let target_file =
+ Ext_namespace.change_ext_ns_suffix
+ (Filename.basename output_prefix)
+ Literals.suffix_js
+ in
+ dump_deps_program_with_source_map ~remove_stale_map:false ~target_file
+ ~output_prefix Commonjs lambda_output stdout
+ | _ ->
+ Js_dump_program.dump_deps_program ~output_prefix Commonjs lambda_output
+ stdout
+ else
+ Js_packages_info.iter package_info (fun {module_system; path; suffix} ->
+ let basename =
+ Ext_namespace.change_ext_ns_suffix
+ (Filename.basename output_prefix)
+ suffix
+ in
+ let target_file =
+ Ext_path.package_dir () // path
+ // basename (* #913 only generate little-case js file *)
+ in
+ let output_chan chan =
+ match !Js_config.source_map with
+ | No_source_map ->
+ Js_dump_program.dump_deps_program ~output_prefix module_system
+ lambda_output chan;
+ remove_stale_source_map target_file
+ | Linked | Inline | Hidden ->
+ dump_deps_program_with_source_map ~target_file ~output_prefix
+ module_system lambda_output chan
+ in
+ if not !Clflags.dont_write_files then
+ Ext_pervasives.with_file_as_chan target_file output_chan;
+ if !Warnings.has_warnings then (
+ Warnings.has_warnings := false;
+ (* 5206: When there were warnings found during the compilation, we want the file
+ to be rebuilt on the next "rescript build" so that the warnings keep being shown.
+ Set the timestamp of the ast file to 1970-01-01 to make this rebuild happen.
+ (Do *not* set the timestamp of the JS output file instead
+ as that does not play well with every bundler.) *)
+ let ast_file = output_prefix ^ Literals.suffix_ast in
+ if Sys.file_exists ast_file then Build_artifact.mark_stale ast_file))
+
+(* We can use {!Env.current_unit = "Pervasives"} to tell if it is some specific module,
+ We need handle some definitions in standard libraries in a special way, most are io specific,
+ includes {!Pervasives.stdin, Pervasives.stdout, Pervasives.stderr}
+
+ However, use filename instead of {!Env.current_unit} is more honest, since node-js module system is coupled with the file name
+*)
diff --git a/compiler/core/lam_module_ident.ml b/compiler/core/lam_module_ident.ml
index e92cdb4db0d..4ad1f1e9084 100644
--- a/compiler/core/lam_module_ident.ml
+++ b/compiler/core/lam_module_ident.ml
@@ -64,10 +64,10 @@ module Cmp = struct
match x.kind with
| External {name = x_kind; _} ->
(* The hash collision is rare? *)
- Bs_hash_stubs.hash_string x_kind
+ Ext_platform_primitives.hash_string x_kind
| Ml | Runtime ->
let x_id = x.id in
- Bs_hash_stubs.hash_stamp_and_name x_id.stamp x_id.name
+ Ext_platform_primitives.hash_stamp_and_name x_id.stamp x_id.name
end
module Hash = Hash.Make (Cmp)
diff --git a/compiler/core/platform/native/build_artifact.ml b/compiler/core/platform/native/build_artifact.ml
new file mode 100644
index 00000000000..443d1c801ec
--- /dev/null
+++ b/compiler/core/platform/native/build_artifact.ml
@@ -0,0 +1,2 @@
+(* Mark an AST stale so the build system reports its warnings again. *)
+external mark_stale : string -> unit = "caml_stale_file"
diff --git a/compiler/core/platform/native/js_name_of_module_id.ml b/compiler/core/platform/native/js_name_of_module_id.ml
new file mode 100644
index 00000000000..f75abeba808
--- /dev/null
+++ b/compiler/core/platform/native/js_name_of_module_id.ml
@@ -0,0 +1,139 @@
+(* Copyright (C) 2017 Hongbo Zhang, Authors of ReScript
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In addition to the permissions granted to you by the LGPL, you may combine
+ * or link a "work that uses the Library" with a publicly distributed version
+ * of this file to produce a combined library or application, then distribute
+ * that combined work under the terms of your choosing, with no requirement
+ * to comply with the obligations normally placed on you by section 4 of the
+ * LGPL version 3 (or the corresponding section of a later version of the LGPL
+ * should you choose to use a later version).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
+
+(* Native compiler module-path resolution. *)
+(*
+let (=) (x : int) (y:float) = assert false
+*)
+
+let ( // ) = Filename.concat
+
+let fix_path_for_windows : string -> string =
+ if Ext_sys.is_windows_or_cygwin then Ext_string.replace_backward_slash
+ else fun s -> s
+
+let runtime_suffix_of_module_system
+ (module_system : Js_packages_info.module_system) =
+ match module_system with
+ | Commonjs -> ".cjs"
+ | Esmodule -> ".mjs"
+
+(* dependency is runtime module *)
+let get_runtime_module_path (dep_module_id : Lam_module_ident.t)
+ (current_package_info : Js_packages_info.t)
+ (module_system : Js_packages_info.module_system) =
+ let current_info_query =
+ Js_packages_info.query_package_infos current_package_info module_system
+ in
+ let js_file =
+ Ext_namespace.js_name_of_modulename dep_module_id.id.name Upper
+ (runtime_suffix_of_module_system module_system)
+ in
+ match current_info_query with
+ | Package_not_found -> assert false
+ | Package_script ->
+ Js_packages_info.runtime_package_path module_system js_file
+ | Package_found pkg ->
+ let dep_path =
+ "lib" // Js_packages_info.runtime_dir_of_module_system module_system
+ in
+ if Js_packages_info.is_runtime_package current_package_info then
+ Ext_path.node_rebase_file ~from:pkg.rel_path ~to_:dep_path js_file
+ (* TODO: we assume that both [x] and [path] could only be relative path
+ which is guaranteed by [-bs-package-output]
+ *)
+ else Js_packages_info.runtime_package_path module_system js_file
+
+(* [output_dir] is decided by the command line argument *)
+let string_of_module_id (dep_module_id : Lam_module_ident.t)
+ ~(output_dir : string) (module_system : Js_packages_info.module_system) :
+ string =
+ let current_package_info = Js_packages_state.get_packages_info () in
+ fix_path_for_windows
+ (match dep_module_id.kind with
+ | External {name} -> name (* the literal string for external package *)
+ (* This may not be enough,
+ 1. For cross packages, we may need settle
+ down a single js package
+ 2. We may need es6 path for dead code elimination
+ But frankly, very few JS packages have no dependency,
+ so having plugin may sound not that bad
+ *)
+ | Runtime ->
+ get_runtime_module_path dep_module_id current_package_info module_system
+ | Ml -> (
+ let current_info_query =
+ Js_packages_info.query_package_infos current_package_info module_system
+ in
+ match Lam_compile_env.get_package_path_from_cmj dep_module_id with
+ | package_path, dep_package_info, case -> (
+ let dep_info_query =
+ Js_packages_info.query_package_infos dep_package_info module_system
+ in
+ match (dep_info_query, current_info_query) with
+ | Package_not_found, _ ->
+ Bs_exception.error (Missing_ml_dependency dep_module_id.id.name)
+ | Package_script, Package_found _ ->
+ Bs_exception.error
+ (Dependency_script_module_dependent_not dep_module_id.id.name)
+ | (Package_script | Package_found _), Package_not_found -> assert false
+ | Package_found ({suffix} as pkg), Package_script ->
+ let js_file =
+ Ext_namespace.js_name_of_modulename dep_module_id.id.name case
+ suffix
+ in
+ pkg.pkg_rel_path // js_file
+ | Package_found ({suffix} as dep_pkg), Package_found cur_pkg ->
+ let js_file =
+ Ext_namespace.js_name_of_modulename dep_module_id.id.name case
+ suffix
+ in
+
+ if
+ Js_packages_info.same_package_by_name current_package_info
+ dep_package_info
+ then
+ Ext_path.node_rebase_file ~from:cur_pkg.rel_path
+ ~to_:dep_pkg.rel_path js_file
+ (* TODO: we assume that both [x] and [path] could only be relative path
+ which is guaranteed by [-bs-package-output]
+ *)
+ else if Js_packages_info.is_runtime_package dep_package_info then
+ get_runtime_module_path dep_module_id current_package_info
+ module_system
+ else dep_pkg.pkg_rel_path // js_file
+ | Package_script, Package_script -> (
+ let js_file =
+ Ext_namespace.js_name_of_modulename dep_module_id.id.name case
+ Literals.suffix_js
+ in
+ match Config_util.find_opt js_file with
+ | Some file ->
+ let basename = Filename.basename file in
+ let dirname = Filename.dirname file in
+ Ext_path.node_rebase_file
+ ~from:(Ext_path.absolute_cwd_path output_dir)
+ ~to_:(Ext_path.absolute_cwd_path dirname)
+ basename
+ | None -> Bs_exception.error (Js_not_found js_file)))))
diff --git a/compiler/core/platform/playground/build_artifact.ml b/compiler/core/platform/playground/build_artifact.ml
new file mode 100644
index 00000000000..819e7adac0e
--- /dev/null
+++ b/compiler/core/platform/playground/build_artifact.ml
@@ -0,0 +1,2 @@
+(* The playground compiler does not write build artifacts. *)
+let mark_stale _path = ()
diff --git a/compiler/core/platform/playground/js_name_of_module_id.ml b/compiler/core/platform/playground/js_name_of_module_id.ml
new file mode 100644
index 00000000000..011b3ac5f37
--- /dev/null
+++ b/compiler/core/platform/playground/js_name_of_module_id.ml
@@ -0,0 +1,6 @@
+(* The playground bundle keeps runtime modules under ./stdlib. *)
+let string_of_module_id (module_id : Lam_module_ident.t)
+ ~output_dir:(_ : string) (_module_system : Js_packages_info.module_system) =
+ match module_id.kind with
+ | External {name} -> name
+ | Runtime | Ml -> "./stdlib/" ^ module_id.id.name ^ ".mjs"
diff --git a/compiler/dune b/compiler/dune
index c56cb09eadf..dd56eeb2ce5 100644
--- a/compiler/dune
+++ b/compiler/dune
@@ -13,20 +13,11 @@
(env
(release
- (env-vars
- (CPPO_FLAGS -U=BROWSER))
(ocamlopt_flags
(:standard -O3 -unbox-closures)))
(static
- (env-vars
- (CPPO_FLAGS -U=BROWSER))
(ocamlopt_flags
(:standard -O3 -unbox-closures)))
(browser
- (env-vars
- (CPPO_FLAGS -D=BROWSER))
(ocamlopt_flags
- (:standard -O3 -unbox-closures)))
- (_
- (env-vars
- (CPPO_FLAGS -U=BROWSER))))
+ (:standard -O3 -unbox-closures))))
diff --git a/compiler/ext/bs_hash_stubs.cppo.ml b/compiler/ext/bs_hash_stubs.cppo.ml
deleted file mode 100644
index 6a59fe51ec5..00000000000
--- a/compiler/ext/bs_hash_stubs.cppo.ml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-#ifdef BROWSER
-
-
-let hash_string : string -> int = Hashtbl.hash
-let hash_string_int s i = Hashtbl.hash (s,i)
-let hash_string_small_int : string -> int -> int = hash_string_int
-let hash_stamp_and_name (i:int) (s:string) = Hashtbl.hash(i,s)
-let hash_int (i:int) = Hashtbl.hash i
-let string_length_based_compare (x : string ) (y : string) =
- let len1 = String.length x in
- let len2 = String.length y in
- if len1 = len2 then String.compare x y
- else compare (len1:int) len2
-let int_unsafe_blit: int array -> int -> int array -> int -> int -> unit =
- Array.blit
-
-#else
-external hash_string : string -> int = "caml_bs_hash_string" [@@noalloc];;
-
-external hash_string_int : string -> int -> int = "caml_bs_hash_string_and_int" [@@noalloc];;
-
-external hash_string_small_int : string -> int -> int = "caml_bs_hash_string_and_small_int" [@@noalloc];;
-
-external hash_stamp_and_name : int -> string -> int = "caml_bs_hash_stamp_and_name" [@@noalloc];;
-
-external hash_small_int : int -> int = "caml_bs_hash_small_int" [@@noalloc];;
-
-external hash_int : int -> int = "caml_bs_hash_int" [@@noalloc];;
-
-external string_length_based_compare : string -> string -> int = "caml_string_length_based_compare" [@@noalloc];;
-
-external
- int_unsafe_blit :
- int array -> int -> int array -> int -> int -> unit = "caml_int_array_blit" [@@noalloc];;
-
-external set_as_old_file : string -> unit = "caml_stale_file"
-#endif
-
-
diff --git a/compiler/ext/dune b/compiler/ext/dune
index c6a8a9dfeb9..e8379a964c5 100644
--- a/compiler/ext/dune
+++ b/compiler/ext/dune
@@ -1,50 +1,36 @@
-(library
- (name ext)
- (wrapped false)
- (instrumentation
- (backend bisect_ppx))
- (flags
- (:standard -w +a-4-42-40-9-48-70))
- (foreign_stubs
- (language c)
- (names ext_basic_hash_stubs)))
+(env
+ (_
+ (flags
+ (:standard -w +a-4-42-40-9-48-70))))
-(rule
- (target bs_hash_stubs.ml)
- (deps bs_hash_stubs.cppo.ml)
- (action
- (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target})))
+; The browser profile builds the playground compiler; this rule pair generates a module from platform/{native,playground}.
(rule
- (target js_reserved_map.ml)
- (deps js_reserved_map.cppo.ml)
+ (target ext_platform_primitives.ml)
+ (enabled_if
+ (= %{profile} browser))
(action
- (run
- %{bin:cppo}
- -V
- OCAML:%{ocaml_version}
- %{env:CPPO_FLAGS=}
- %{deps}
- -o
- %{target})))
+ (copy
+ platform/playground/ext_platform_primitives.ml
+ ext_platform_primitives.ml)))
(rule
- (target ext_sys.ml)
- (deps ext_sys.cppo.ml)
+ (target ext_platform_primitives.ml)
+ (enabled_if
+ (<> %{profile} browser))
(action
- (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target})))
+ (copy
+ platform/native/ext_platform_primitives.ml
+ ext_platform_primitives.ml)))
-(rule
- (target ext_string.ml)
- (deps ext_string.cppo.ml)
- (action
- (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target})))
-
-(rule
- (target ext_string.mli)
- (deps ext_string.cppo.mli)
- (action
- (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target})))
+(library
+ (name ext)
+ (wrapped false)
+ (instrumentation
+ (backend bisect_ppx))
+ (foreign_stubs
+ (language c)
+ (names ext_platform_primitives_stubs)))
(rule
(targets hash_set_string.ml)
diff --git a/compiler/ext/ext_basic_hash_stubs.c b/compiler/ext/ext_platform_primitives_stubs.c
similarity index 59%
rename from compiler/ext/ext_basic_hash_stubs.c
rename to compiler/ext/ext_platform_primitives_stubs.c
index 4937997fecd..b1f32eb63d2 100644
--- a/compiler/ext/ext_basic_hash_stubs.c
+++ b/compiler/ext/ext_platform_primitives_stubs.c
@@ -1,12 +1,8 @@
#include "caml/hash.h"
#include "caml/mlvalues.h"
-#include
#include
-#include "caml/memory.h"
-#include "caml/osdeps.h"
-#include "caml/signals.h"
+#include
#include "caml/misc.h"
-#include
typedef uint32_t uint32;
#define FINAL_MIX(h) \
@@ -49,23 +45,6 @@ CAMLprim value caml_bs_hash_string_and_int (value obj, value d){
return Val_int(h & 0x3FFFFFFFU);
}
-CAMLprim value caml_bs_hash_string_and_small_int(value obj, value d){
- uint32 h = 0;
- h = caml_hash_mix_string(h,obj);
- MIX(h,d);
- FINAL_MIX(h);
- return Val_int(h & 0x3FFFFFFFU);
-}
-
-CAMLprim value caml_bs_hash_small_int(value d){
- uint32 h = 0;
- // intnat stamp = Long_val(d);
- // FIXME: unused value
- MIX(h,d);
- FINAL_MIX(h);
- return Val_int(h & 0x3FFFFFFFU);
-}
-
CAMLprim value caml_int_array_blit(
value a1, value ofs1,
value a2, value ofs2,
@@ -142,75 +121,6 @@ CAMLprim value caml_string_length_based_compare(value s1, value s2)
-#include
-#ifdef _WIN32
-#include
-CAMLprim value caml_stale_file(value path)
-{
- CAMLparam1(path);
- struct _utimbuf tv;
- char * p = caml_stat_strdup(String_val(path));
- tv.modtime = 0;
- caml_enter_blocking_section();
- _utime(p, &tv);
- caml_leave_blocking_section();
- caml_stat_free(p);
- CAMLreturn(Val_unit);
-}
-#else
-CAMLprim value caml_stale_file(value path)
-{
- CAMLparam1(path);
- struct timeval tv[2];
- char * p = caml_stat_strdup_to_os(String_val(path));
- // unicode friendly
- tv[0].tv_sec = 0.0;
- tv[0].tv_usec = 0.0;
- tv[1].tv_sec = 0.0;
- tv[1].tv_usec = 0.0;
- // caml_enter_blocking_section();
- // not needed for single thread
- utimes(p, tv);
- // caml_leave_blocking_section();
- // not needed for single thread
- caml_stat_free(p);
- // TODO: error checking
- CAMLreturn(Val_unit);
-}
-#endif
-
-
-CAMLprim value caml_sys_is_directory_no_exn(value name)
-{
- CAMLparam1(name);
-#ifdef _WIN32
- struct _stati64 st;
-#else
- struct stat st;
-#endif
- char_os * p;
- int ret;
-
-
- if(!caml_string_is_c_safe(name)){
- CAMLreturn(Val_false);
- }
-
- p = caml_stat_strdup_to_os(String_val(name));
- caml_enter_blocking_section();
- ret = stat_os(p, &st);
- caml_leave_blocking_section();
- caml_stat_free(p);
-
- if (ret == -1) CAMLreturn(Val_false);
-#ifdef S_ISDIR
- CAMLreturn(Val_bool(S_ISDIR(st.st_mode)));
-#else
- CAMLreturn(Val_bool(st.st_mode & S_IFDIR));
-#endif
-}
/* local variables: */
-/* compile-command: "ocamlopt.opt -c ext_basic_hash_stubs.c" */
+/* compile-command: "ocamlopt.opt -c ext_platform_primitives_stubs.c" */
/* end: */
-
-
diff --git a/compiler/ext/ext_string.cppo.ml b/compiler/ext/ext_string.cppo.ml
deleted file mode 100644
index dcc7ab00e40..00000000000
--- a/compiler/ext/ext_string.cppo.ml
+++ /dev/null
@@ -1,530 +0,0 @@
-(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
- * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * In addition to the permissions granted to you by the LGPL, you may combine
- * or link a "work that uses the Library" with a publicly distributed version
- * of this file to produce a combined library or application, then distribute
- * that combined work under the terms of your choosing, with no requirement
- * to comply with the obligations normally placed on you by section 4 of the
- * LGPL version 3 (or the corresponding section of a later version of the LGPL
- * should you choose to use a later version).
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
-
-
-
-
-
-
-
-(*
- {[ split " test_unsafe_obj_ffi_ppx.cmi" ~keep_empty:false ' ']}
-*)
-let split_by ?(keep_empty=false) is_delim str =
- let len = String.length str in
- let rec loop acc last_pos pos =
- if pos = -1 then
- if last_pos = 0 && not keep_empty then
-
- acc
- else
- String.sub str 0 last_pos :: acc
- else
- if is_delim str.[pos] then
- let new_len = (last_pos - pos - 1) in
- if new_len <> 0 || keep_empty then
- let v = String.sub str (pos + 1) new_len in
- loop ( v :: acc)
- pos (pos - 1)
- else loop acc pos (pos - 1)
- else loop acc last_pos (pos - 1)
- in
- loop [] len (len - 1)
-
-let trim s =
- let i = ref 0 in
- let j = String.length s in
- while !i < j &&
- let u = String.unsafe_get s !i in
- u = '\t' || u = '\n' || u = ' '
- do
- incr i;
- done;
- let k = ref (j - 1) in
- while !k >= !i &&
- let u = String.unsafe_get s !k in
- u = '\t' || u = '\n' || u = ' ' do
- decr k ;
- done;
- String.sub s !i (!k - !i + 1)
-
-let split ?keep_empty str on =
- if str = "" then [] else
- split_by ?keep_empty (fun x -> (x : char) = on) str ;;
-
-let quick_split_by_ws str : string list =
- split_by ~keep_empty:false (fun x -> x = '\t' || x = '\n' || x = ' ') str
-
-let starts_with s beg =
- let beg_len = String.length beg in
- let s_len = String.length s in
- beg_len <= s_len &&
- (let i = ref 0 in
- while !i < beg_len
- && String.unsafe_get s !i =
- String.unsafe_get beg !i do
- incr i
- done;
- !i = beg_len
- )
-
-let rec ends_aux s end_ j k =
- if k < 0 then (j + 1)
- else if String.unsafe_get s j = String.unsafe_get end_ k then
- ends_aux s end_ (j - 1) (k - 1)
- else -1
-
-(** return an index which is minus when [s] does not
- end with [beg]
-*)
-let ends_with_index s end_ : int =
- let s_finish = String.length s - 1 in
- let s_beg = String.length end_ - 1 in
- if s_beg > s_finish then -1
- else
- ends_aux s end_ s_finish s_beg
-
-let ends_with s end_ = ends_with_index s end_ >= 0
-
-let ends_with_then_chop s beg =
- let i = ends_with_index s beg in
- if i >= 0 then Some (String.sub s 0 i)
- else None
-
-(* let check_suffix_case = ends_with *)
-(* let check_suffix_case_then_chop = ends_with_then_chop *)
-
-(* let check_any_suffix_case s suffixes =
- Ext_list.exists suffixes (fun x -> check_suffix_case s x) *)
-
-(* let check_any_suffix_case_then_chop s suffixes =
- let rec aux suffixes =
- match suffixes with
- | [] -> None
- | x::xs ->
- let id = ends_with_index s x in
- if id >= 0 then Some (String.sub s 0 id)
- else aux xs in
- aux suffixes *)
-
-
-
-
-(* it is unsafe to expose such API as unsafe since
- user can provide bad input range
-
-*)
-let rec unsafe_for_all_range s ~start ~finish p =
- start > finish ||
- p (String.unsafe_get s start) &&
- unsafe_for_all_range s ~start:(start + 1) ~finish p
-
-let for_all_from s start p =
- let len = String.length s in
- if start < 0 then invalid_arg "Ext_string.for_all_from"
- else unsafe_for_all_range s ~start ~finish:(len - 1) p
-
-
-let for_all s (p : char -> bool) =
- unsafe_for_all_range s ~start:0 ~finish:(String.length s - 1) p
-
-let is_empty s = String.length s = 0
-
-
-let repeat n s =
- let len = String.length s in
- let res = Bytes.create(n * len) in
- for i = 0 to pred n do
- String.blit s 0 res (i * len) len
- done;
- Bytes.to_string res
-
-
-
-
-let unsafe_is_sub ~sub i s j ~len =
- let rec check k =
- if k = len
- then true
- else
- String.unsafe_get sub (i+k) =
- String.unsafe_get s (j+k) && check (k+1)
- in
- j+len <= String.length s && check 0
-
-
-
-let find ?(start=0) ~sub s =
- let exception Local_exit in
- let n = String.length sub in
- let s_len = String.length s in
- let i = ref start in
- try
- while !i + n <= s_len do
- if unsafe_is_sub ~sub 0 s !i ~len:n then
- raise_notrace Local_exit;
- incr i
- done;
- -1
- with Local_exit ->
- !i
-
-let contain_substring s sub =
- find s ~sub >= 0
-
-(** TODO: optimize
- avoid nonterminating when string is empty
-*)
-let non_overlap_count ~sub s =
- let sub_len = String.length sub in
- let rec aux acc off =
- let i = find ~start:off ~sub s in
- if i < 0 then acc
- else aux (acc + 1) (i + sub_len) in
- if String.length sub = 0 then invalid_arg "Ext_string.non_overlap_count"
- else aux 0 0
-
-
-let rfind ~sub s =
- let exception Local_exit in
- let n = String.length sub in
- let i = ref (String.length s - n) in
- try
- while !i >= 0 do
- if unsafe_is_sub ~sub 0 s !i ~len:n then
- raise_notrace Local_exit;
- decr i
- done;
- -1
- with Local_exit ->
- !i
-
-let tail_from s x =
- let len = String.length s in
- if x > len then invalid_arg ("Ext_string.tail_from " ^s ^ " : "^ string_of_int x )
- else String.sub s x (len - x)
-
-let equal (x : string) y = x = y
-
-(* let rec index_rec s lim i c =
- if i >= lim then -1 else
- if String.unsafe_get s i = c then i
- else index_rec s lim (i + 1) c *)
-
-
-
-let rec index_rec_count s lim i c count =
- if i >= lim then -1 else
- if String.unsafe_get s i = c then
- if count = 1 then i
- else index_rec_count s lim (i + 1) c (count - 1)
- else index_rec_count s lim (i + 1) c count
-
-let index_count s i c count =
- let lim = String.length s in
- if i < 0 || i >= lim || count < 1 then
- invalid_arg ("index_count: ( " ^string_of_int i ^ "," ^string_of_int count ^ ")" );
- index_rec_count s lim i c count
-
-(* let index_next s i c =
- index_count s i c 1 *)
-
-(* let extract_until s cursor c =
- let len = String.length s in
- let start = !cursor in
- if start < 0 || start >= len then (
- cursor := -1;
- ""
- )
- else
- let i = index_rec s len start c in
- let finish =
- if i < 0 then (
- cursor := -1 ;
- len
- )
- else (
- cursor := i + 1;
- i
- ) in
- String.sub s start (finish - start) *)
-
-let rec rindex_rec s i c =
- if i < 0 then i else
- if String.unsafe_get s i = c then i else rindex_rec s (i - 1) c;;
-
-let rec rindex_rec_opt s i c =
- if i < 0 then None else
- if String.unsafe_get s i = c then Some i else rindex_rec_opt s (i - 1) c;;
-
-let rindex_neg s c =
- rindex_rec s (String.length s - 1) c;;
-
-let rindex_opt s c =
- rindex_rec_opt s (String.length s - 1) c;;
-
-
-(** TODO: can be improved to return a positive integer instead *)
-let rec unsafe_no_char x ch i last_idx =
- i > last_idx ||
- (String.unsafe_get x i <> ch && unsafe_no_char x ch (i + 1) last_idx)
-
-let rec unsafe_no_char_idx x ch i last_idx =
- if i > last_idx then -1
- else
- if String.unsafe_get x i <> ch then
- unsafe_no_char_idx x ch (i + 1) last_idx
- else i
-
-let no_char x ch i len : bool =
- let str_len = String.length x in
- if i < 0 || i >= str_len || len >= str_len then invalid_arg "Ext_string.no_char"
- else unsafe_no_char x ch i len
-
-
-let no_slash x =
- unsafe_no_char x '/' 0 (String.length x - 1)
-
-let no_slash_idx x =
- unsafe_no_char_idx x '/' 0 (String.length x - 1)
-
-let no_slash_idx_from x from =
- let last_idx = String.length x - 1 in
- assert (from >= 0);
- unsafe_no_char_idx x '/' from last_idx
-
-let replace_slash_backward (x : string ) =
- let len = String.length x in
- if unsafe_no_char x '/' 0 (len - 1) then x
- else
- String.map (function
- | '/' -> '\\'
- | x -> x ) x
-
-let replace_backward_slash (x : string)=
- let len = String.length x in
- if unsafe_no_char x '\\' 0 (len -1) then x
- else
- String.map (function
- |'\\'-> '/'
- | x -> x) x
-
-let empty = ""
-
-#ifdef BROWSER
-let compare = Bs_hash_stubs.string_length_based_compare
-#else
-external compare : string -> string -> int = "caml_string_length_based_compare" [@@noalloc];;
-#endif
-let single_space = " "
-let single_colon = ":"
-
-let concat_array sep (s : string array) =
- let s_len = Array.length s in
- match s_len with
- | 0 -> empty
- | 1 -> Array.unsafe_get s 0
- | _ ->
- let sep_len = String.length sep in
- let len = ref 0 in
- for i = 0 to s_len - 1 do
- len := !len + String.length (Array.unsafe_get s i)
- done;
- let target =
- Bytes.create
- (!len + (s_len - 1) * sep_len ) in
- let hd = (Array.unsafe_get s 0) in
- let hd_len = String.length hd in
- String.unsafe_blit hd 0 target 0 hd_len;
- let current_offset = ref hd_len in
- for i = 1 to s_len - 1 do
- String.unsafe_blit sep 0 target !current_offset sep_len;
- let cur = Array.unsafe_get s i in
- let cur_len = String.length cur in
- let new_off_set = (!current_offset + sep_len ) in
- String.unsafe_blit cur 0 target new_off_set cur_len;
- current_offset :=
- new_off_set + cur_len ;
- done;
- Bytes.unsafe_to_string target
-
-let concat3 a b c =
- let a_len = String.length a in
- let b_len = String.length b in
- let c_len = String.length c in
- let len = a_len + b_len + c_len in
- let target = Bytes.create len in
- String.unsafe_blit a 0 target 0 a_len ;
- String.unsafe_blit b 0 target a_len b_len;
- String.unsafe_blit c 0 target (a_len + b_len) c_len;
- Bytes.unsafe_to_string target
-
-let concat4 a b c d =
- let a_len = String.length a in
- let b_len = String.length b in
- let c_len = String.length c in
- let d_len = String.length d in
- let len = a_len + b_len + c_len + d_len in
-
- let target = Bytes.create len in
- String.unsafe_blit a 0 target 0 a_len ;
- String.unsafe_blit b 0 target a_len b_len;
- String.unsafe_blit c 0 target (a_len + b_len) c_len;
- String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
- Bytes.unsafe_to_string target
-
-
-let concat5 a b c d e =
- let a_len = String.length a in
- let b_len = String.length b in
- let c_len = String.length c in
- let d_len = String.length d in
- let e_len = String.length e in
- let len = a_len + b_len + c_len + d_len + e_len in
-
- let target = Bytes.create len in
- String.unsafe_blit a 0 target 0 a_len ;
- String.unsafe_blit b 0 target a_len b_len;
- String.unsafe_blit c 0 target (a_len + b_len) c_len;
- String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
- String.unsafe_blit e 0 target (a_len + b_len + c_len + d_len) e_len;
- Bytes.unsafe_to_string target
-
-
-
-let inter2 a b =
- concat3 a single_space b
-
-
-let inter3 a b c =
- concat5 a single_space b single_space c
-
-
-
-
-
-let inter4 a b c d =
- concat_array single_space [| a; b ; c; d|]
-
-
-let parent_dir_lit = ".."
-let current_dir_lit = "."
-
-
-(* reference {!Bytes.unppercase} *)
-let capitalize_ascii (s : string) : string =
- if String.length s = 0 then s
- else
- begin
- let c = String.unsafe_get s 0 in
- if (c >= 'a' && c <= 'z')
- || (c >= '\224' && c <= '\246')
- || (c >= '\248' && c <= '\254') then
- let uc = Char.unsafe_chr (Char.code c - 32) in
- let bytes = Bytes.of_string s in
- Bytes.unsafe_set bytes 0 uc;
- Bytes.unsafe_to_string bytes
- else s
- end
-
-let capitalize_sub (s : string) len : string =
- let slen = String.length s in
- if len < 0 || len > slen then invalid_arg "Ext_string.capitalize_sub"
- else
- if len = 0 then ""
- else
- let bytes = Bytes.create len in
- let uc =
- let c = String.unsafe_get s 0 in
- if (c >= 'a' && c <= 'z')
- || (c >= '\224' && c <= '\246')
- || (c >= '\248' && c <= '\254') then
- Char.unsafe_chr (Char.code c - 32) else c in
- Bytes.unsafe_set bytes 0 uc;
- for i = 1 to len - 1 do
- Bytes.unsafe_set bytes i (String.unsafe_get s i)
- done ;
- Bytes.unsafe_to_string bytes
-
-
-
-let uncapitalize_ascii =
- String.uncapitalize_ascii
-
-let lowercase_ascii = String.lowercase_ascii
-
-external (.![]) : string -> int -> int = "%string_unsafe_get"
-
-let get_int_1_unsafe (x : string) off : int =
- x.![off]
-
-let get_int_2_unsafe (x : string) off : int =
- x.![off] lor
- x.![off+1] lsl 8
-
-let get_int_3_unsafe (x : string) off : int =
- x.![off] lor
- x.![off+1] lsl 8 lor
- x.![off+2] lsl 16
-
-
-let get_int_4_unsafe (x : string) off : int =
- x.![off] lor
- x.![off+1] lsl 8 lor
- x.![off+2] lsl 16 lor
- x.![off+3] lsl 24
-
-let get_1_2_3_4 (x : string) ~off len : int =
- if len = 1 then get_int_1_unsafe x off
- else if len = 2 then get_int_2_unsafe x off
- else if len = 3 then get_int_3_unsafe x off
- else if len = 4 then get_int_4_unsafe x off
- else assert false
-
-let unsafe_sub x offs len =
- let b = Bytes.create len in
- Ext_bytes.unsafe_blit_string x offs b 0 len;
- (Bytes.unsafe_to_string b)
-
-let is_valid_hash_number (x:string) =
- let len = String.length x in
- len > 0 && (
- let a = x.![0] in
- a <= 57 &&
- (if len > 1 then
- a > 48 &&
- for_all_from x 1 (function '0' .. '9' -> true | _ -> false)
- else
- a >= 48 )
- )
-
-
-let hash_number_as_i32_exn
- ( x : string) : int32 =
- Int32.of_string x
-
-
-let first_marshal_char (x : string) =
- x <> "" &&
- ( String.unsafe_get x 0 = '\132')
diff --git a/compiler/ext/ext_string.ml b/compiler/ext/ext_string.ml
new file mode 100644
index 00000000000..ec564df90b4
--- /dev/null
+++ b/compiler/ext/ext_string.ml
@@ -0,0 +1,469 @@
+(* Copyright (C) 2015 - 2016 Bloomberg Finance L.P.
+ * Copyright (C) 2017 - Hongbo Zhang, Authors of ReScript
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In addition to the permissions granted to you by the LGPL, you may combine
+ * or link a "work that uses the Library" with a publicly distributed version
+ * of this file to produce a combined library or application, then distribute
+ * that combined work under the terms of your choosing, with no requirement
+ * to comply with the obligations normally placed on you by section 4 of the
+ * LGPL version 3 (or the corresponding section of a later version of the LGPL
+ * should you choose to use a later version).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
+
+(*
+ {[ split " test_unsafe_obj_ffi_ppx.cmi" ~keep_empty:false ' ']}
+*)
+let split_by ?(keep_empty = false) is_delim str =
+ let len = String.length str in
+ let rec loop acc last_pos pos =
+ if pos = -1 then
+ if last_pos = 0 && not keep_empty then acc
+ else String.sub str 0 last_pos :: acc
+ else if is_delim str.[pos] then
+ let new_len = last_pos - pos - 1 in
+ if new_len <> 0 || keep_empty then
+ let v = String.sub str (pos + 1) new_len in
+ loop (v :: acc) pos (pos - 1)
+ else loop acc pos (pos - 1)
+ else loop acc last_pos (pos - 1)
+ in
+ loop [] len (len - 1)
+
+let trim s =
+ let i = ref 0 in
+ let j = String.length s in
+ while
+ !i < j
+ &&
+ let u = String.unsafe_get s !i in
+ u = '\t' || u = '\n' || u = ' '
+ do
+ incr i
+ done;
+ let k = ref (j - 1) in
+ while
+ !k >= !i
+ &&
+ let u = String.unsafe_get s !k in
+ u = '\t' || u = '\n' || u = ' '
+ do
+ decr k
+ done;
+ String.sub s !i (!k - !i + 1)
+
+let split ?keep_empty str on =
+ if str = "" then [] else split_by ?keep_empty (fun x -> (x : char) = on) str
+
+let quick_split_by_ws str : string list =
+ split_by ~keep_empty:false (fun x -> x = '\t' || x = '\n' || x = ' ') str
+
+let starts_with s beg =
+ let beg_len = String.length beg in
+ let s_len = String.length s in
+ beg_len <= s_len
+ &&
+ let i = ref 0 in
+ while !i < beg_len && String.unsafe_get s !i = String.unsafe_get beg !i do
+ incr i
+ done;
+ !i = beg_len
+
+let rec ends_aux s end_ j k =
+ if k < 0 then j + 1
+ else if String.unsafe_get s j = String.unsafe_get end_ k then
+ ends_aux s end_ (j - 1) (k - 1)
+ else -1
+
+(** return an index which is minus when [s] does not
+ end with [beg]
+*)
+let ends_with_index s end_ : int =
+ let s_finish = String.length s - 1 in
+ let s_beg = String.length end_ - 1 in
+ if s_beg > s_finish then -1 else ends_aux s end_ s_finish s_beg
+
+let ends_with s end_ = ends_with_index s end_ >= 0
+
+let ends_with_then_chop s beg =
+ let i = ends_with_index s beg in
+ if i >= 0 then Some (String.sub s 0 i) else None
+
+(* let check_suffix_case = ends_with *)
+(* let check_suffix_case_then_chop = ends_with_then_chop *)
+
+(* let check_any_suffix_case s suffixes =
+ Ext_list.exists suffixes (fun x -> check_suffix_case s x) *)
+
+(* let check_any_suffix_case_then_chop s suffixes =
+ let rec aux suffixes =
+ match suffixes with
+ | [] -> None
+ | x::xs ->
+ let id = ends_with_index s x in
+ if id >= 0 then Some (String.sub s 0 id)
+ else aux xs in
+ aux suffixes *)
+
+(* it is unsafe to expose such API as unsafe since
+ user can provide bad input range
+
+*)
+let rec unsafe_for_all_range s ~start ~finish p =
+ start > finish
+ || p (String.unsafe_get s start)
+ && unsafe_for_all_range s ~start:(start + 1) ~finish p
+
+let for_all_from s start p =
+ let len = String.length s in
+ if start < 0 then invalid_arg "Ext_string.for_all_from"
+ else unsafe_for_all_range s ~start ~finish:(len - 1) p
+
+let for_all s (p : char -> bool) =
+ unsafe_for_all_range s ~start:0 ~finish:(String.length s - 1) p
+
+let is_empty s = String.length s = 0
+
+let repeat n s =
+ let len = String.length s in
+ let res = Bytes.create (n * len) in
+ for i = 0 to pred n do
+ String.blit s 0 res (i * len) len
+ done;
+ Bytes.to_string res
+
+let unsafe_is_sub ~sub i s j ~len =
+ let rec check k =
+ if k = len then true
+ else
+ String.unsafe_get sub (i + k) = String.unsafe_get s (j + k)
+ && check (k + 1)
+ in
+ j + len <= String.length s && check 0
+
+let find ?(start = 0) ~sub s =
+ let exception Local_exit in
+ let n = String.length sub in
+ let s_len = String.length s in
+ let i = ref start in
+ try
+ while !i + n <= s_len do
+ if unsafe_is_sub ~sub 0 s !i ~len:n then raise_notrace Local_exit;
+ incr i
+ done;
+ -1
+ with Local_exit -> !i
+
+let contain_substring s sub = find s ~sub >= 0
+
+(** TODO: optimize
+ avoid nonterminating when string is empty
+*)
+let non_overlap_count ~sub s =
+ let sub_len = String.length sub in
+ let rec aux acc off =
+ let i = find ~start:off ~sub s in
+ if i < 0 then acc else aux (acc + 1) (i + sub_len)
+ in
+ if String.length sub = 0 then invalid_arg "Ext_string.non_overlap_count"
+ else aux 0 0
+
+let rfind ~sub s =
+ let exception Local_exit in
+ let n = String.length sub in
+ let i = ref (String.length s - n) in
+ try
+ while !i >= 0 do
+ if unsafe_is_sub ~sub 0 s !i ~len:n then raise_notrace Local_exit;
+ decr i
+ done;
+ -1
+ with Local_exit -> !i
+
+let tail_from s x =
+ let len = String.length s in
+ if x > len then
+ invalid_arg ("Ext_string.tail_from " ^ s ^ " : " ^ string_of_int x)
+ else String.sub s x (len - x)
+
+let equal (x : string) y = x = y
+
+(* let rec index_rec s lim i c =
+ if i >= lim then -1 else
+ if String.unsafe_get s i = c then i
+ else index_rec s lim (i + 1) c *)
+
+let rec index_rec_count s lim i c count =
+ if i >= lim then -1
+ else if String.unsafe_get s i = c then
+ if count = 1 then i else index_rec_count s lim (i + 1) c (count - 1)
+ else index_rec_count s lim (i + 1) c count
+
+let index_count s i c count =
+ let lim = String.length s in
+ if i < 0 || i >= lim || count < 1 then
+ invalid_arg
+ ("index_count: ( " ^ string_of_int i ^ "," ^ string_of_int count ^ ")");
+ index_rec_count s lim i c count
+
+(* let index_next s i c =
+ index_count s i c 1 *)
+
+(* let extract_until s cursor c =
+ let len = String.length s in
+ let start = !cursor in
+ if start < 0 || start >= len then (
+ cursor := -1;
+ ""
+ )
+ else
+ let i = index_rec s len start c in
+ let finish =
+ if i < 0 then (
+ cursor := -1 ;
+ len
+ )
+ else (
+ cursor := i + 1;
+ i
+ ) in
+ String.sub s start (finish - start) *)
+
+let rec rindex_rec s i c =
+ if i < 0 then i
+ else if String.unsafe_get s i = c then i
+ else rindex_rec s (i - 1) c
+
+let rec rindex_rec_opt s i c =
+ if i < 0 then None
+ else if String.unsafe_get s i = c then Some i
+ else rindex_rec_opt s (i - 1) c
+
+let rindex_neg s c = rindex_rec s (String.length s - 1) c
+
+let rindex_opt s c = rindex_rec_opt s (String.length s - 1) c
+
+(** TODO: can be improved to return a positive integer instead *)
+let rec unsafe_no_char x ch i last_idx =
+ i > last_idx
+ || (String.unsafe_get x i <> ch && unsafe_no_char x ch (i + 1) last_idx)
+
+let rec unsafe_no_char_idx x ch i last_idx =
+ if i > last_idx then -1
+ else if String.unsafe_get x i <> ch then
+ unsafe_no_char_idx x ch (i + 1) last_idx
+ else i
+
+let no_char x ch i len : bool =
+ let str_len = String.length x in
+ if i < 0 || i >= str_len || len >= str_len then
+ invalid_arg "Ext_string.no_char"
+ else unsafe_no_char x ch i len
+
+let no_slash x = unsafe_no_char x '/' 0 (String.length x - 1)
+
+let no_slash_idx x = unsafe_no_char_idx x '/' 0 (String.length x - 1)
+
+let no_slash_idx_from x from =
+ let last_idx = String.length x - 1 in
+ assert (from >= 0);
+ unsafe_no_char_idx x '/' from last_idx
+
+let replace_slash_backward (x : string) =
+ let len = String.length x in
+ if unsafe_no_char x '/' 0 (len - 1) then x
+ else
+ String.map
+ (function
+ | '/' -> '\\'
+ | x -> x)
+ x
+
+let replace_backward_slash (x : string) =
+ let len = String.length x in
+ if unsafe_no_char x '\\' 0 (len - 1) then x
+ else
+ String.map
+ (function
+ | '\\' -> '/'
+ | x -> x)
+ x
+
+let empty = ""
+
+let compare = Ext_platform_primitives.string_length_based_compare
+let single_space = " "
+let single_colon = ":"
+
+let concat_array sep (s : string array) =
+ let s_len = Array.length s in
+ match s_len with
+ | 0 -> empty
+ | 1 -> Array.unsafe_get s 0
+ | _ ->
+ let sep_len = String.length sep in
+ let len = ref 0 in
+ for i = 0 to s_len - 1 do
+ len := !len + String.length (Array.unsafe_get s i)
+ done;
+ let target = Bytes.create (!len + ((s_len - 1) * sep_len)) in
+ let hd = Array.unsafe_get s 0 in
+ let hd_len = String.length hd in
+ String.unsafe_blit hd 0 target 0 hd_len;
+ let current_offset = ref hd_len in
+ for i = 1 to s_len - 1 do
+ String.unsafe_blit sep 0 target !current_offset sep_len;
+ let cur = Array.unsafe_get s i in
+ let cur_len = String.length cur in
+ let new_off_set = !current_offset + sep_len in
+ String.unsafe_blit cur 0 target new_off_set cur_len;
+ current_offset := new_off_set + cur_len
+ done;
+ Bytes.unsafe_to_string target
+
+let concat3 a b c =
+ let a_len = String.length a in
+ let b_len = String.length b in
+ let c_len = String.length c in
+ let len = a_len + b_len + c_len in
+ let target = Bytes.create len in
+ String.unsafe_blit a 0 target 0 a_len;
+ String.unsafe_blit b 0 target a_len b_len;
+ String.unsafe_blit c 0 target (a_len + b_len) c_len;
+ Bytes.unsafe_to_string target
+
+let concat4 a b c d =
+ let a_len = String.length a in
+ let b_len = String.length b in
+ let c_len = String.length c in
+ let d_len = String.length d in
+ let len = a_len + b_len + c_len + d_len in
+
+ let target = Bytes.create len in
+ String.unsafe_blit a 0 target 0 a_len;
+ String.unsafe_blit b 0 target a_len b_len;
+ String.unsafe_blit c 0 target (a_len + b_len) c_len;
+ String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
+ Bytes.unsafe_to_string target
+
+let concat5 a b c d e =
+ let a_len = String.length a in
+ let b_len = String.length b in
+ let c_len = String.length c in
+ let d_len = String.length d in
+ let e_len = String.length e in
+ let len = a_len + b_len + c_len + d_len + e_len in
+
+ let target = Bytes.create len in
+ String.unsafe_blit a 0 target 0 a_len;
+ String.unsafe_blit b 0 target a_len b_len;
+ String.unsafe_blit c 0 target (a_len + b_len) c_len;
+ String.unsafe_blit d 0 target (a_len + b_len + c_len) d_len;
+ String.unsafe_blit e 0 target (a_len + b_len + c_len + d_len) e_len;
+ Bytes.unsafe_to_string target
+
+let inter2 a b = concat3 a single_space b
+
+let inter3 a b c = concat5 a single_space b single_space c
+
+let inter4 a b c d = concat_array single_space [|a; b; c; d|]
+
+let parent_dir_lit = ".."
+let current_dir_lit = "."
+
+(* reference {!Bytes.unppercase} *)
+let capitalize_ascii (s : string) : string =
+ if String.length s = 0 then s
+ else
+ let c = String.unsafe_get s 0 in
+ if
+ (c >= 'a' && c <= 'z')
+ || (c >= '\224' && c <= '\246')
+ || (c >= '\248' && c <= '\254')
+ then (
+ let uc = Char.unsafe_chr (Char.code c - 32) in
+ let bytes = Bytes.of_string s in
+ Bytes.unsafe_set bytes 0 uc;
+ Bytes.unsafe_to_string bytes)
+ else s
+
+let capitalize_sub (s : string) len : string =
+ let slen = String.length s in
+ if len < 0 || len > slen then invalid_arg "Ext_string.capitalize_sub"
+ else if len = 0 then ""
+ else
+ let bytes = Bytes.create len in
+ let uc =
+ let c = String.unsafe_get s 0 in
+ if
+ (c >= 'a' && c <= 'z')
+ || (c >= '\224' && c <= '\246')
+ || (c >= '\248' && c <= '\254')
+ then Char.unsafe_chr (Char.code c - 32)
+ else c
+ in
+ Bytes.unsafe_set bytes 0 uc;
+ for i = 1 to len - 1 do
+ Bytes.unsafe_set bytes i (String.unsafe_get s i)
+ done;
+ Bytes.unsafe_to_string bytes
+
+let uncapitalize_ascii = String.uncapitalize_ascii
+
+let lowercase_ascii = String.lowercase_ascii
+
+external ( .![] ) : string -> int -> int = "%string_unsafe_get"
+
+let get_int_1_unsafe (x : string) off : int = x.![off]
+
+let get_int_2_unsafe (x : string) off : int = x.![off] lor (x.![off + 1] lsl 8)
+
+let get_int_3_unsafe (x : string) off : int =
+ x.![off] lor (x.![off + 1] lsl 8) lor (x.![off + 2] lsl 16)
+
+let get_int_4_unsafe (x : string) off : int =
+ x.![off]
+ lor (x.![off + 1] lsl 8)
+ lor (x.![off + 2] lsl 16)
+ lor (x.![off + 3] lsl 24)
+
+let get_1_2_3_4 (x : string) ~off len : int =
+ if len = 1 then get_int_1_unsafe x off
+ else if len = 2 then get_int_2_unsafe x off
+ else if len = 3 then get_int_3_unsafe x off
+ else if len = 4 then get_int_4_unsafe x off
+ else assert false
+
+let unsafe_sub x offs len =
+ let b = Bytes.create len in
+ Ext_bytes.unsafe_blit_string x offs b 0 len;
+ Bytes.unsafe_to_string b
+
+let is_valid_hash_number (x : string) =
+ let len = String.length x in
+ len > 0
+ &&
+ let a = x.![0] in
+ a <= 57
+ &&
+ if len > 1 then
+ a > 48
+ && for_all_from x 1 (function
+ | '0' .. '9' -> true
+ | _ -> false)
+ else a >= 48
+
+let hash_number_as_i32_exn (x : string) : int32 = Int32.of_string x
+
+let first_marshal_char (x : string) = x <> "" && String.unsafe_get x 0 = '\132'
diff --git a/compiler/ext/ext_string.cppo.mli b/compiler/ext/ext_string.mli
similarity index 71%
rename from compiler/ext/ext_string.cppo.mli
rename to compiler/ext/ext_string.mli
index 7099464a239..20fdf3e49b6 100644
--- a/compiler/ext/ext_string.cppo.mli
+++ b/compiler/ext/ext_string.mli
@@ -22,42 +22,32 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
-
-
-
-
-
-
-
(** Extension to the standard library [String] module, fixed some bugs like
- avoiding locale sensitivity *)
+ avoiding locale sensitivity *)
-(** default is false *)
val split_by : ?keep_empty:bool -> (char -> bool) -> string -> string list
+(** default is false *)
-
+val trim : string -> string
(** remove whitespace letters ('\t', '\n', ' ') on both side*)
-val trim : string -> string
-
-(** default is false *)
val split : ?keep_empty:bool -> string -> char -> string list
+(** default is false *)
+val quick_split_by_ws : string -> string list
(** split by space chars for quick scripting *)
-val quick_split_by_ws : string -> string list
-
-
val starts_with : string -> string -> bool
+val ends_with_index : string -> string -> int
(**
return [-1] when not found, the returned index is useful
see [ends_with_then_chop]
*)
-val ends_with_index : string -> string -> int
val ends_with : string -> string -> bool
+val ends_with_then_chop : string -> string -> string option
(**
[ends_with_then_chop name ext]
@example:
@@ -67,30 +57,19 @@ val ends_with : string -> string -> bool
]}
This is useful in controlled or file case sensitve system
*)
-val ends_with_then_chop : string -> string -> string option
-
-
-
+val for_all_from : string -> int -> (char -> bool) -> bool
(**
[for_all_from s start p]
if [start] is negative, it raises,
if [start] is too large, it returns true
*)
-val for_all_from:
- string ->
- int ->
- (char -> bool) ->
- bool
-val for_all :
- string ->
- (char -> bool) ->
- bool
+val for_all : string -> (char -> bool) -> bool
val is_empty : string -> bool
-val repeat : int -> string -> string
+val repeat : int -> string -> string
val equal : string -> string -> bool
@@ -108,12 +87,7 @@ val equal : string -> string -> bool
char ->
string *)
-val index_count:
- string ->
- int ->
- char ->
- int ->
- int
+val index_count : string -> int -> char -> int -> int
(* val index_next :
string ->
@@ -121,79 +95,68 @@ val index_count:
char ->
int *)
-
+val find : ?start:int -> sub:string -> string -> int
(**
[find ~start ~sub s]
returns [-1] if not found
*)
-val find : ?start:int -> sub:string -> string -> int
-val contain_substring : string -> string -> bool
+val contain_substring : string -> string -> bool
-val non_overlap_count : sub:string -> string -> int
+val non_overlap_count : sub:string -> string -> int
val rfind : sub:string -> string -> int
+val tail_from : string -> int -> string
(** [tail_from s 1]
return a substring from offset 1 (inclusive)
*)
-val tail_from : string -> int -> string
-
+val rindex_neg : string -> char -> int
(** returns negative number if not found *)
-val rindex_neg : string -> char -> int
val rindex_opt : string -> char -> int option
+val no_char : string -> char -> int -> int -> bool
-val no_char : string -> char -> int -> int -> bool
-
-
-val no_slash : string -> bool
+val no_slash : string -> bool
+val no_slash_idx : string -> int
(** return negative means no slash, otherwise [i] means the place for first slash *)
-val no_slash_idx : string -> int
-val no_slash_idx_from : string -> int -> int
+val no_slash_idx_from : string -> int -> int
+val replace_slash_backward : string -> string
(** if no conversion happens, reference equality holds *)
-val replace_slash_backward : string -> string
+val replace_backward_slash : string -> string
(** if no conversion happens, reference equality holds *)
-val replace_backward_slash : string -> string
-val empty : string
+val empty : string
-#ifdef BROWSER
-val compare : string -> string -> int
-#else
-external compare : string -> string -> int = "caml_string_length_based_compare" [@@noalloc];;
-#endif
+val compare : string -> string -> int
val single_space : string
-val concat3 : string -> string -> string -> string
-val concat4 : string -> string -> string -> string -> string
-val concat5 : string -> string -> string -> string -> string -> string
+val concat3 : string -> string -> string -> string
+val concat4 : string -> string -> string -> string -> string
+val concat5 : string -> string -> string -> string -> string -> string
val inter2 : string -> string -> string
-val inter3 : string -> string -> string -> string
+val inter3 : string -> string -> string -> string
val inter4 : string -> string -> string -> string -> string
-val concat_array : string -> string array -> string
+val concat_array : string -> string array -> string
-val single_colon : string
+val single_colon : string
val parent_dir_lit : string
val current_dir_lit : string
val capitalize_ascii : string -> string
-val capitalize_sub:
- string ->
- int ->
- string
+val capitalize_sub : string -> int -> string
val uncapitalize_ascii : string -> string
-val lowercase_ascii : string -> string
+val lowercase_ascii : string -> string
(** Play parity to {!Ext_buffer.add_int_1} *)
(* val get_int_1 : string -> int -> int
@@ -201,26 +164,12 @@ val lowercase_ascii : string -> string
val get_int_3 : string -> int -> int
val get_int_4 : string -> int -> int *)
-val get_1_2_3_4 :
- string ->
- off:int ->
- int ->
- int
-
-val unsafe_sub :
- string ->
- int ->
- int ->
- string
-
-val is_valid_hash_number:
- string ->
- bool
-
-val hash_number_as_i32_exn:
- string ->
- int32
-
-val first_marshal_char:
- string ->
- bool
+val get_1_2_3_4 : string -> off:int -> int -> int
+
+val unsafe_sub : string -> int -> int -> string
+
+val is_valid_hash_number : string -> bool
+
+val hash_number_as_i32_exn : string -> int32
+
+val first_marshal_char : string -> bool
diff --git a/compiler/ext/ext_sys.cppo.ml b/compiler/ext/ext_sys.ml
similarity index 85%
rename from compiler/ext/ext_sys.cppo.ml
rename to compiler/ext/ext_sys.ml
index 917d397550e..c5412249222 100644
--- a/compiler/ext/ext_sys.cppo.ml
+++ b/compiler/ext/ext_sys.ml
@@ -22,15 +22,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
-(** TODO: not exported yet, wait for Windows Fix*)
-#ifdef BROWSER
-let is_directory_no_exn f =
- try Sys.is_directory f with _ -> false
-#else
-external is_directory_no_exn : string -> bool = "caml_sys_is_directory_no_exn"
-#endif
-
+(* Sys.is_directory is available on every supported compiler target. *)
+let is_directory_no_exn file = try Sys.is_directory file with _ -> false
let is_windows_or_cygwin = Sys.win32 || Sys.cygwin
-
-
diff --git a/compiler/ext/hash.cppo.ml b/compiler/ext/hash.cppo.ml
index 6194e414ea8..905d1c97ea2 100644
--- a/compiler/ext/hash.cppo.ml
+++ b/compiler/ext/hash.cppo.ml
@@ -2,20 +2,19 @@
type key = Ident.t
type 'a t = (key, 'a) Hash_gen.t
let key_index (h : _ t ) (key : key) =
- (Bs_hash_stubs.hash_stamp_and_name key.stamp key.name ) land (Array.length h.data - 1)
-(* (Bs_hash_stubs.hash_string_int key.name key.stamp ) land (Array.length h.data - 1) *)
+ (Ext_platform_primitives.hash_stamp_and_name key.stamp key.name ) land (Array.length h.data - 1)
let eq_key = Ext_ident.equal
#elif defined TYPE_STRING
type key = string
type 'a t = (key, 'a) Hash_gen.t
let key_index (h : _ t ) (key : key) =
- (Bs_hash_stubs.hash_string key ) land (Array.length h.data - 1)
+ (Ext_platform_primitives.hash_string key ) land (Array.length h.data - 1)
let eq_key = Ext_string.equal
#elif defined TYPE_INT
type key = int
type 'a t = (key, 'a) Hash_gen.t
let key_index (h : _ t ) (key : key) =
- (Bs_hash_stubs.hash_int key ) land (Array.length h.data - 1)
+ (Ext_platform_primitives.hash_int key ) land (Array.length h.data - 1)
let eq_key = Ext_int.equal
#elif defined TYPE_FUNCTOR
diff --git a/compiler/ext/hash_set.cppo.ml b/compiler/ext/hash_set.cppo.ml
index 73779079ad2..93b51a340c4 100644
--- a/compiler/ext/hash_set.cppo.ml
+++ b/compiler/ext/hash_set.cppo.ml
@@ -25,19 +25,19 @@
#ifdef TYPE_INT
type key = int
let key_index (h : _ Hash_set_gen.t ) (key : key) =
- (Bs_hash_stubs.hash_int key) land (Array.length h.data - 1)
+ (Ext_platform_primitives.hash_int key) land (Array.length h.data - 1)
let eq_key = Ext_int.equal
type t = key Hash_set_gen.t
#elif defined TYPE_STRING
type key = string
let key_index (h : _ Hash_set_gen.t ) (key : key) =
- (Bs_hash_stubs.hash_string key) land (Array.length h.data - 1)
+ (Ext_platform_primitives.hash_string key) land (Array.length h.data - 1)
let eq_key = Ext_string.equal
type t = key Hash_set_gen.t
#elif defined TYPE_IDENT
type key = Ident.t
let key_index (h : _ Hash_set_gen.t ) (key : key) =
- (Bs_hash_stubs.hash_string_int key.name key.stamp) land (Array.length h.data - 1)
+ (Ext_platform_primitives.hash_string_int key.name key.stamp) land (Array.length h.data - 1)
let eq_key = Ext_ident.equal
type t = key Hash_set_gen.t
#elif defined TYPE_FUNCTOR
@@ -121,4 +121,3 @@ module Make (H: Hashtbl.HashedType) : (Hash_set_gen.S with type key = H.t) = str
#ifdef TYPE_FUNCTOR
end
#endif
-
diff --git a/compiler/ext/hash_set_ident_mask.ml b/compiler/ext/hash_set_ident_mask.ml
index 67a78d836d8..2d7722f1305 100644
--- a/compiler/ext/hash_set_ident_mask.ml
+++ b/compiler/ext/hash_set_ident_mask.ml
@@ -35,7 +35,8 @@ type t = {
}
let key_index_by_ident (h : t) (key : Ident.t) =
- Bs_hash_stubs.hash_string_int key.name key.stamp land (Array.length h.data - 1)
+ Ext_platform_primitives.hash_string_int key.name key.stamp
+ land (Array.length h.data - 1)
let create initial_size =
let s = Ext_util.power_2_above 8 initial_size in
diff --git a/compiler/ext/js_reserved_map.cppo.ml b/compiler/ext/js_reserved_map.ml
similarity index 52%
rename from compiler/ext/js_reserved_map.cppo.ml
rename to compiler/ext/js_reserved_map.ml
index 6daaff97601..9950bf89ca1 100644
--- a/compiler/ext/js_reserved_map.cppo.ml
+++ b/compiler/ext/js_reserved_map.ml
@@ -23,16 +23,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
module STbl = struct
- #if OCAML_VERSION >= (5, 0, 0)
- include Hashtbl.Make (String)
- #else
- module StringHash : Hashtbl.HashedType with type t = string = struct
- type t = string
- let equal = String.equal
- let hash = Hashtbl.hash (* polymorphic hash function *)
- end
- include Hashtbl.Make (StringHash)
- #endif
+ include Hashtbl.Make (String)
let of_array arr =
let tbl = create (Array.length arr) in
@@ -44,61 +35,59 @@ end
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#reserved_words
*)
-let js_keywords = STbl.of_array [|
- "break";
- "case";
- "catch";
- "class";
- "const";
- "continue";
- "debugger";
- "default";
- "delete";
- "do";
- "else";
- "export";
- "extends";
- "false";
- "finally";
- "for";
- "function";
- "if";
- "import";
- "in";
- "instanceof";
- "new";
- "null";
- "return";
- "super";
- "switch";
- "this";
- "throw";
- "true";
- "try";
- "typeof";
- "var";
- "void";
- "while";
- "with";
-
- (* The following are also reserved in strict context, including ESM *)
- "let";
- "static";
- "yield";
-
- (* `await` is reserved in async context, including ESM *)
- "await";
-
- (* Future reserved words *)
- "enum";
- "implements";
- "interface";
- "package";
- "private";
- "protected";
- "public";
-
- (* Special identifiers
+let js_keywords =
+ STbl.of_array
+ [|
+ "break";
+ "case";
+ "catch";
+ "class";
+ "const";
+ "continue";
+ "debugger";
+ "default";
+ "delete";
+ "do";
+ "else";
+ "export";
+ "extends";
+ "false";
+ "finally";
+ "for";
+ "function";
+ "if";
+ "import";
+ "in";
+ "instanceof";
+ "new";
+ "null";
+ "return";
+ "super";
+ "switch";
+ "this";
+ "throw";
+ "true";
+ "try";
+ "typeof";
+ "var";
+ "void";
+ "while";
+ "with";
+ (* The following are also reserved in strict context, including ESM *)
+ "let";
+ "static";
+ "yield";
+ (* `await` is reserved in async context, including ESM *)
+ "await";
+ (* Future reserved words *)
+ "enum";
+ "implements";
+ "interface";
+ "package";
+ "private";
+ "protected";
+ "public";
+ (* Special identifiers
`arguments` and `eval` is not real *keywords*
@@ -107,9 +96,9 @@ let js_keywords = STbl.of_array [|
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#identifiers_with_special_meanings
*)
- "arguments";
- "eval";
-|]
+ "arguments";
+ "eval";
+ |]
let is_js_keyword s = STbl.mem js_keywords s
@@ -121,93 +110,87 @@ let is_js_keyword s = STbl.mem js_keywords s
However, these names are actually used with no problems today. (Except `arguments` and `eval`)
*)
-let js_special_words = STbl.of_array [|
- "arguments";
- "as";
- "async";
- "eval";
- "from";
- "get";
- "of";
- "set";
-|]
+let js_special_words =
+ STbl.of_array
+ [|"arguments"; "as"; "async"; "eval"; "from"; "get"; "of"; "set"|]
let is_js_special_word s = STbl.mem js_special_words s
(** Identifier names _might_ need to care about *)
-let js_globals = STbl.of_array [|
- (* JavaScript standards built-ins
+let js_globals =
+ STbl.of_array
+ [|
+ (* JavaScript standards built-ins
See https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects
*)
- "AggregateError";
- "Array";
- "ArrayBuffer";
- "AsyncFunction";
- "AsyncGenerator";
- "AsyncGeneratorFunction";
- "AsyncIterator";
- "Atomics";
- "BigInt";
- "BigInt64Array";
- "BigUint64Array";
- "Boolean";
- "DataView";
- "Date";
- "decodeURI";
- "decodeURIComponent";
- "encodeURI";
- "encodeURIComponent";
- "Error";
- "eval";
- "EvalError";
- "FinalizationRegistry";
- "Float16Array";
- "Float32Array";
- "Float64Array";
- "Function";
- "Generator";
- "GeneratorFunction";
- "globalThis";
- "Infinity";
- "Int16Array";
- "Int32Array";
- "Int8Array";
- "Intl";
- "isFinite";
- "isNaN";
- "Iterator";
- "JSON";
- "Map";
- "Math";
- "NaN";
- "Number";
- "Object";
- "parseFloat";
- "parseInt";
- "Promise";
- "Proxy";
- "RangeError";
- "ReferenceError";
- "Reflect";
- "RegExp";
- "Set";
- "SharedArrayBuffer";
- "String";
- "Symbol";
- "SyntaxError";
- "TypedArray";
- "TypeError";
- "Uint16Array";
- "Uint32Array";
- "Uint8Array";
- "Uint8ClampedArray";
- "undefined";
- "URIError";
- "WeakMap";
- "WeakRef";
- "WeakSet";
-
- (* A few of the HTML standard globals
+ "AggregateError";
+ "Array";
+ "ArrayBuffer";
+ "AsyncFunction";
+ "AsyncGenerator";
+ "AsyncGeneratorFunction";
+ "AsyncIterator";
+ "Atomics";
+ "BigInt";
+ "BigInt64Array";
+ "BigUint64Array";
+ "Boolean";
+ "DataView";
+ "Date";
+ "decodeURI";
+ "decodeURIComponent";
+ "encodeURI";
+ "encodeURIComponent";
+ "Error";
+ "eval";
+ "EvalError";
+ "FinalizationRegistry";
+ "Float16Array";
+ "Float32Array";
+ "Float64Array";
+ "Function";
+ "Generator";
+ "GeneratorFunction";
+ "globalThis";
+ "Infinity";
+ "Int16Array";
+ "Int32Array";
+ "Int8Array";
+ "Intl";
+ "isFinite";
+ "isNaN";
+ "Iterator";
+ "JSON";
+ "Map";
+ "Math";
+ "NaN";
+ "Number";
+ "Object";
+ "parseFloat";
+ "parseInt";
+ "Promise";
+ "Proxy";
+ "RangeError";
+ "ReferenceError";
+ "Reflect";
+ "RegExp";
+ "Set";
+ "SharedArrayBuffer";
+ "String";
+ "Symbol";
+ "SyntaxError";
+ "TypedArray";
+ "TypeError";
+ "Uint16Array";
+ "Uint32Array";
+ "Uint8Array";
+ "Uint8ClampedArray";
+ "undefined";
+ "URIError";
+ "WeakMap";
+ "WeakRef";
+ "WeakSet";
+ (* A few of the HTML standard globals
See https://developer.mozilla.org/en-US/docs/Web/API/Window
See https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope
@@ -222,22 +205,20 @@ let js_globals = STbl.of_array [|
"origin";
*)
- (* A few of the Node.js globals
+ (* A few of the Node.js globals
Specifically related to the CommonJS module system
They cannot be redeclared in nested scope.
*)
- "__dirname";
- "__filename";
- "require";
- "module";
- "exports";
-
- (* Bun's global namespace *)
- "Bun";
-
- (* Deno's global namespace *)
- "Deno";
-|]
+ "__dirname";
+ "__filename";
+ "require";
+ "module";
+ "exports";
+ (* Bun's global namespace *)
+ "Bun";
+ (* Deno's global namespace *)
+ "Deno";
+ |]
let is_js_global s = STbl.mem js_globals s
diff --git a/compiler/ext/ordered_hash_map.cppo.ml b/compiler/ext/ordered_hash_map.cppo.ml
index b44ef6679e9..24ec39b2797 100644
--- a/compiler/ext/ordered_hash_map.cppo.ml
+++ b/compiler/ext/ordered_hash_map.cppo.ml
@@ -10,7 +10,7 @@ struct
type key = Ident.t
type 'value t = (key,'value) Ordered_hash_map_gen.t
let key_index (h : _ t) (key : key) =
- (Bs_hash_stubs.hash_int key.stamp) land (Array.length h.data - 1)
+ (Ext_platform_primitives.hash_int key.stamp) land (Array.length h.data - 1)
let equal_key = Ext_ident.equal
#else
@@ -108,4 +108,3 @@ end
-
diff --git a/compiler/ext/platform/native/ext_platform_primitives.ml b/compiler/ext/platform/native/ext_platform_primitives.ml
new file mode 100644
index 00000000000..96d4c23bb8b
--- /dev/null
+++ b/compiler/ext/platform/native/ext_platform_primitives.ml
@@ -0,0 +1,19 @@
+(* Native optimized compiler primitives. *)
+external hash_string : string -> int = "caml_bs_hash_string" [@@noalloc]
+
+external hash_string_int : string -> int -> int = "caml_bs_hash_string_and_int"
+[@@noalloc]
+
+external hash_stamp_and_name : int -> string -> int
+ = "caml_bs_hash_stamp_and_name"
+[@@noalloc]
+
+external hash_int : int -> int = "caml_bs_hash_int" [@@noalloc]
+
+external string_length_based_compare : string -> string -> int
+ = "caml_string_length_based_compare"
+[@@noalloc]
+
+external int_unsafe_blit : int array -> int -> int array -> int -> int -> unit
+ = "caml_int_array_blit"
+[@@noalloc]
diff --git a/compiler/ext/platform/playground/ext_platform_primitives.ml b/compiler/ext/platform/playground/ext_platform_primitives.ml
new file mode 100644
index 00000000000..4eaea3dd3b4
--- /dev/null
+++ b/compiler/ext/platform/playground/ext_platform_primitives.ml
@@ -0,0 +1,13 @@
+(* Portable compiler primitives used by the playground. *)
+let hash_string : string -> int = Hashtbl.hash
+let hash_string_int string int = Hashtbl.hash (string, int)
+let hash_stamp_and_name int string = Hashtbl.hash (int, string)
+let hash_int : int -> int = Hashtbl.hash
+
+let string_length_based_compare x y =
+ let x_length = String.length x in
+ let y_length = String.length y in
+ if x_length = y_length then String.compare x y
+ else Int.compare x_length y_length
+
+let int_unsafe_blit = Array.blit
diff --git a/compiler/ext/vec.cppo.ml b/compiler/ext/vec.cppo.ml
index 6e8ba607f02..14027008aa0 100644
--- a/compiler/ext/vec.cppo.ml
+++ b/compiler/ext/vec.cppo.ml
@@ -37,7 +37,7 @@ module Make ( Resize : Vec_gen.ResizeType) = struct
type elt = int
let null = 0 (* can be optimized *)
-let unsafe_blit = Bs_hash_stubs.int_unsafe_blit
+let unsafe_blit = Ext_platform_primitives.int_unsafe_blit
#else
[%error "unknown type"]
#endif
diff --git a/compiler/ml/cmt_format.ml b/compiler/ml/cmt_format.ml
new file mode 100644
index 00000000000..d96c006ac00
--- /dev/null
+++ b/compiler/ml/cmt_format.ml
@@ -0,0 +1,6 @@
+include Cmt_format_common
+
+let save_cmt filename modname binary_annots sourcefile initial_env cmi =
+ Cmt_format_persistence.save_cmt filename modname binary_annots sourcefile
+ initial_env cmi;
+ clear ()
diff --git a/compiler/ml/cmt_format.mli b/compiler/ml/cmt_format.mli
index 66589f088de..9d37721f1b8 100644
--- a/compiler/ml/cmt_format.mli
+++ b/compiler/ml/cmt_format.mli
@@ -58,7 +58,7 @@ type cmt_infos = {
cmt_sourcefile: string option;
cmt_builddir: string;
cmt_loadpath: string list;
- cmt_source_digest: string option;
+ cmt_source_digest: Digest.t option;
cmt_initial_env: Env.t;
cmt_imports: (string * Digest.t option) list;
cmt_interface_digest: Digest.t option;
diff --git a/compiler/ml/cmt_format.cppo.ml b/compiler/ml/cmt_format_common.ml
similarity index 52%
rename from compiler/ml/cmt_format.cppo.ml
rename to compiler/ml/cmt_format_common.ml
index ff30fc00435..d3a151f9444 100644
--- a/compiler/ml/cmt_format.cppo.ml
+++ b/compiler/ml/cmt_format_common.ml
@@ -13,24 +13,12 @@
(* *)
(**************************************************************************)
-#ifdef BROWSER
-[@@@warning "-32"]
-#endif
+(* Shared CMT reading and collection logic. Persistence is supplied by the
+ selected Cmt_format implementation so the playground does not retain the
+ native writer and its transitive dependencies. *)
open Typedtree
-(* Note that in Typerex, there is an awful hack to save a cmt file
- together with the interface file that was generated by ocaml (this
- is because the installed version of ocaml might differ from the one
- integrated in Typerex).
-*)
-
-
-
-let read_magic_number ic =
- let len_magic_number = String.length Config.cmt_magic_number in
- really_input_string ic len_magic_number
-
type binary_annots =
| Packed of Types.signature * string list
| Implementation of structure
@@ -39,38 +27,49 @@ type binary_annots =
| Partial_interface of binary_part array
and binary_part =
-| Partial_structure of structure
-| Partial_structure_item of structure_item
-| Partial_expression of expression
-| Partial_pattern of pattern
-| Partial_class_expr of unit
-| Partial_signature of signature
-| Partial_signature_item of signature_item
-| Partial_module_type of module_type
+ | Partial_structure of structure
+ | Partial_structure_item of structure_item
+ | Partial_expression of expression
+ | Partial_pattern of pattern
+ | Partial_class_expr of unit
+ | Partial_signature of signature
+ | Partial_signature_item of signature_item
+ | Partial_module_type of module_type
type cmt_infos = {
- cmt_modname : string;
- cmt_annots : binary_annots;
- cmt_value_dependencies :
+ cmt_modname: string;
+ cmt_annots: binary_annots;
+ cmt_value_dependencies:
(Types.value_description * Types.value_description) list;
- cmt_comments : (string * Location.t) list;
- cmt_args : string array;
- cmt_sourcefile : string option;
- cmt_builddir : string;
- cmt_loadpath : string list;
- cmt_source_digest : Digest.t option;
- cmt_initial_env : Env.t;
- cmt_imports : (string * Digest.t option) list;
- cmt_interface_digest : Digest.t option;
- cmt_use_summaries : bool;
+ cmt_comments: (string * Location.t) list;
+ cmt_args: string array;
+ cmt_sourcefile: string option;
+ cmt_builddir: string;
+ cmt_loadpath: string list;
+ cmt_source_digest: Digest.t option;
+ cmt_initial_env: Env.t;
+ cmt_imports: (string * Digest.t option) list;
+ cmt_interface_digest: Digest.t option;
+ cmt_use_summaries: bool;
cmt_extra_info: Cmt_utils.cmt_extra_info;
}
-type error =
- Not_a_typedtree of string
+(* Note that in Typerex, there is an awful hack to save a cmt file
+ together with the interface file that was generated by ocaml (this
+ is because the installed version of ocaml might differ from the one
+ integrated in Typerex).
+*)
+
+let read_magic_number ic =
+ let len_magic_number = String.length Config.cmt_magic_number in
+ really_input_string ic len_magic_number
+
+type error = Not_a_typedtree of string
let need_to_clear_env =
- try ignore (Sys.getenv "OCAML_BINANNOT_WITHENV"); false
+ try
+ ignore (Sys.getenv "OCAML_BINANNOT_WITHENV");
+ false
with Not_found -> true
let keep_only_summary = Env.keep_only_summary
@@ -78,18 +77,18 @@ let keep_only_summary = Env.keep_only_summary
open Tast_mapper
let cenv =
- {Tast_mapper.default with env = fun _sub env -> keep_only_summary env}
+ {Tast_mapper.default with env = (fun _sub env -> keep_only_summary env)}
let clear_part = function
| Partial_structure s -> Partial_structure (cenv.structure cenv s)
| Partial_structure_item s ->
- Partial_structure_item (cenv.structure_item cenv s)
+ Partial_structure_item (cenv.structure_item cenv s)
| Partial_expression e -> Partial_expression (cenv.expr cenv e)
| Partial_pattern p -> Partial_pattern (cenv.pat cenv p)
| Partial_class_expr () -> assert false
| Partial_signature s -> Partial_signature (cenv.signature cenv s)
| Partial_signature_item s ->
- Partial_signature_item (cenv.signature_item cenv s)
+ Partial_signature_item (cenv.signature_item cenv s)
| Partial_module_type s -> Partial_module_type (cenv.module_type cenv s)
let clear_env binary_annots =
@@ -99,63 +98,61 @@ let clear_env binary_annots =
| Interface s -> Interface (cenv.signature cenv s)
| Packed _ -> binary_annots
| Partial_implementation array ->
- Partial_implementation (Array.map clear_part array)
- | Partial_interface array ->
- Partial_interface (Array.map clear_part array)
-
+ Partial_implementation (Array.map clear_part array)
+ | Partial_interface array -> Partial_interface (Array.map clear_part array)
else binary_annots
exception Error of error
let input_cmt ic = (input_value ic : cmt_infos)
-let output_cmt oc cmt =
- output_string oc Config.cmt_magic_number;
- output_value oc (cmt : cmt_infos)
-
let read filename =
-(* Printf.fprintf stderr "Cmt_format.read %s\n%!" filename; *)
+ (* Printf.fprintf stderr "Cmt_format.read %s\n%!" filename; *)
let ic = open_in_bin filename in
try
let magic_number = read_magic_number ic in
let cmi, cmt =
- if magic_number = Config.cmt_magic_number then
- None, Some (input_cmt ic)
+ if magic_number = Config.cmt_magic_number then (None, Some (input_cmt ic))
else if magic_number = Config.cmi_magic_number then
let cmi = Cmi_format.input_cmi ic in
- let cmt = try
- let magic_number = read_magic_number ic in
- if magic_number = Config.cmt_magic_number then
- let cmt = input_cmt ic in
- Some cmt
- else None
+ let cmt =
+ try
+ let magic_number = read_magic_number ic in
+ if magic_number = Config.cmt_magic_number then
+ let cmt = input_cmt ic in
+ Some cmt
+ else None
with _ -> None
in
- Some cmi, cmt
- else
- raise(Cmi_format.Error(Cmi_format.Not_an_interface filename))
+ (Some cmi, cmt)
+ else raise (Cmi_format.Error (Cmi_format.Not_an_interface filename))
in
close_in ic;
-(* Printf.fprintf stderr "Cmt_format.read done\n%!"; *)
- cmi, cmt
+ (* Printf.fprintf stderr "Cmt_format.read done\n%!"; *)
+ (cmi, cmt)
with e ->
close_in ic;
raise e
let read_cmt filename =
match read filename with
- _, None -> raise (Error (Not_a_typedtree filename))
- | _, Some cmt -> cmt
+ | _, None -> raise (Error (Not_a_typedtree filename))
+ | _, Some cmt -> cmt
let read_cmi filename =
match read filename with
- None, _ ->
- raise (Cmi_format.Error (Cmi_format.Not_an_interface filename))
- | Some cmi, _ -> cmi
+ | None, _ -> raise (Cmi_format.Error (Cmi_format.Not_an_interface filename))
+ | Some cmi, _ -> cmi
+
+let saved_types : binary_part list ref = ref []
-let saved_types = ref []
-let value_deps = ref []
-let deprecated_used = ref []
+let value_deps : (Types.value_description * Types.value_description) list ref =
+ ref []
+
+let deprecated_used : Cmt_utils.deprecated_used list ref = ref []
+
+let value_dependencies () = !value_deps
+let deprecated_uses () = !deprecated_used
let clear () =
saved_types := [];
@@ -166,7 +163,8 @@ let add_saved_type b = saved_types := b :: !saved_types
let get_saved_types () = !saved_types
let set_saved_types l = saved_types := l
-let record_deprecated_used ?deprecated_context ?migration_template ?migration_in_pipe_chain_template source_loc deprecated_text =
+let record_deprecated_used ?deprecated_context ?migration_template
+ ?migration_in_pipe_chain_template source_loc deprecated_text =
deprecated_used :=
{
Cmt_utils.source_loc;
@@ -182,40 +180,3 @@ let _ = Cmt_utils.record_deprecated_used := record_deprecated_used
let record_value_dependency vd1 vd2 =
if vd1.Types.val_loc <> vd2.Types.val_loc then
value_deps := (vd1, vd2) :: !value_deps
-
-#ifdef BROWSER
-let save_cmt _filename _modname _binary_annots _sourcefile _initial_env _cmi = ()
-#else
-open Cmi_format
-
-let save_cmt filename modname binary_annots sourcefile initial_env cmi =
- if !Clflags.binary_annotations then begin
- Misc.output_to_bin_file_directly filename
- (fun temp_file_name oc ->
- let this_crc =
- match cmi with
- | None -> None
- | Some cmi -> Some (output_cmi temp_file_name oc cmi)
- in
- let source_digest = Misc.may_map Digest.file sourcefile in
- let cmt = {
- cmt_modname = modname;
- cmt_annots = clear_env binary_annots;
- cmt_value_dependencies = !value_deps;
- cmt_comments = [];
- cmt_args = Sys.argv;
- cmt_sourcefile = sourcefile;
- cmt_builddir = Sys.getcwd ();
- cmt_loadpath = !Config.load_path;
- cmt_source_digest = source_digest;
- cmt_initial_env = if need_to_clear_env then
- keep_only_summary initial_env else initial_env;
- cmt_imports = List.sort compare (Env.imports ());
- cmt_interface_digest = this_crc;
- cmt_use_summaries = need_to_clear_env;
- cmt_extra_info = {deprecated_used = !deprecated_used};
- } in
- output_cmt oc cmt)
- end;
- clear ()
-#endif
diff --git a/compiler/ml/dune b/compiler/ml/dune
index a5a53b70c3f..7a286e14044 100644
--- a/compiler/ml/dune
+++ b/compiler/ml/dune
@@ -1,14 +1,30 @@
+(env
+ (_
+ (flags
+ (:standard -w +a-4-42-40-41-44-45-9-48-67-70))))
+
+; The browser profile builds the playground compiler; this rule pair generates a module from platform/{native,playground}.
+
+(rule
+ (target cmt_format_persistence.ml)
+ (enabled_if
+ (= %{profile} browser))
+ (action
+ (copy
+ platform/playground/cmt_format_persistence.ml
+ cmt_format_persistence.ml)))
+
+(rule
+ (target cmt_format_persistence.ml)
+ (enabled_if
+ (<> %{profile} browser))
+ (action
+ (copy platform/native/cmt_format_persistence.ml cmt_format_persistence.ml)))
+
(library
(name ml)
(wrapped false)
+ (private_modules cmt_format_common cmt_format_persistence)
(instrumentation
(backend bisect_ppx))
- (flags
- (:standard -w +a-4-42-40-41-44-45-9-48-67-70))
(libraries ext flow_parser))
-
-(rule
- (target cmt_format.ml)
- (deps cmt_format.cppo.ml)
- (action
- (run %{bin:cppo} %{env:CPPO_FLAGS=} %{deps} -o %{target})))
diff --git a/compiler/ml/platform/native/cmt_format_persistence.ml b/compiler/ml/platform/native/cmt_format_persistence.ml
new file mode 100644
index 00000000000..b98fef30e8c
--- /dev/null
+++ b/compiler/ml/platform/native/cmt_format_persistence.ml
@@ -0,0 +1,52 @@
+(**************************************************************************)
+(* *)
+(* OCaml *)
+(* *)
+(* Fabrice Le Fessant, INRIA Saclay *)
+(* *)
+(* Copyright 2012 Institut National de Recherche en Informatique et *)
+(* en Automatique. *)
+(* *)
+(* All rights reserved. This file is distributed under the terms of *)
+(* the GNU Lesser General Public License version 2.1, with the *)
+(* special exception on linking described in the file LICENSE. *)
+(* *)
+(**************************************************************************)
+
+open Cmt_format_common
+
+let output_cmt output_channel cmt =
+ output_string output_channel Config.cmt_magic_number;
+ output_value output_channel (cmt : cmt_infos)
+
+let save_cmt filename modname binary_annots sourcefile initial_env cmi =
+ if !Clflags.binary_annotations then
+ Misc.output_to_bin_file_directly filename
+ (fun temp_file_name output_channel ->
+ let interface_digest =
+ match cmi with
+ | None -> None
+ | Some cmi ->
+ Some (Cmi_format.output_cmi temp_file_name output_channel cmi)
+ in
+ let cmt =
+ {
+ cmt_modname = modname;
+ cmt_annots = clear_env binary_annots;
+ cmt_value_dependencies = value_dependencies ();
+ cmt_comments = [];
+ cmt_args = Sys.argv;
+ cmt_sourcefile = sourcefile;
+ cmt_builddir = Sys.getcwd ();
+ cmt_loadpath = !Config.load_path;
+ cmt_source_digest = Misc.may_map Digest.file sourcefile;
+ cmt_initial_env =
+ (if need_to_clear_env then keep_only_summary initial_env
+ else initial_env);
+ cmt_imports = List.sort compare (Env.imports ());
+ cmt_interface_digest = interface_digest;
+ cmt_use_summaries = need_to_clear_env;
+ cmt_extra_info = {deprecated_used = deprecated_uses ()};
+ }
+ in
+ output_cmt output_channel cmt)
diff --git a/compiler/ml/platform/playground/cmt_format_persistence.ml b/compiler/ml/platform/playground/cmt_format_persistence.ml
new file mode 100644
index 00000000000..2fc0fe177c4
--- /dev/null
+++ b/compiler/ml/platform/playground/cmt_format_persistence.ml
@@ -0,0 +1,2 @@
+let save_cmt _filename _modname _binary_annots _sourcefile _initial_env _cmi =
+ ()
diff --git a/tests/ERROR_VARIANTS.md b/tests/ERROR_VARIANTS.md
index afeba3034e4..36595660b69 100644
--- a/tests/ERROR_VARIANTS.md
+++ b/tests/ERROR_VARIANTS.md
@@ -473,7 +473,7 @@ Build / dependency errors. Mostly need the `rescript build` runtime to fire —
| `Bs_main_not_exist` | ☐ (needs build harness) | — | `rescript.json` `main` entry missing. |
| `Bs_invalid_path` | ☐ (needs build harness) | — | `-I` / source path with invalid form. |
| `Missing_ml_dependency` | ☐ (needs build harness) | — | Compile-time missing dependency from a `.cmj` lookup table. |
-| `Dependency_script_module_dependent_not` | ☐ (needs build harness) | — | `js_name_of_module_id.cppo.ml:122`. **Reachable** when a dependent module is in script mode (`Package_script`) but the current module is in package mode (`Package_found _`). Legacy script-vs-package interaction; needs `rescript.json` harness. |
+| `Dependency_script_module_dependent_not` | ☐ (needs build harness) | — | `core/platform/native/js_name_of_module_id.ml:99`. **Reachable** when a dependent module is in script mode (`Package_script`) but the current module is in package mode (`Package_found _`). Legacy script-vs-package interaction; needs `rescript.json` harness. |
---
@@ -529,7 +529,7 @@ multi-file harnesses, which never set `-ppx`.
| `compiler/ml/transl_recmodule.ml` | `Circular_dependency` | ✓ | `recmodule_circular_dependency.res` | |
| `compiler/ml/rec_check.ml` | `Illegal_letrec_expr` | ✓ | `illegal_letrec_expr.res` | |
| `compiler/ml/syntaxerr.ml` | `Variable_in_scope` | ? (live, broken printer) | — | Reachable via `let f: type t. (t, 't) => t = …` (locally-abstract `t` collides with type variable `'t` during `varify_constructors`), but `Syntaxerr.error` has no registered pretty-printer, so it propagates as an uncaught `Fatal error: exception Syntaxerr.Error(_)`. Not removed because the variant is live; the fix should wire up a printer or convert the check into a regular typed diagnostic. |
-| `compiler/ml/cmt_format.cppo.ml` | `Not_a_typedtree` | ☐ (needs binary harness) | — | cmt_format.cppo.ml:147. Fires when a tool reads a `.cmt` file whose first block isn't a typed tree. Reachable in principle by pointing the analyzer at an arbitrary file with a `.cmt` extension; out of scope for the source-only fixture harnesses. |
+| `compiler/ml/cmt_format_common.ml` | `Not_a_typedtree` | ☐ (needs binary harness) | — | cmt_format_common.ml:139. Fires when a tool reads a `.cmt` file whose first block isn't a typed tree. Reachable in principle by pointing the analyzer at an arbitrary file with a `.cmt` extension; out of scope for the source-only fixture harnesses. |
| `compiler/ext/bsc_args.ml` | `Unknown` | ☐ (needs CLI harness) | — | bsc_args.ml:45. Reachable trivially via `bsc --bogus`, but the `super_errors{,_multi}` runners only pass `bsc` a fixed flag list plus the source file — they can't exercise CLI-level errors. |
| `compiler/ext/bsc_args.ml` | `Missing` | ☐ (needs CLI harness) | — | Same as above: `bsc -o` (no following filename). Needs a harness that invokes `bsc` with crafted argv. |
diff --git a/tests/ounit_tests/ounit_hash_stubs_test.ml b/tests/ounit_tests/ounit_hash_stubs_test.ml
index 6f686653c3e..0c033af7f22 100644
--- a/tests/ounit_tests/ounit_hash_stubs_test.ml
+++ b/tests/ounit_tests/ounit_hash_stubs_test.ml
@@ -27,39 +27,34 @@ let bench () =
done)
type id = {stamp: int; name: string; mutable flags: int} (* = Ident.t *)
-let hash id = Bs_hash_stubs.hash_stamp_and_name id.stamp id.name
+let hash id = Ext_platform_primitives.hash_stamp_and_name id.stamp id.name
let suites =
__FILE__
>::: [
- (__LOC__ >:: fun _ -> Bs_hash_stubs.hash_int 0 =~ Hashtbl.hash 0);
( __LOC__ >:: fun _ ->
- Bs_hash_stubs.hash_int max_int =~ Hashtbl.hash max_int );
+ Ext_platform_primitives.hash_int 0 =~ Hashtbl.hash 0 );
( __LOC__ >:: fun _ ->
- Bs_hash_stubs.hash_int max_int =~ Hashtbl.hash max_int );
+ Ext_platform_primitives.hash_int max_int =~ Hashtbl.hash max_int );
( __LOC__ >:: fun _ ->
- Bs_hash_stubs.hash_string
+ Ext_platform_primitives.hash_int max_int =~ Hashtbl.hash max_int );
+ ( __LOC__ >:: fun _ ->
+ Ext_platform_primitives.hash_string
"The quick brown fox jumps over the lazy dog"
=~ Hashtbl.hash "The quick brown fox jumps over the lazy dog" );
( __LOC__ >:: fun _ ->
Array.init 100 (fun i -> String.make i 'a')
|> Array.iter (fun x ->
- Bs_hash_stubs.hash_string x =~ Hashtbl.hash x) );
- ( __LOC__ >:: fun _ ->
- (* only stamp matters here *)
- hash {stamp = 1; name = "xx"; flags = 0}
- =~ Bs_hash_stubs.hash_small_int 1;
- hash {stamp = 11; name = "xx"; flags = 0}
- =~ Bs_hash_stubs.hash_small_int 11 );
+ Ext_platform_primitives.hash_string x =~ Hashtbl.hash x) );
( __LOC__ >:: fun _ ->
(* only string matters here *)
hash {stamp = 0; name = "Pervasives"; flags = 0}
- =~ Bs_hash_stubs.hash_string "Pervasives";
+ =~ Ext_platform_primitives.hash_string "Pervasives";
hash {stamp = 0; name = "UU"; flags = 0}
- =~ Bs_hash_stubs.hash_string "UU" );
+ =~ Ext_platform_primitives.hash_string "UU" );
( __LOC__ >:: fun _ ->
let v = Array.init 20 (fun i -> i) in
let u = Array.init 30 (fun i -> 0 - i) in
- Bs_hash_stubs.int_unsafe_blit v 0 u 10 20;
+ Ext_platform_primitives.int_unsafe_blit v 0 u 10 20;
OUnit.assert_equal u
(Array.init 30 (fun i -> if i < 10 then -i else i - 10)) );
]