Repository: LexiFi/gen_js_api Branch: master Commit: dcdd0b1dd852 Files: 114 Total size: 352.5 KB Directory structure: gitextract_fdgla5ae/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── workflow.yml ├── .gitignore ├── .ocp-indent ├── CHANGES.md ├── CLASSES.md ├── IMPLGEN.md ├── INSTALL_AND_USE.md ├── LICENSE ├── LOW_LEVEL_BINDING.md ├── Makefile ├── NAMING.md ├── NODE_RUNTIME_BINDINGS.md ├── PPX.md ├── README.md ├── TODO.md ├── TYPES.md ├── VALUES.md ├── dune ├── dune-project ├── examples/ │ ├── calc/ │ │ ├── calc.html │ │ ├── calc.ml │ │ └── dune │ ├── misc/ │ │ ├── dune │ │ ├── jquery.mli │ │ ├── js_date.mli │ │ ├── js_str.mli │ │ ├── test_jquery.html │ │ └── test_jquery.ml │ └── test/ │ ├── dune │ ├── main.html │ ├── main.ml │ └── test_bindings.mli ├── gen_js_api.opam ├── lib/ │ ├── dune │ ├── ojs.ml │ ├── ojs.mli │ ├── ojs_exn.ml │ ├── ojs_exn.mli │ ├── ojs_runtime.js │ └── ojs_runtime_stubs.c ├── node-test/ │ ├── bindings/ │ │ ├── arrays.mli │ │ ├── buffer.mli │ │ ├── console.mli │ │ ├── container.ml │ │ ├── container.mli │ │ ├── dune │ │ ├── errors.mli │ │ ├── expected/ │ │ │ ├── arrays.ml │ │ │ ├── buffer.ml │ │ │ ├── console.ml │ │ │ ├── errors.ml │ │ │ ├── fs.ml │ │ │ ├── global.ml │ │ │ ├── imports.ml │ │ │ ├── number.ml │ │ │ ├── path.ml │ │ │ ├── process.ml │ │ │ └── promise.ml │ │ ├── fs.mli │ │ ├── global.mli │ │ ├── imports.js │ │ ├── imports.mli │ │ ├── imports.wat │ │ ├── number.mli │ │ ├── path.mli │ │ ├── process.mli │ │ └── promise.mli │ ├── runtime_primitives/ │ │ ├── bindings.mli │ │ ├── dune │ │ ├── example.ml │ │ ├── imports.js │ │ └── imports.wat │ └── test1/ │ ├── dune │ ├── recursive.js │ ├── recursive.mli │ └── test.ml ├── ojs.opam ├── ojs.opam.template ├── ppx-driver/ │ ├── dune │ └── gen_js_api_ppx_driver.ml ├── ppx-lib/ │ ├── dune │ ├── gen_js_api_ppx.ml │ └── gen_js_api_ppx.mli ├── ppx-standalone/ │ ├── dune │ ├── gen_js_api.ml │ └── gen_js_api.mli └── ppx-test/ ├── binding.mli ├── binding_automatic.mli ├── binding_explicitly_automatic.mli ├── binding_manual.mli ├── dune ├── expected/ │ ├── binding.ml │ ├── binding_automatic.ml │ ├── extension.ml │ ├── first_class_modules.ml │ ├── issues.ml │ ├── issues_mli.ml │ ├── modules.ml │ ├── recursive_modules.ml │ ├── scoped.ml │ ├── types.ml │ └── union_and_enum.ml ├── extension.ml ├── first_class_modules.mli ├── issues.ml ├── issues_mli.mli ├── modules.mli ├── ppx/ │ ├── dune │ └── main.ml ├── recursive_modules.mli ├── scoped.mli ├── types.ml └── union_and_enum.mli ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: daily ================================================ FILE: .github/workflows/workflow.yml ================================================ name: Builds, tests & co on: - push - pull_request permissions: read-all jobs: build: strategy: fail-fast: false matrix: os: - ubuntu-latest - macos-latest - windows-latest ocaml-compiler: - 5 - 4 include: - os: ubuntu-latest ocaml-compiler: "4.13" runs-on: ${{ matrix.os }} steps: - name: Checkout tree uses: actions/checkout@v6 - name: Set-up Node.js uses: actions/setup-node@v4 with: node-version: latest - name: Set-up OCaml uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: ${{ matrix.ocaml-compiler }} - run: opam install . --deps-only --with-test - run: opam exec -- make - run: opam exec -- make test # lint-doc: # runs-on: ubuntu-latest # steps: # - name: Checkout tree # uses: actions/checkout@v6 # - name: Set-up OCaml # uses: ocaml/setup-ocaml@v3 # with: # ocaml-compiler: 5 # - uses: ocaml/setup-ocaml/lint-doc@v3 # lint-fmt: # runs-on: ubuntu-latest # steps: # - name: Checkout tree # uses: actions/checkout@v6 # - name: Set-up OCaml # uses: ocaml/setup-ocaml@v3 # with: # ocaml-compiler: 5 # - uses: ocaml/setup-ocaml/lint-fmt@v3 lint-opam: runs-on: ubuntu-latest steps: - name: Checkout tree uses: actions/checkout@v6 - name: Set-up OCaml uses: ocaml/setup-ocaml@v3 with: ocaml-compiler: 5 - uses: ocaml/setup-ocaml/lint-opam@v3 ================================================ FILE: .gitignore ================================================ gen_js_api.install ojs.install *.merlin _build _opam .vscode ================================================ FILE: .ocp-indent ================================================ match_clause=4 strict_with=auto ================================================ FILE: CHANGES.md ================================================ Changelog ========= Unreleased ---------- - Support for binding to js_of_ocaml runtime primitives via `@`-prefixed payloads on `[@@js.global]` and `[@@@js.scope "@..."]`, enabling generated bindings to target values supplied by the JavaScript runtime. - Test suite updates adapted for wasm_of_ocaml. Version 1.1.6 ------------- - GPR#181: Upgrade ppxlib dependency to 0.37.0 (=> support OCaml 5.4) Version 1.1.5 ------------- - GPR#180: Fix for OCaml 5.3.0 Version 1.1.4 ------------- - GPR#176: Remove references to joo_global_object (@hhugo) - GPR#175: Support for simple module construction (@sbriais) Version 1.1.3 ------------- - GPR#173: Compatibility with Wasm_of_ocaml (@vouillon) - GPR#171: Update build badge and remove unused travis config (@tmcgilchrist) Version 1.1.2 ------------- - GPR#170: Make Ojs.iter_properties compatible with jsoo/effects (@vouillon) Version 1.1.1 ------------- - GPR#167: Fix CI (@cannorin) - GPR#166: Support first class modules to treat type variables safely (@cannorin) Version 1.1.0 ------------- - GPR#164: Switch to js_of_ocaml.4.0 (@hhugo) - GPR#165: Allow n-ary constructors in [@js.union] (@cannorin) Version 1.0.9 ------------- - GPR#161: Fix broken link to VALUES.md (@joelburget) - GPR#154: Upgrade to ocaml/setup-ocaml@v2 (@smorimoto) - GPR#153: Support recursive modules (@cannorin) - GPR#152: [@@js.invoke] attribute to call the global object as a function (@cannorin) Version 1.0.8 ------------- - GPR#149: Stop using OMP directly (@mlasson) - GPR#145: Add support for "newable" functions to [@@js.apply] (@cannorin) - GPR#143: Disable eta reduction for of_js and to_js of type aliases (@cannorin) - GPR#144: Disable "Spurious js.\* attribute" error for @js.dummy (@cannorin, @mlasson) - GPR#146: Fix an edge-case bug of prepare_args Version 1.0.7 ------------- - GPR#140: Adds a deprecation warning the automatic heuristic is used (@mlasson) - GPR#139: Rename things for backward compatibility (@mlasson) - GPR#135: UTF-8 support for (Ojs.get/set/delete) adaptions (@mlasson) - GPR#132: Add support for indexers and "callable" objects (@cannorin) - GPR#130: Javascript -> JavaScript (@smorimoto) - GPR#129: Add GitHub Actions workflow (@smorimoto) - GPR#128: Bucklescript -> ReScript (also add genType ppx as a resource) (@ryyppy) - GPR#127: Support boolean "enum"s and boolean union discriminators (@cannorin) - GPR#125: js.custom attribute for type declaration to support custom mapping #125 (@cannorin) - GPR#123: Upgrade ppx to the ocaml 4.11 ast (@hhugo) - GPR#120: Split runtime library to own package (@rgrinberg) - GPR#118: Add ppx tests setup (@jchavarri, @mlasson) - GPR#115: Support for functors and module inclusion (@mlasson) - GPR#114: Dependency tweaks (@rgrinberg) - GPR#113: Add support for type variables (@jchavarri, @mlasson) - GPR#111: Better ppxlib integration (@hhugo) - GPR#110: Include payload in extension node (@nojb) Version 1.0.6 ------------- - GPR #101: Adds travis support + use ocaml-migrate-parsetree (@mlasson) - GPR #94: Typo: correct wrong 'apply_arr' to 'apply' (@facelesspanda) - GPR #89: Update the opam file (@hhugo) - GPR #87: Switch to dune (@hhugo) - GPR #88: Fix some warnings (@hhugo) - GRP #85: Adapt to 4.08 (@nojb) Version 1.0.5 ------------- - Adapt to OCaml 4.06 Version 1.0.4 ------------- - Adapt to OCaml 4.05. ================================================ FILE: CLASSES.md ================================================ Class wrapping in gen_js_api ============================ gen_js_api can bind JavaScript objects into OCaml abstract types with associated functions (to get/set property and to call methods). This form of binding is quite efficient, since the opaque OCaml values are just the underlying JavaScript objects, with no mapping or wrapping. In addition to that, gen_js_api provides ways to **wrap JavaScript objects into OCaml objects**. This adds some runtime overhead, but allows users to use standard OO syntax in OCaml and to rely on inheritance (to mimic similar hierarchy on the JS side). In addition to the runtime overhead, wrapping JS objects as OCaml objects also forces to define all methods at once. With opaque bindings, methods of a given JS "class" can be spread over multiple OCaml modules. This can be especially useful to mimic the behavior of JS library addins that extends the library's object prototype with more methods. Class wrapping -------------- An interface processed by js_of_ocaml can define an OCaml class used to wrap some JavaScript objects: ```` class my_class: Ojs.t -> object inherit Ojs.obj (* method declarations *) .... end ```` The class must inherit from `Ojs.obj` directly or indirectly. This class simply defines a `to_js` method (returning the underlying `Ojs.t` object). Such a class declaration produces in the implementation a class definition with the list of `inherit` clauses (passing the `Ojs.t` handle to each of them) and a definition for all listed methods. It also produces a standard pair of `*_to_js`/`*_of_js` functions (the `*_to_js` function calls the `to_js` method inherited from `Ojs.obj`, and `*_of_js` calls the constructor of the class). Method binding -------------- - Property getter: ```` method foo: t [@@js.get "foo"] ```` - Property setter: ```` method set_foo: t -> unit [@@js.set "foo"] ```` - Method call: ```` method f: t -> unit [@@js.call "f"] ```` As always, the names can be omitted if they correspond to the implicit naming scheme. Prior to version 1.0.7, as for value bindings, some implicit rules applied, so that `[@@js.*]` attributes could often be omitted (in particular, in all the examples above). The following rules were applied in order: - If the method is a function with one argument `t -> unit` and its name starts with `set_`, then the declaration is assumed to be a `[@@js.set]` property setter (on the property whose name is obtained by dropping the `set_` prefix). - If the method is a function, then the definition is assumed to be a `[@@js.call]` method call. - Otherwise, the method is assumed to be a `[@@js.get]` property getter. But since version 1.0.7, *this feature has been deprecated*; all method should be explicitly annotated or a preprocessor warning will be emitted. Constructors ------------ The default constructor for a class wrapper is necessarily an `Ojs.t` object (see above). (Note: it would be easy to allow such classes to take a value of an arbitrary JS-able type, but this would make it more difficult to support inheritance.) It is possible to bind to actual JS constructors declarations such as: ```` class foo: string -> my_class ```` Calling this constructor is then implemented by calling the JavaScript constructor of the same name, and wrapping the resulting object with the `my_class` wrapper. This is similar to defining: ```` val foo: string -> my_class [@@js.new] ```` but allows writing `new foo(...)` instead of `foo(...)`. A custom name can be provided with a `[@@js.new]` attribute: ```` class foo: string -> my_class [@@js.new "MyConstr"] ```` ================================================ FILE: IMPLGEN.md ================================================ gen_js_api: generate implementations from interfaces ==================================================== The primary operating mode for gen_js_api is to generate .ml implementation from annotated .mli interfaces. These interfaces must follow a certain shape. They describe both the JavaScript components to be imported and how they should be reflected within OCaml. Usage ----- ``` $ gen_js_api my_module.mli ``` or with findlib: ``` $ ocamlfind gen_js_api/gen_js_api my_module.mli ``` This generates my_module.ml. Supported declarations ---------------------- Interfaces processed by gen_js_api can currently contain: - [Type declarations](TYPES.md): ```` type t = ... ```` See [this page](TYPES.md) for a description of supported types. Such a type declaration produces in the implementation an identical defininition, and associated `*_to_js` and `*_of_js` functions (which can be manually exported if needed). - [Value declarations](VALUES.md): ```` val f: tyexpr ```` This produces in the implementation a definition for such a value, whose content depends on three elements: the name of the value (`f` in the example), its declared type (`tyexpr`), and possible `[@@js.xxx]` attributes attached to the declaration in the interface. See [this page](VALUES.md) for supported forms of value declarations. - Sub-modules: ```` module M : sig ... end ```` This naturally produces in the implementation a corresponding sub-module: ```` module M = struct ... end ```` - Module aliases: If a module alias is declared in the interface, like: ```ocaml module M = ``` it is directly reflected in the generated implementation without modifications. - Module inclusion: To include a module `M` in the generated implementation, simply add ```ocaml include (module type of M) ``` in the corresponding interface. - [Class declarations](CLASSES.md) Verbatim sections ----------------- A floating attribute `[@@@js.stop]` tells the tool to ignore the remaining items until the end of the current (possibly nested) signature. This can be reverted with a floating attribute `[@@@js.start]`. This system makes it possible to specify fragments of the interface that should not generate any code in the implementation. A floating `[@@@js.implem ...]` tells the tool to generate some custom code in the implementation. The payload `...` is an OCaml structure, which is processed in the same way as in [ppx mode](PPX.md). Example: ```ocaml [@@@js.stop] val foo: int -> unit [@@@js.start] [@@@js.implem val foo_internal: string -> int -> unit [@@js.global "foo"] let foo = foo_internal "" ] ``` For the common case where verbatim sections are used to create custom value bindings, a `[@@js.custom]` attribute can be applied to a `val` declaration. The effect is that the `val` declaration itself is ignored (nothing is generated in the implementation), and a structure can be provided as the payload of the attribute. The example above is equivalent to: ```ocaml val foo: int -> int [@@js.custom val foo_internal: string -> int -> unit [@@js.global "foo"] let foo = foo_internal "" ] ``` and to: ```ocaml val foo: int -> int [@@js.custom] [@@js.implem ... ] ``` ================================================ FILE: INSTALL_AND_USE.md ================================================ gen_js_api: installation and usage instructions =============================================== Dependencies ------------ gen_js_api does not have any external build-time dependency except the OCaml compiler (version 4.03). Of course, it will be used in conjuncion with the js_of_ocaml compiler and runtime support. Installation (with OPAM) ------------------------ ```` opam install gen_js_api ```` Or, to track the development version: ```` opam pin add gen_js_api https://github.com/LexiFi/gen_js_api.git ```` Manual installation ------------------- ```` git clone https://github.com/LexiFi/gen_js_api.git cd gen_js_api make all make install # assuming opam-installer is installed ```` Usage (with dune) ----------------- - Invoking the [standalone tool](IMPLGEN.md) (`.mli` -> `.ml` generator): ``` (rule (targets my_unit.ml) (deps my_unit.mli) (action (run %{bin:gen_js_api} %{deps}))) ``` - Compiling binding (`.mli` and generated `.ml` files), user code which rely on the `Ojs` or with the [ppx processor](PPX.md): ``` (executables (names test_jquery) (js_of_ocaml) (libraries ojs js_of_ocaml) (preprocess (pps gen_js_api.ppx)) (modes byte) ) ``` - Compiling into JavaScript: Just ask dune to build the `*.bc.js` target. (e.g. `dune build test_jquery.bc.js`) Usage (with ocamlfind) ---------------------- - Invoking the [standalone tool](IMPLGEN.md) (`.mli` -> `.ml` generator): ``` ocamlfind gen_js_api/gen_js_api my_unit.mli ``` - Compiling binding (`.mli` and generated `.ml` files) and user code which rely on the `Ojs` module: ``` ocamlfind ocamlc -package gen_js_api my_unit.mli ocamlfind ocamlc -package gen_js_api my_unit.ml ``` - Compiling with the [ppx processor](PPX.md): ``` ocamlfind ocamlc -c -package gen_js_api my_prog.ml ``` - Linking the bytecode program: ``` ocamlfind ocamlc -o my_prog -package gen_js_api -linkpkg ... ``` - Compiling into JavaScript: ``` js_of_ocaml -o my_prog.js +gen_js_api/ojs_runtime.js my_prog ``` ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright 2015 by LexiFi. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: LOW_LEVEL_BINDING.md ================================================ gen_js_api: low-level binding to JavaScript =========================================== The code generated by gen_js_api relies on a `Ojs` module (the runtime support library). In the same way that OCaml `Obj` module exposes (unsafe) operations to manipulate arbitrary OCaml values (after casting them to a universal type `Obj.t`), `Ojs` allows to manipulate arbitrary JavaScript values through an `Ojs.t` universal type. `Ojs` encourages to think of native JS values as being "foreign" values, even though in practice, all OCaml values are represented as JS values when the OCaml code is compiled with js_of_ocaml. In particular, `Ojs` does not expose a function allowing to cast an arbitrary OCaml value to `Ojs.t` (this can always be done with `Obj.magic`). `Ojs.t` is similar to `Js.Unsafe.any` type, but it abstracts away from specific properties of how js_of_ocaml represents OCaml values. For instance the fact, that OCaml integers are encoded directly as JS numbers is not apparent in `Ojs`, and if this property was to change, client code would be unaffected. Abstracting away from js_of_ocaml encoding would also make it easy to change the way OCaml and JS are connected (either because of changes in js_of_ocaml's encoding of OCaml values, or because an entirely different technology is used, such as an OCaml bytecode interpreter written in JavaScript or a JavaScript engine linked with native OCaml code). Note that code generated by gen_js_api doesn't depend on js_of_ocaml's standard library (`Js` module), only on js_of_ocaml's runtime system. Our local `Ojs` interface maps directly to primitives provided by js_of_ocaml's runtime. Users of gen_js_api would not use `Ojs` very often, except to define "opaque sub-types" of `Ojs.t` in order to represent JS "classes" or "interfaces": ```ocaml type t = private Ojs.t ``` Occasionnaly, it it useful to go down to `Ojs` in order to define **custom mappings** between JS and OCaml. For instance, one can define a type for association lists indexed on strings in OCaml that are mapped to JS objects: ```ocaml module Dict : sig type 'a t = (string * 'a) list val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t end = struct type 'a t = (string * 'a) list let t_to_js ml2js l = let o = Ojs.empty_obj () in List.iter (fun (k, v) -> Ojs.set o k (ml2js v)) l; o let t_of_js js2ml o = let l = ref [] in Ojs.iter_properties o (fun k -> l := (k, js2ml (Ojs.get o k)) :: !l); !l end ``` ================================================ FILE: Makefile ================================================ # The package gen_js_api is released under the terms of an MIT-like license. # See the attached LICENSE file. # Copyright 2015 by LexiFi. .PHONY: all examples test test-promote clean install uninstall doc reindent publish all: dune build @install @DEFAULT examples: dune build @examples/DEFAULT doc: dune build @doc test: dune build @runtest test-promote: dune build @runtest --auto-promote clean: dune clean PREFIX := $$(opam config var prefix) install: opam-installer --prefix $(PREFIX) gen_js_api.install uninstall: opam-installer -u --prefix $(PREFIX) gen_js_api.install reindent: git ls-files *.ml *.mli | grep -v expected | xargs ocp-indent -i VERSION := $$(opam show . | grep "^version" | sort -u | sed 's/version *//') publish: all echo "Publishing v$(VERSION) ..." git tag -a v$(VERSION) git push origin v$(VERSION) opam publish ================================================ FILE: NAMING.md ================================================ gen_js_api: default naming convention ===================================== JavaScript names corresponding to bound components can always be specified explicitly (with the use of attributes). When the naming is left implicit, a JavaScript name is automatically derived from the OCaml name by applying the following rules: 1. uppercasing every character following an underscore; 2. removing every underscore; 3. uppercasing the first character when generating object constructor names. This automatic naming convention can be partially disabled by adding an attribute `[@js.verbatim_names]` on outer structures. When the attribute `[@js.verbatim_names]` is inherited from the context, the rule 1 and 2 are disabled. For instance, ```ocaml type myType = { x_coord : int; y_coord : int [@js "Y"]} ``` is mapped to a JS record with two fields named "xCoord" and "Y" whereas ```ocaml type myType = { x_coord : int; y_coord : int [@js "Y"]} [@@js.verbatim_names] ``` is mapped to a JS record with two fields named "x_coord" and "y". ================================================ FILE: NODE_RUNTIME_BINDINGS.md ================================================ # Binding Node.js Modules with Runtime Primitives This guide shows how to use the new runtime primitive support in `gen_js_api` to bind Node.js libraries that are usually obtained with `require(...)`. The feature hinges on two additions: - any `[@@js.global "@primitive_name"]` binding returns an `Ojs.t` pointing to a primitive exported by the JavaScript runtime; - a scope string that starts with `@` (for example `[@@@js.scope "@node_fs.promises"]`) resolves the first path component through the runtime primitives before following regular properties. Together, those tools let you keep your bindings declarative while delegating the actual `require` calls to a tiny JavaScript stub. ## Example layout ``` runtime_primitives/ dune imports.js imports.wat bindings.mli example.ml ``` ### Step 1 - expose the runtime primitives Create a JavaScript file that `require`s the Node modules you need and publishes them as js_of_ocaml runtime primitives. The js_of_ocaml linker recognises `//Provides: ` comments and registers the value under that name at startup. ```javascript // runtime_primitives/imports.js 'use strict'; //Provides: node_path var node_path = require('path'); //Provides: node_fs var node_fs = require('fs'); //Provides: node_version var node_version = require('process').version; //Provides: node_console var node_console = console.log; ``` When targeting WebAssembly you also need to expose the primitives through a `.wat` shim so that `wasm_of_ocaml` can import them at runtime: ```wat ;; runtime_primitives/imports.wat (global (export "_node_path") (import "js" "node_path") anyref) (global (export "_node_fs") (import "js" "node_fs") anyref) (global (export "_node_version") (import "js" "node_version") anyref) (global (export "_node_console") (import "js" "node_console") anyref) ``` List this file in your dune stanza so that js_of_ocaml ships it with the compiled artefacts: ``` ; runtime_primitives/dune (rule (targets bindings.ml) (deps bindings.mli) (action (run gen_js_api %{deps}))) (executable (name example) (libraries ojs) (preprocess (pps gen_js_api.ppx)) (modes js wasm) (js_of_ocaml (javascript_files imports.js)) (wasm_of_ocaml (javascript_files imports.js imports.wat))) ``` Adding the file to both `js_of_ocaml` and `wasm_of_ocaml` makes the primitives available in browser and wasm builds alike. ### Step 2 - bind module functions with `[@js.scope "@..."]` Use `module [@js.scope "@primitive"]` blocks to call methods on runtime primitives without manually threading the module objects. The interface below covers the synchronous filesystem API used in the reference JavaScript while keeping the underlying modules abstract. ```ocaml (* runtime_primitives/bindings.mli *) module [@js.scope "@node_fs"] Fs : sig val write_file_sync : string -> string -> unit [@@js.global "writeFileSync"] val read_file_sync : string -> encoding:string -> string [@@js.global "readFileSync"] val readdir_sync : string -> string array [@@js.global "readdirSync"] val append_file_sync : string -> string -> unit [@@js.global "appendFileSync"] end module [@js.scope "@node_path"] Path : sig val separator: string [@@js.global "sep"] val join : (string list [@js.variadic]) -> string [@@js.global "join"] end ``` Each module-level scope starts with `@`, so the ppx turns calls like `Fs.write_file_sync` into direct invocations on the corresponding Node module (`node_fs.writeFileSync` in this case) without requiring you to pass the module object around. ### Step 3 - bind direct values with `@`-prefixed `[@@js.global]` When you only need the primitive itself—such as a constant exported by a Node module—use the `@` prefix inside `[@@js.global]` to obtain it directly as an OCaml value. ```ocaml (* runtime_primitives/primitives_bindings.mli continued *) val node_version : string [@@js.global "@node_version"] val log : string -> unit [@@js.global "@node_console"] ``` These expand to `Jsoo_runtime.Js.runtime_value ...` calls and convert the results to the requested OCaml types, so you can expose constants or functions alongside the scoped modules described above. ### Step 4 - port the JavaScript example `main.ml` mirrors the original JavaScript snippet that writes, reads, appends, and re-reads a file while logging progress to the Node console. It relies on the scoped `Fs`/`Path` modules plus the direct `log`, `path_separator`, and `node_version` values. ```ocaml open Bindings let initial_content = "Hello, Node.js!" let appended_line = "\nAppending a new line." let encoding = "utf-8" let filename = "example.txt" let run () = let file = Path.join ["."; filename] in Fs.write_file_sync file initial_content; let content = Fs.read_file_sync file ~encoding in if content <> initial_content then failwith "Unexpected initial content"; log ("File content: " ^ content); let files = Fs.readdir_sync "." |> Array.to_list in if not (List.mem filename files) then failwith "example.txt missing from directory listing"; log ("Files in current directory: " ^ String.concat ", " files); Fs.append_file_sync file appended_line; let updated = Fs.read_file_sync file ~encoding in if updated <> initial_content ^ appended_line then failwith "Append failed"; log ("Updated content: " ^ updated); log ("Path separator reported by Node: " ^ Path.separator); log ("Node.js version: " ^ node_version) let () = run () ``` ### Putting it together 1. Declare each required Node module once in `imports.js` (and mirror them in `imports.wat` for wasm) using the js_of_ocaml `//Provides:` convention. 2. Export the files through dune so that the js_of_ocaml toolchain registers those primitives at runtime. 3. Map node modules in OCaml with `module [@js.scope "@primitive"]` blocks, and use `@`-prefixed `[@@js.global]` bindings for direct values. 4. Consume the generated modules from OCaml exactly as you would in JavaScript, as shown in `example.ml`. With these pieces in place you can keep writing high-level `gen_js_api` bindings while relying on the new runtime primitive support to bridge your OCaml code to Node-specific libraries provided via `require`. ================================================ FILE: PPX.md ================================================ gen_js_api: ppx mode ==================== While the primary mode of operation for gen_js_api is to generate an .ml file from an annotated .mli file, it is also possible to use it as a ppx preprocessor on an .ml file directly to insert local JS bindings. The `-ppx` command-line option must be the first argument passed to gen_js_api to enable the ppx mode: ``` $ ocamlc -c -ppx "gen_js_api -ppx" my_prog.ml ``` or with findlib: ``` $ ocamlfind ocamlc -c -package gen_js_api my_prog.ml ``` Note: the ppx currently does nothing on `.mli` files. Several forms are supported: - `[%js: ]` extension as a module expression. Examples: ```` include [%js: ] module M = [%js: ] ```` The signature is processed as if it were found in an .mli file, and the resulting structure is inserted in place of the `[%js: ...]` extension. See [this page](IMPLGEN.md) for a list of declarations supported in such interfaces. - `[@@js]` attributes on type declarations. Example: ```` type t = { x : int; y : int } [@@js] ```` This generates the corresponding `*_of_js` and `*_to_js` functions. In case of a multi-type declaration, each type must be annotated with `[@@js]` (if needed). See [this page](TYPES.md) for a description of support forms of type declarations. - `[%js.to: ty]` and `[%js.of: ty]` extensions on expressions. Example: ```` let x : Ojs.t = [%js.of: int list] [ 10; 20; 30 ] ```` This form generates the mapping function associated to a JS-able type. See [this page](TYPES.md) for a description of JS-able type. ================================================ FILE: README.md ================================================ gen_js_api: easy OCaml bindings for JavaScript libraries ======================================================== [![Build Status](https://github.com/LexiFi/gen_js_api/actions/workflows/workflow.yml/badge.svg)](https://github.com/LexiFi/gen_js_api/actions/workflows/workflow.yml) Overview -------- gen_js_api aims at simplifying the creation of OCaml bindings for JavaScript libraries. It must currently be used with the [js_of_ocaml compiler](https://github.com/ocsigen/js_of_ocaml), although other ways to run OCaml code "against" JavaScript might be supported later with the same binding definitions (for instance, [Bucklescript](https://github.com/bloomberg/bucklescript), or direct embedding of a JS engine in a native OCaml application). gen_js_api is based on the following ideas: - Authors of bindings write OCaml signatures for JavaScript libraries and the tool generates the actual binding code with a combination of implicit conventions and explicit annotations. - The generated binding code takes care of translating values between OCaml and JavaScript and of dealing with JavaScript calling conventions. - All syntactic processing is done by authors of bindings: the client code is normal OCaml code and does not depend on custom syntax nor on JS-specific types. gen_js_api can be used in two complementary ways: - [Generating .ml implementations from annotated .mli interfaces](IMPLGEN.md), in order to create the code for stub libraries. - As a [ppx preprocessor on implementations](PPX.md) to define local bindings. Examples -------- The repository contains some examples of OCaml bindings to JavaScript libraries created with gen_js_api: - Very partial [bindings to jQuery](examples/misc/jquery.mli), with some [example client code](examples/misc/test_jquery.ml). - Partial bindings to JavaScript [strings and regexps](examples/misc/js_str.mli) and JavaScript [dates](examples/js_date.mli). - Some [ad hoc test](examples/test) to exercise various features. - An example of a self-contained program, a [simple calculator](examples/calc/calc.ml), implementing local .bindings Documentation ------------- - [Install and use](INSTALL_AND_USE.md) - [Low-level binding to JavaScript](LOW_LEVEL_BINDING.md) - [Using gen_js_api to generate .ml from .mli](IMPLGEN.md) - [Using gen_js_api as a ppx processor](PPX.md) - [Default naming convention](NAMING.md) - [JS-able types and type declarations](TYPES.md) - [Value bindings](VALUES.md) - [Class-wrapping bindings](CLASSES.md) - [TODO list](TODO.md) Related projects ---------------- - [js_of_ocaml](https://github.com/ocsigen/js_of_ocaml): The compiler and runtime system on which gen_js_api relies. (Note: gen_js_api doesn't depend on js_of_ocaml's OCaml library, nor on its language extension.) - [goji](https://github.com/klakplok/goji): A DSL to describe OCaml bindings for JavaScript libraries. - [DefinitelyMaybeTyped](https://github.com/andrewray/DefinitelyMaybeTyped): A project to parse [DefinitelyTyped](https://github.com/borisyankov/DefinitelyTyped) interfaces and produce OCaml interfaces. - [ReScript](https://github.com/rescript-lang/rescript-compiler): Another compiler from OCaml to JavaScript, featuring the [genType](https://github.com/reason-association/genType) ppx for generating TS / Flow types and runtime converters. About ----- gen_js_api has been created by LexiFi for porting a web application from JavaScript to OCaml. The tool has been used in production since 2015. This gen_js_api package is licensed by LexiFi under the terms of the MIT license. See see [Changelog](CHANGES.md) Contact: alain.frisch@lexifi.com Contributors: - Alain Frisch - Sebastien Briais ================================================ FILE: TODO.md ================================================ TODO list for gen_js_api ======================== - Create reasonably complete bindings for JavaScript's stdlib (string, regexp), for the DOM, for jQuery, etc. - Add a safe mode, where the generated code is augmented with explicit checks (e.g. when casting a JS value to a string or integer, when accessing a property, etc). - Optimize generated code (for instance, lift calls to string_of_js on literals). - Idea: to facilitate binding and calling multiple methods at once, provide something like (jQuery example): ```ocaml val set: ?text:string -> ?hide:unit -> ?css:(string * string) -> t -> unit [@@js.multicall] ``` One can then write: ```ocaml set ~text:"Hello" ~hide:() node ``` Each provided argument yields one method call (in the order where arguments are declared, of course). This is mostly interesting when methods are used to "set" internal properties, and when the different calls commute. This could be simulated with: ```ocaml val set: ?text:string -> ?hide:unit -> ?css:(string * string) -> t -> unit [@@@js.custom val set_text: t -> string -> unit [@@js.meth "text"] let set ?text ... x = Option.iter (set_text x) text; ... ] ``` - Optional arguments on JS methods are usually at the end. But this forces to add a `unit` pseudo-argument. One could have an (optional) convention to push optional arguments at the end of the JS call even though there are not in the OCaml type. This would also work for instance methods: ```ocaml val foo: ?bla:int -> t -> int ``` instead of: ```ocaml val foo: t -> ?bla:int -> unit -> int ``` - When defining a binding to a function with `[@@js.global "foo.bar"]`, this is currently interpreted as calling this global function. One could interpret it as calling the bar method on object foo, which would have the effect of assigning `this` during the function evaluation. ================================================ FILE: TYPES.md ================================================ Types supported in gen_js_api ============================= JS-able types ------------- A JS-able type is an OCaml type whose values can be mapped to and from JavaScript objects. The following types are supported out-of-the-box: - Basic built-in types: `string`, `int`, `bool`, `float` and `Ojs.t`. - Tuples of JS-able types (mapped to JS arrays). - Sequences of JS-able types: `array` and `list`, both mapped to JS arrays (which are assumed to be indexed by integers 0..length-1). - Options on JS-able types. They are mapped to the same type as their parameter: `None` is mapped to JS `null` value, and both `null` and `undefined` are mapped back to `None`. This encoding doesn't support nested options in a faithful way. - Arrows (see section below). - Polymorphic variants with only constant variants are supported (see the section on enums below). - Polymorphic variants can also be used to encode non-discriminated unions on the JS side (see the section on union types below). - Polymorphic variants can also be used to encode discriminated unions on the JS side (see the section on discriminated union types below). - Free type variables like `'a`, they will involve no runtime mapping when moving between OCaml and JS (see type variable section). An arbitrary non-parametrized type with path `M.t` is JS-able if the following two values are available in module `M`: ```ocaml val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t ``` The name of these values is obtained by appending `_of_js` or `_to_js` to the local name of the type. It is thus possible to define JS-able manually by defining these two functions. Type and class declarations processed by gen_js_api (see sections below) create JS-able type (by generating those functions automatically). Parametrized types can also be JS-able. It is currently assumed that such types are covariant in each of their parameter. Mapping functions take extra arguments corresponding to the mapper for each parameter. For instance, a type `'a t` would need to come with the following functions: ```ocaml val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t ``` Arrow types ----------- Arrow types can also be used in contexts that expect JS-able types. All arguments must be JS-able types, and a final `unit` pseudo-argument is allowed (and mandatory when there is no real argument). The function's result can be either a JS-able type or `unit`. Note that `unit` is not considered as a proper JS-able type: it is only allowed in these two contexts (as the result, or the final pseudo-argument). Arguments can be **labelled or optional**. Labels are simply ignored on the JS side. Optional arguments have different treatments: - When mapping an OCaml function to JS (e.g. a callback), optional arguments are treated as normal values of an option type (i.e. both `null` and `undefined` are mapped to `None`). - When mapping a JS function to OCaml, it is possible to specify a default value to fill in a missing argument: ```ocaml val f: t -> ?x:(int [@js.default 0]) -> unit -> t ``` If no default value is provided and the argument is missing, the argument is *dropped* from the list of arguments passed to the JS call (this apply to function/method/constructor calls). - In `[@@js.builder]` values, missing optional arguments are ignored (they don't create any property on the object). There is a special treatment for optional argument on a `[@js.variadic]` argument (see below), in which case a missing value is interpreted as an empty list (i.e. no extra arguments). When mapping an OCaml function to JS, the **function arity** is the number of real arguments (not counting the final `unit`) and the semantics is the standard one for JS functions: missing arguments are filled with `undefined` and extra arguments are dropped. The correct way to support a calling convention where the JS caller might not provide all arguments to a function defined in OCaml is to use optional arguments (or just arguments with option types) on the OCaml side. In order to define **functions that return functions**, one can put a `[@js.dummy]` attribute (or any arbitrary attribute) on the resulting type : ```ocaml t1 -> (t2 -> t3 [@js.dummy]) ``` Without the attribute, such a type would be parsed as a function of arity 2 (returning type `t3`). **Variadic functions** are supported, by adding a `[@js.variadic]` attribute on the last parameter (which will represent all remaining arguments): ```ocaml val sep: string -> (string list [@js.variadic]) -> string ``` Type declarations ----------------- All type declarations processed by gen_js_api create JS-able types, i.e. associated `*_to_js` and `*_to_js` mapping functions. A optional "private" modifier is allowed on the type declaration (in the interface) and dropped from the generated definition (in the implementation). Mutually recursive type declarations are supported. - "Abstract" subtype of `Ojs.t`: ```ocaml type t = private Ojs.t ``` This is used to bind to JS "opaque" objects, with no runtime mapping involved when moving between OCaml and JS (mapping functions are the identity). - Abstract type ```ocaml type t ``` This will generate `type t = Ojs.t` in the implementation. This is very similar to the case above. - Type abbreviation: ```ocaml type t = tyexp ``` (formally, abstract types with a manifest). This assumes that the abbreviated type expression is itself JS-able. Note that the first kind of type declaration above (abstract subtypes of `Ojs.t`) are a special kind of such declaration, since `abstract` is always dropped and `Ojs.t` is JS-able. - Record declaration: ```ocaml type t = { .... } ``` This assumes that the type for all fields are JS-able. Fields can be mutable (but conversions still create copies). Polymorphic fields are not yet supported. OCaml record values of this type are mapped to JS objects (one property per field). By default, property names are equal to OCaml labels, but this can be changed manually with a `[@js]` attribute. ```ocaml type myType = { x : int; y : int [@js "Y"]} ``` - Parametrized Type: It is allowed to parametrize types processed by gen_js_api as long as type variables does not occur at contravariant positions. For instance : ```ocaml type ('a, 'b) coord = { x : 'a; y : 'b} ``` is accepted while : ```ocaml type 'a t = 'a -> int ``` is rejected. - Sum type declaration, mapped to enums (see Enums section). - Sum type declaration with non constant constructors, mapped to records with a discriminator field (see Sum types section). - Arbitrary type with custom mappings If you want to use a type that is not supported by gen_js_api, you can make it JS-able by providing your own `*_of_js` and `*_to_js` functions (custom mappings) with a `[@@js.custom ...]` attribute. ```ocaml type t = ... [@@js.custom { of_js = (fun ... -> ...); to_js = (fun ... -> ...) } ] ``` This is particularly useful when the type is mutually recursive with other types which can be processed by gen_js_api. See the [section on manually created bindings](LOW_LEVEL_BINDING.md) for more information on writing custom mappings by hand. Not to be confused with [the `[@@js.custom]` attribute for `val` declarations](IMPLGEN.md#verbatim-sections). Enums mapped to polymorphic variants or sum types ------------------------------------------------- Either polymorphic variants or normal sum types (all with constant constructors) can be used to bind to "enums" in JavaScript. By default, constructors are mapped to the JS string equal to their OCaml name, but a custom translation can be provided with a `[@js]` attribute. This custom translation can be a string or an integer literal or a float literal. ```ocaml type t = | Foo [@js "foo"] | Bar [@js 42] | Baz [@js 4.2] | Qux [@@js.enum] type t = [`foo | `bar [@js 42] | `baz [@js 4.2] | `Qux] [@@js.enum] ``` It is possible to specify constructors with one argument of type (int or float or string), used to represent "all other cases" of JS values. ```ocaml type status = | OK [@js 1] | KO [@js 2] | OO [@js 1.5] | OtherS of string [@js.default] | OtherI of int [@js.default] [@@js.enum] ``` There cannot be two default constructors with the same argument type. Also, there cannot be default constructors of type int and float at the same time. Sum types mapped to records with a discriminator field ------------------------------------------------------ Either polymorphic variants or sum types can be mapped to JS records with a discriminator field. By default, the name of the discriminator field is `kind`, but this can be changed by specifying a field name as attribute value of the `[@@js.sum]` attribute. The value of the discriminator field is set to the representation of the constructor name: it is derived automatically from the constructor name but can also be specified with a `[@js]` attribute. In this latter case, it can be either a string or an integer. A constant constructor is simply mapped to a record containing the discriminator field. A unary constructor is mapped to a record containing two fields: the discriminator field and an argument field representing the unique argument of the constructor. The argument field name is by default `arg`, but this can be changed with a `[@js.arg]` attribute. At most one unary constructor may have the attribute `[@js.default]` and the argument of this constructor must be of type `Ojs.t`. In this case, this constructor is used to handle the default case when either the discriminator field is equal to an unexpected value or even worse when the discriminator field is absent (from JS to ML). In the other direction (from ML to JS), the unique argument is used as JavaScript representation. A nary constructor is mapped to a record containing two fields: the discriminator field and an argument field set to an array representing the arguments of the constructor. Once again, the argument field name is by default `arg`, but this can be changed with a `[@js.arg]` attribute. In the case of polymorphic variant, if the argument is a tuple, then the polymorphic variant constructor is considered to be n-ary. Finally, an inline record constructor is mapped to a record containing all the field of the record in addition of the discriminator field. The name of the fields are derived from the name of the record fields. As usual, these names can be customized using a `[@js]` directive. This last case only applies to sum types. ```ocaml type t = | A | B of int | C of int * string | D of {age: int; name: string} | Unknown of Ojs.t [@js.default] [@@js.sum] ``` The following declaration is equivalent to the previous one. ```ocaml type t = | A [@js "A"] | B of int [@js.arg "arg"] | C of int * string [@js.arg "arg"] | D of {age: int [@js "age"]; name: string} | Unknown of Ojs.t [@js.default] [@@js.sum "kind"] ``` Union types ----------- It is common for JS functions to allow arguments of several different types (for instance, a string or an object). To represent this calling convention, one can use polymorphic variants: ``` val f: t -> ([`Str of string | `Obj of t | `Nothing] [@js.union]) -> ... ``` When the `[@js.union]` attribute is used without any other option, only the ML to JS function is generated. The ML to JS conversion function simply maps constant constructors to the `null` value, unary constructors to the value of the constructor argument, and n-ary constructors to the array of the constructor argument values (i.e. treated as a tuple). For generating the converse function, one needs to have a way to distinguish JS values in the union type. At the moment, union types with a discriminator field argument are supported. To indicate the name of the field, one can add extra option `on_field "kind"` (where "kind" is the name of the field) to the `[@js.union]` attribute. In this case, the JS to ML conversion function will inspect the value of the field named "kind" and will map the JS value to the corresponding unary constructor. As for sum types, the value of the discriminator field is deduced from the name of the constructors but it can always be overridden by using a `[@js]`attribute. ``` type close_path type moveto_abs type svg_path_seg = | Unknown of Ojs.t [@js.default] | Close_path of close_path [@js 1] | Moveto_abs of moveto_abs [@js 2] [@@js.union on_field "pathSegType"] ``` As for sum types, at most one unary constructor may have the `[@js.default]` attribute and the argument of this constructor must be of type `Ojs.t`. In this case, this constructor is used to handle the default case when either the discriminator field is equal to an unexpected value or even worse when the discriminator field is absent (from JS to ML). Discriminated union types ------------------------- It is common for JS functions to allow arguments of several different types (for instance, a string or an object), whose type depends on a preceding argument. To represent this calling convention, one can use polymorphic variants: ``` val f: t -> ([`Str of string | `Obj of t | `Nothing] [@js.enum]) -> ... ``` This generalisation of the `[@js.enum]` attribute can only be used on polymorphic variant used in contravariant context (i.e. to describe mapping from OCaml to JavaScript, not the other way around). With this calling convention, first the representation of the constructor (which can be an integer or a float or a string, which is derived automatically if not specified with a `[@js]` attribute) is passed, followed by the n arguments of the constructor. Type variables -------------- Unbound type variable are processed implicitly coerced from and to `Ojs.t` using unsafe coercion. This is useful when writing bindings to JS functions that rely on data structures that can contain OCaml values as is. For example, to directly use the JS arrays to store OCaml values in their original runtime representation, a `JsArray` module could be defined: ```ocaml module JsArray : sig type 'a t = private Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val create: int -> 'a t [@@js.new "Array"] val push: 'a t -> 'a -> unit [@@js.call] val pop: 'a t -> 'a option [@@js.call] end ``` **Important:** the functions generated from types with variables will only apply the identity function when converting to or from JS. So this approach should never be used to interface with a JS function that expects the types to be converted. For example, the following would break if we used it as a binding to the [`Array.join`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) function: ```ocaml val join: string JsArray.t -> string -> string ``` Indeed, the objects contained in the JsArray.t are not JavaScript strings but representation of caml strings. To properly do this, we would want the strings contained in the data structure to be converted /to JS types, this would require conversion functions not ignoring their first argument that are manually implemented. One approach is to use functors instead: ```ocaml (* Ojs.T is defined as follows: module type T = sig type t val t_to_js : t -> Ojs.t val t_of_js : Ojs.t -> t end *) module JsArray (E: Ojs.T): sig type t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val create: unit -> t [@@js.new "Array"] val push: t -> E.t -> unit [@@js.call] val pop: t -> E.t option [@@js.call] end module StringArray : sig include (module type of JsArray(Ojs.String)) val join: t -> string -> string [@@js.call] end ``` By moving the type parameters to the functor arguments, you can enforce the value conversion between JS types and OCaml types. You can also use [first-class modules](VALUES.md#first-class-modules) for value bindings, which will be used to convert the polymorphic values and thus making the binding safe: ```ocaml module[@js.scope "console"] Console: sig val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global] end ``` You can also create safe bindings manually with the low level functions provided by `Ojs` module. See the [section on manually created bindings](LOW_LEVEL_BINDING.md) for more information. ================================================ FILE: VALUES.md ================================================ Value bindings in gen_js_api ============================ Supported forms --------------- - Method call: ```ocaml val my_method: t -> T1 -> ... -> Tn -> T [@@js.call] ``` Calling the function on a first argument `o` of type `t` corresponds to calling the method `myMethod` on the underlying JS object, with other arguments passed to it. By default, the name of the method on the JS side is derived from the name of the OCaml value (`myMethod` above). It is possible to specify a custom name explicitly, for instance for cases where the JS name is not a valid OCaml (lowercase-)identifier, or to support overloading (exposing multiple OCaml functions that correspond to different types given to the same JS method): ```ocaml val my_method: t -> T1 -> ... -> Tn -> T [@@js.call "JavaScriptMethodName"] ``` - Function Application ```ocaml val apply: t -> T1 -> ... -> Tn [@@js.apply] ``` Calling the function on a first argument `f` of type `t` corresponds to calling the underlying JS function object directly, with other arguments passed to it. This is particularly useful when binding to a "callable" JS object (an object that is also a function), or [a function type in a TypeScript interface](https://www.typescriptlang.org/docs/handbook/interfaces.html#function-types). The name of the function need not necessarily be `apply` as long as the `[@@js.apply]` attribute is present. When the function you want to bind is a "newable" one (a function that must be called with a prefix `new`, e.g. constructors), use `[@@js.apply_newable]` instead. This is especially useful to bind to constructor interfaces in TypeScript. ```ocaml module FooConstructor: sig type t val new_: t -> Foo.t [@@js.apply_newable] end val fooConstructor: FooConstructor.t [@@js.global "Foo"] ``` When the "callable" object you want to bind to is a global object, the `[@@js.invoke]` attribute along with the `[@js.scope]` attribute (see below) may be used to call it. For instance, you can write ```ocaml module[@js.scope "JavaScriptClassName"] C : sig val invoke: T1 -> ... -> Tn -> t [@@js.invoke] end (* usage *) let x = C.invoke arg1 ... argn ``` instead of ```ocaml module C : sig type t val apply: t -> T1 -> ... -> Tn -> t [@@js.apply] end val c: C.t [@@js.global "JavaScriptClassName"] (* usage *) let x = C.apply c arg1 ... argn ``` - Object constructor: ```ocaml val new_my_class: T1 -> ... -> Tn -> t [@@js.new] ``` Corresponds to calling a JS constructor with arguments passed to it. By default, the name of the class on the JS side is derived from the name of the OCaml value (`MyClass` above): in this case, the value name must start with the `new_` prefix which is dropped and the remaining name is capitalize to obtain the class name. It is also possible to specify a custom name explicitly. ```ocaml val f: T1 -> ... -> Tn -> t [@@js.new "JavaScriptClassName"] ``` As for global values, it is possible to indicate the access path by using `[@js.scope]` attributes on englobing modules (see below). When the global object is itself an object constructor, the `[@@js.create]` attribute may be used to instantiate it. For instance, ```ocaml module[@js.scope "JavaScriptClassName"] C : sig val create: T1 -> ... -> Tn -> t [@@js.create] end ``` is the same as ```ocaml module C : sig val create: T1 -> ... -> Tn -> t [@@js.new "JavaScriptClassName"] end ``` - Global value or function: ```ocaml val x: t [@@js.global] ``` This creates an OCaml value that corresponds to a globally accessible JavaScript value. This is used to access both global objects (e.g. the `window` object) and global functions (e.g. `alert`). It is also possible to specify a custom name for the JavaScript variable: ```ocaml val x: t [@@js.global "JavaScriptValueName"] ``` Example: ```ocaml val alert: string -> unit [@@js.global] ``` By default, a global value or function is taken from the global object. However, it is possible to specify an access path by using `[@js.scope]` attribute on englobing modules (see the Scope section). - Property getter ```ocaml val prop: t -> T [@@js.get] ``` Calling the function on a first argument `o` of type `t` corresponds to getting the `prop` property of the underlying JS object. A custom name for the JS property can be specified: ```ocaml val get_property: t -> T [@@js.get "MyProp"] ``` - Property setter ```ocaml val set_prop: t -> T -> unit [@@js.set] ``` Calling the function on a first argument `o` of type `t` corresponds to setting the `prop` property of the underlying JS object. Note that the value name must start with the `set_` prefix, which is dropped to obtain the property name. A custom name for the JS property can also be specified (in which case the name of the value can be arbitrary): ```ocaml val modify_prop: t -> T -> unit [@@js.set "prop"] ``` - Index getter ```ocaml val get: t -> index -> T option [@@js.index_get] ``` Corresponds to getting from an index accessor or [an index signature in a TypeScript interface](https://www.typescriptlang.org/docs/handbook/interfaces.html#indexable-types). The return type may be `T` or `T option`, depending on whether the property is optional or not. The name of the function need not necessarily be `get` as long as the `[@@js.index_get]` attribute is present. `index` must be `int`, `string`, or abstract types holding a JavaScript `number` or `string` value. - Index setter ```ocaml val set: t -> index -> T -> unit [@@js.index_set] ``` Corresponds to setting to an index accessor or [an index signature in a TypeScript interface](https://www.typescriptlang.org/docs/handbook/interfaces.html#indexable-types). The name of the function need not necessarily be `set` as long as the `[@@js.index_set]` attribute is present. `index` must be `int`, `string`, or abstract types holding a JavaScript `number` or `string` value. - Global getter ```ocaml val get_x: unit -> T [@@js.get "x"] val get_sub_x: unit -> T [@@js.get "MyObject.x"] ``` This creates a function which returns the current value of a global variable or of a (possibly nested) inner field of a global variable. As for global values, it is possible to indicate the access path by using `[@js.scope]` attributes on englobing modules. - Global setter ```ocaml val set_x: T -> unit [@@js.set "x"] val set_sub_x: T -> unit [@@js.set "MyObject.x"] ``` This creates a function which sets the value of a global variable or of a (possibly nested) inner field of a global variable. As for global values, it is possible to indicate the access path by using `[@js.scope]` attributes on englobing modules. - Cast ```ocaml val cast: t1 -> t2 [@@js.cast] ``` Calling this function performs an unchecked cast from type `t1` to type `t2`, going through the JavaScript representation (i.e. applying mapper from `t1` to the underlying JS object, and back using the mapper for `t2`). - Literal object builder: ```ocaml val make: l1:T1 -> ... -> ln:tn -> t [@@js.builder] ``` Corresponds to creating a JS plain object with fields initialized with the provided values. The name of the function (`make` in the example) does not correspond to any concept in JS. By default, the JS field names are derived from OCaml labels, but it is also possible to override that with a `[@js]` attribute on the argument's type. All fields must be labeled or optional, or come with such an attribute. Optional arguments (but not non-optional argument with optional type) are treated in a special way: no field is created in the JS object if the parameter is not provided on the call site (without this special behavior, the treatment would be to set the field to `null`, which is the encoding of `None`). Example: ```ocaml type t = private Ojs.t val mk: ?children:t list -> age:int -> (string[@js "name"]) -> t [@@js.builder] ``` - Custom binding: ```ocaml val f: ... [@@js.custom let f = ... ] ``` The val declaration itself doesn't produce anything in the implementation. Instead, custom OCaml code that goes into the implementation must be provided explicitly. See [Verbatim section](IMPLGEN.md) for more details and examples. Calling a function/constructor by different means ------------------------------------------------- | | as a function | as a constructor | |-------------------------------------|------------------------|------------------------| | call the first argument | `[@@js.apply]` | `[@@js.apply_newable]` | | call the global object | `[@@js.invoke]` | `[@@js.create]` | | call a member of the first argument | `[@@js.call "methodName"]` | N/A | | call a member of the global object | `[@@js.global "funcName"]` | `[@@js.new "ClassName"]` | Scope ----- The signature attribute `[@@@js.scope "property"]` changes the reference to the current global object by following the property provided as payload. Nested scopes work as if the access path were composed by concatenation of all the names indicated by [@js.scope] attribute, separated by a '.'. A simple use case is to bind to JavaScript values packed in singleton objects or classes. For instance, ```ocaml module[@js.scope "console"] Console: sig val log: string -> unit [@@js.global] end ``` is equivalent to ```ocaml module Console: sig val log: string -> unit [@@js.global "console.log"] end ``` When attached directly to a module, the payload of `[@@js.scope]` may be omitted, it will be implicitly filled with the module name (preserving the capitalization !). Before version 1.0.7, the presence of `[@@js.scope]` used to change the behavior of automatic bindings. It is no longer the case. An experimental feature also allows to pass an expression of type `Ojs.t` as a payload to replace the global object. The intended use case is to allow dynamic loading of modules. There is also a tuple notation `[@js.scope (s1, ..., sn)]` that helps writing nested scopes. It is equivalent to `[@js.scope sn]...[@js.scope s1]`. For instance, the following annotated modules will generate the same code: ```ocaml module NestedScope0 : sig val f: string -> unit [@@js.global "outer.inner.f"] end module [@js.scope ("outer", "inner")] NestedScope1 : sig val f: string -> unit [@@js.global] end module NestedScope2 : sig val f: string -> unit [@@js.global] end [@js.scope "inner"] [@js.scope "outer"] ``` First-class modules ------------------- As introduced in [Type variables](TYPES.md#type-variables), you can use first-class modules to enforce JS/OCaml value conversion on polymorphic functions. ```ocaml module[@js.scope "console"] Console: sig val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global] end ``` There are several restrictions when using first-class modules: * First-class modules must be annotated with `@js`. - This attribute indicates that it should only be used to convert values and should not be passed directly to the JS function. * First-class modules must come before any other normal types. - The following is invalid because the first-class module comes after a normal type `'a`: ```ocaml val log: 'a -> (module[@js] Ojs.T with type t = 'a) -> unit [@@js.global] ``` * A first-class module to convert a type variable `'x` must be in the form of `(module[@js] Ojs.T with type t = 'x)`. - The following is invalid because it has a different form (though the meaning is equivalent): ```ocaml module type MyOjsT = Ojs.T val log: (module[@js] MyOjsT with type t = 'a]) -> 'a -> unit [@@js.global] ``` * First-class modules can't be used outside of value bindings. - The following is invalid because it is used in a type alias: ```ocaml type 'a logger = (module[@js] Ojs.T with type t = 'a) -> 'a -> unit val log: 'a logger [@@js.global] ``` To use bindings with first-class modules, you just have to pass the enclosing modules of the types: ```ocaml module[@js.scope "Person"] Person : sig type t (* these functions must be present *) val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val create: string -> t [@@js.create] end let p = Person.create "Foo";; Console.log (module Person) p;; (* Person { name: 'Foo' } *) ``` For built-in types, there are pre-defined modules available in the `Ojs` module: ```ocaml Console.log (module Ojs.String) "hello, world!";; Console.log (module Ojs.Int) 42;; Console.log (module Ojs.List(Ojs.String)) ["hello"; "world!"];; ``` Automatic binding (Deprecated since 1.0.7) ------------------------------------------ Some conventions, based on the declared value names and their types, allow to infer implicitly the `[@@js.xxx]` attributes on value declarations in most cases. *This feature has been deprecated starting from version 1.0.7*. All values declaration should be annotated with an explicit attribute. Otherwise a preprocessor warning will be emitted. Note that in all modes the declaration of conversion functions generated from types are ignored in order to expose the generated functions. This means all value of declarations of the form: ```ocaml val τ_to_js: ... -> Ojs.t ``` or the form ```ocaml val τ_of_js: ... -> τ ``` The rules are applied in order: - If the value is a function whose result is a named type `... -> τ` and the name is `create`, then the declaration is assumed to be a `[@@js.create]` object creation. - If the value is a function whose result is a named type `... -> τ` and its name starts with `new_`, then the declaration is assumed to be a `[@@js.new]` object creation (on the class whose name is obtained by dropping the `new_`prefix). - If the value is a function with a single argument `τ1 -> unit` and its name starts with `set_`, then the declaration is assumed to be a `[@@js.set]` global setter (whose name is obtained by dropping the `set_` prefix). - If the value is a function returning `unit` with three arguments whose first argument is a named type `τ -> τ2 -> τ3 -> unit` and the name is `set`, then the declaration is assumed to be a `[@@js.set_index]` index setter. - If the value is a function with two arguments `τ1 -> τ2 -> unit` and its name starts with `set_`, then the declaration is assumed to be a `[@@js.set]` property setter (on the property whose name is obtained by dropping the `set_` prefix). - If the value is a function with a single argument (named type) `τ -> unit`, then the declaration is assumed to be a `[@@js.call]` method call. - If the value is a function with two arguments whose first argument is a named type `τ -> τ2 -> τ3` (and `τ3` is not `unit`) and the name is `get`, then the declaration is assumed to be a `[@@js.index_get]` index getter. - If the value is a function with a single argument (named type) `τ -> τ2` (and `τ2` is not `unit`), then the declaration is assumed to be a `[@@js.get]` property getter. - If the value is a function with a single argument `unit -> τ2`, then the declaration is assumed to be a `[@@js.get]` global getter. - If the value is a function whose first argument is a named type `τ -> ...` and the name is `apply`, then the definition is assumed to be a `[@@js.apply]` function object application. - If the value is a function whose first argument is a named type `τ -> ...` (and the name is not `apply`), then the definition is assumed to be a `[@@js.call]` method call. - Otherwise, the declaration is assumed to be a `[@@js.global]` value. This applies in particular for any non-functional type. ================================================ FILE: dune ================================================ (env (dev (flags (:standard)))) (deprecated_library_name (old_public_name gen_js_api) (new_public_name ojs)) ================================================ FILE: dune-project ================================================ (lang dune 3.17) (name gen_js_api) (version 1.1.7) (maintainers "Alain Frisch ") (authors "Alain Frisch " "Sebastien Briais ") (source (github LexiFi/gen_js_api)) (generate_opam_files true) (license MIT) (package (name ojs) (synopsis "Runtime Library for gen_js_api generated libraries") (description "To be used in conjunction with gen_js_api") (depends (ocaml (>= 4.13)) (js_of_ocaml-compiler (>= 6.3.0))) ) (package (name gen_js_api) (synopsis "Easy OCaml bindings for JavaScript libraries") (description " gen_js_api aims at simplifying the creation of OCaml bindings for JavaScript libraries. Authors of bindings write OCaml signatures for JavaScript libraries and the tool generates the actual binding code with a combination of implicit conventions and explicit annotations. gen_js_api is to be used with the js_of_ocaml compiler. ") (conflicts (js_of_ocaml-compiler (< 6.3.0))) (depends (ocaml (>= 4.13)) (ppxlib (>= 0.37)) (js_of_ocaml-compiler :with-test) (ojs (= :version))) ) ================================================ FILE: examples/calc/calc.html ================================================ Calculator ================================================ FILE: examples/calc/calc.ml ================================================ module Element = [%js: type t val t_of_js: Ojs.t -> t val append_child: t -> t -> unit [@@js.call] val set_attribute: t -> string -> string -> unit [@@js.call] val set_onclick: t -> (unit -> unit) -> unit [@@js.set] ] module Window = [%js: type t val instance: t [@@js.global "window"] val set_onload: t -> (unit -> unit) -> unit [@@js.set] ] module Document = [%js: type t val instance: t [@@js.global "document"] val create_element: t -> string -> Element.t [@@js.call] val create_text_node: t -> string -> Element.t [@@js.call] val body: t -> Element.t [@@js.get] ] let element tag children = let elt = Document.create_element Document.instance tag in List.iter (Element.append_child elt) children; elt let textnode s = Document.create_text_node Document.instance s let td ?colspan child = let elt = element "td" [child] in begin match colspan with | None -> () | Some n -> Element.set_attribute elt "colspan" (string_of_int n) end; elt let tr = element "tr" let table = element "table" let center x = element "center" [x] let button x f = let elt = element "button" [textnode x] in Element.set_attribute elt "type" "button"; Element.set_onclick elt f; elt module Engine = struct type op = Add | Sub | Mul | Div type state = { x: float; y: float; operator: op option; input: bool; equal: bool; comma: int; } let initial = { x = 0.; y = 0.; operator = None; input = false; equal = false; comma = 0 } let make_op op x y = match op with | Add -> x +. y | Sub -> x -. y | Mul -> x *. y | Div -> x /. y let of_digit d = float_of_int d let add_digit x comma d = if comma = 0 then 10. *. x +. float_of_int d, comma else x +. float_of_int d /. (10. ** (float_of_int comma)), comma + 1 let input_digit ({x; y; operator = _; input; equal; comma} as state) d = let y = if equal then y else x in let x, comma = if input then add_digit x comma d else of_digit d, 0 in {state with x; y; comma; input = true} let apply_comma ({input; comma; _} as state) = if comma = 0 then if input then {state with comma = 1} else {(input_digit state 0) with comma = 1} else state let apply_equal ({x; y; operator; input; equal; comma = _} as state) = match operator with | None -> {state with y = x; input = false; equal = true} | Some o -> if input && not equal then {state with x = make_op o y x; y = x; input = false; equal = true} else {state with x = make_op o x y; equal = true} let apply_op ({input; equal; _} as state) op = if input && not equal then {(apply_equal state) with operator = Some op; equal = false} else {state with operator = Some op; equal= false; input = false} let print_op ppf = function | None -> Printf.fprintf ppf " " | Some Add -> Printf.fprintf ppf "+" | Some Sub -> Printf.fprintf ppf "-" | Some Mul -> Printf.fprintf ppf "*" | Some Div -> Printf.fprintf ppf "/" let print ppf {x; y; operator; input; equal; comma} = Printf.fprintf ppf "x = %g, y = %g, op = %a, input = %b, equal = %b, comma = %d" x y print_op operator input equal comma end let widget () = let open Engine in let state = ref initial in let res, set_value = let elt = element "input" [] in Element.set_attribute elt "type" "text"; Element.set_attribute elt "readonly" ""; let set_value v = Element.set_attribute elt "value" (string_of_float v) in elt, set_value in let update st = Printf.printf "%a\n" print st; state := st; set_value !state.x in let reset() = update initial in reset(); let binop op () = update (apply_op !state op) in let equal () = update (apply_equal !state) in let comma () = update (apply_comma !state) in let figure digit = let f () = update (input_digit !state digit) in button (string_of_int digit) f in let c l = td l in let nothing () = element "div" [] in table [tr [td ~colspan:4 res]; tr (List.map c [nothing(); button "C" reset; nothing(); button "/" (binop Div)]); tr (List.map c [figure 7; figure 8; figure 9; button "*" (binop Mul)]); tr (List.map c [figure 4; figure 5; figure 6; button "-" (binop Sub)]); tr (List.map c [figure 1; figure 2; figure 3; button "+" (binop Add)]); tr (List.map c [nothing(); figure 0; button "." comma; button "=" equal])] let go () = Element.append_child (Document.body Document.instance) (center (widget())) let () = Window.set_onload Window.instance go ================================================ FILE: examples/calc/dune ================================================ (executables (names calc) (libraries ojs) (preprocess (pps gen_js_api.ppx)) (modes js)) (rule (targets calc.js) (deps calc.bc.js) (action (run cp %{deps} %{targets}))) (alias (name DEFAULT) (deps calc.js calc.html)) ================================================ FILE: examples/misc/dune ================================================ (executables (names test_jquery) (libraries ojs) (preprocess (pps gen_js_api.ppx)) (modes js)) (rule (targets jquery.ml) (deps jquery.mli) (action (run %{bin:gen_js_api} %{deps}))) (rule (targets js_date.ml) (deps js_date.mli) (action (run %{bin:gen_js_api} %{deps}))) (rule (targets js_str.ml) (deps js_str.mli) (action (run %{bin:gen_js_api} %{deps}))) (rule (targets test_jquery.js) (deps test_jquery.bc.js) (action (run cp %{deps} %{targets}))) (alias (name DEFAULT) (deps test_jquery.js test_jquery.html)) ================================================ FILE: examples/misc/jquery.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** Partial binding to jQuery, serving as an illustration of gen_js_api. The binding is far from complete! *) [@@@js.implem [@@@ocaml.warning "-22"]] (** {2 Sets of elements} *) type t = private Ojs.t val selector: string -> t [@@js.global "jQuery"] (** Either select a set of elements from the current document, or create a new element (if given a string such as "
". *) val wrap: Ojs.t -> t [@@js.global "jQuery"] val explode: t -> t list [@@js.custom let explode x = Ojs.list_of_js wrap x] val find: t -> string -> t list [@@js.custom val find: t -> string -> t [@@js.call "find"] let find x sel = explode (find x sel) ] val text: t -> string [@@js.call] val set_text: t -> string -> unit [@@js.call "text"] val update_text: t -> (int -> string -> string) -> unit [@@js.call "text"] val append_html: t -> string -> unit [@@js.call "append"] val append: t -> (t list [@js.variadic]) -> unit [@@js.call "append"] val prepend: t -> (t list [@js.variadic]) -> unit [@@js.call] val after: t -> t -> unit [@@js.call] val before: t -> t -> unit [@@js.call] val get_val: t -> string [@@js.call "val"] val hide: t -> unit [@@js.call] val show: t -> unit [@@js.call] val detach: t -> unit [@@js.call] val remove: t -> unit [@@js.call] val empty: t -> unit [@@js.call] val focus: t -> unit val height: t -> int [@@js.call] val set_height: t -> ([`String of string | `Int of int] [@js.union]) -> unit [@@js.call "height"] val width: t -> int [@@js.call] val set_width: t -> ([`String of string | `Int of int] [@js.union]) -> unit [@@js.call "width"] val string_value: t -> string [@@js.call "val"] val set_string_value: t -> string -> unit [@@js.call "val"] val add_class: t -> string -> unit [@@js.call] val remove_class: t -> string -> unit [@@js.call] val css: t -> string -> Ojs.t [@@js.call] val set_css_value: t -> string -> ([`String of string | `Float of float] [@js.union]) -> unit [@@js.call "css"] val set_css: t -> Ojs.t -> unit [@@js.call "css"] val clone: t -> t [@@js.call] val html: t -> string [@@js.call "html"] val set_html: t -> string -> unit [@@js.call "html"] (** {2 Properties} *) val prop: t -> string -> Ojs.t [@@js.call] val set_prop: t -> string -> ([`String of string | `Int of int | `Bool of bool | `Any of Ojs.t] [@js.union]) -> unit [@@js.call "prop"] (** {2 Data} *) val data: t -> string -> Ojs.t [@@js.call] val set_data: t -> string -> Ojs.t -> unit [@@js.call "data"] (** {2 Attributes} *) val attr: t -> string -> string option [@@js.call] val set_attr: t -> string -> string -> unit [@@js.call "attr"] val remove_attr: t -> string -> unit [@@js.call] (** {2 Animations} *) val fade_in: t -> ?duration:int -> ?finished:(unit -> unit) -> unit -> unit [@@js.call] val fade_out: t -> ?duration:int -> ?finished:(unit -> unit) -> unit -> unit [@@js.call] (** {2 Events} *) module Event : sig type t val page_x: t -> float val page_y: t -> float val type_: t -> string val target: t -> Ojs.t val which: t -> int val stop_propagation: t -> unit [@@js.call] val prevent_default: t -> unit [@@js.call] end val on: t -> string -> (Event.t -> unit) -> unit val off: t -> string -> unit val trigger: t -> string -> unit [@@js.call] val ready: (unit -> unit) -> unit [@@js.global "jQuery"] module Dialog: sig type button val button: text:string -> click:(unit -> unit) -> unit -> button [@@js.builder] type settings val settings: ?modal:bool -> ?title:string -> ?buttons:button list -> unit -> settings [@@js.builder] end module UI : sig module Datepicker : sig type settings val settings: ?date_format:string -> unit -> settings [@@js.builder] end val datepicker: t -> Datepicker.settings -> unit end val dialog: t -> ([`Dialog of Dialog.settings | `String of string] [@js.union]) -> unit (** {2 AJAX} *) module Ajax: sig type settings (** The type describing all settings of an AJAX call. *) type t (** Corresponds to jQuery's jqXHR object. *) val settings: ?async:bool -> ?cache:bool -> ?complete:(t -> string -> unit) -> ?error:(t -> string -> string -> unit) -> ?success:(Ojs.t -> string -> t -> unit) -> ?data:Ojs.t -> ?data_type:string -> ?meth:([`GET | `POST | `PUT] [@js "method"] [@js.enum]) -> ?content_type:string -> ?url:string -> unit -> settings [@@js.builder] val run: settings -> unit [@@js.global "jQuery.ajax"] val response_text: t -> string val status: t -> int end ================================================ FILE: examples/misc/js_date.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** JS dates *) (** {2 Type definitions} *) type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val now: unit -> t [@@js.new "Date"] val from_milliseconds: float -> t [@@js.new "Date"] val from_string: string -> t [@@js.new "Date"] val create: year:int -> month:int -> ?day:(int [@js.default 1]) -> ?hours:(int [@js.default 0]) -> ?minutes:(int [@js.default 0]) -> ?seconds:(int [@js.default 0]) -> ?ms:(int [@js.default 0]) -> unit -> t [@@js.new "Date"] val get_UTC_date: t -> int [@@js.call] val get_UTC_day: t -> int [@@js.call] val get_UTC_full_year: t -> int [@@js.call] val get_UTC_hours: t -> int [@@js.call] val get_UTC_milliseconds: t -> int [@@js.call] val get_UTC_minutes: t -> int [@@js.call] val get_UTC_month: t -> int [@@js.call] val get_UTC_seconds: t -> int [@@js.call] val set_UTC_date: t -> int -> unit [@@js.call] val set_UTC_full_year: t -> int -> unit [@@js.call] val set_UTC_hours: t -> int -> unit [@@js.call] val set_UTC_milliseconds: t -> int -> unit [@@js.call] val set_UTC_minutes: t -> int -> unit [@@js.call] val set_UTC_month: t -> int -> unit [@@js.call] val set_UTC_seconds: t -> int -> unit [@@js.call] val get_date: t -> int [@@js.call] val get_day: t -> int [@@js.call] val get_full_year: t -> int [@@js.call] val get_hours: t -> int [@@js.call] val get_milliseconds: t -> int [@@js.call] val get_minutes: t -> int [@@js.call] val get_month: t -> int [@@js.call] val get_seconds: t -> int [@@js.call] val set_date: t -> int -> unit [@@js.call] val set_full_year: t -> int -> unit [@@js.call] val set_hours: t -> int -> unit [@@js.call] val set_milliseconds: t -> int -> unit [@@js.call] val set_minutes: t -> int -> unit [@@js.call] val set_month: t -> int -> unit [@@js.call] val set_seconds: t -> int -> unit [@@js.call] val get_time: t -> float [@@js.call] val set_time: t -> float -> unit [@@js.call] val get_timezone_offset: t -> int [@@js.call] val to_locale_date_string: t -> string [@@js.call] val to_locale_string: t -> string [@@js.call] val to_locale_time_string: t -> string [@@js.call] val to_date_string: t -> string [@@js.call] val to_time_string: t -> string [@@js.call] val to_UTC_string: t -> string [@@js.call] val to_string: t -> string [@@js.call] ================================================ FILE: examples/misc/js_str.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** JS string and regexp objects *) (** {2 Type definitions} *) type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t type regexp = private Ojs.t val regexp_of_js: Ojs.t -> regexp val regexp_to_js: regexp -> Ojs.t (** {2 Conversion between JS strings and OCaml string} *) val to_string: t -> string [@@js.cast] val of_string: string -> t [@@js.cast] (** {2 JS strings} *) val from_char_code: (int list [@js.variadic]) -> t [@@js.global "String.fromCharCode"] val char_at: t -> int -> t val char_code_at: t -> int -> int val concat: t -> (t list [@js.variadic]) -> t val index_of: t -> t -> ?start:int -> unit -> int val last_index_of: t -> t -> ?start:int -> unit -> int val length: t -> int val locale_compare: t -> t -> int val match_: t -> regexp -> t array option val replace: t -> regexp -> t -> t val search: t -> regexp -> int val slice: t -> start:int -> ?end_:int -> unit -> t val split: t -> ?separator:t -> ?limit:int -> unit -> t array val substr: t -> start:int -> ?length:int -> unit -> t val substring: t -> start:int -> ?end_:int -> unit -> t val to_locale_lower_case: t -> t [@@js.call] val to_locale_upper_case: t -> t [@@js.call] val to_lower_case: t -> t [@@js.call] val to_upper_case: t -> t [@@js.call] val trim: t -> t [@@js.call] (** {2 Regexps} *) val regexp: t -> ?global:unit -> ?ignore_case:unit -> ?multiline:unit -> unit -> regexp [@@js.custom val regexp_internal: t -> ?flags:t -> unit -> regexp [@@js.new "RegExp"] let regexp txt ?global ?ignore_case ?multiline () = let l = [] in let l = match global with Some () -> of_string "g" :: l | None -> l in let l = match ignore_case with Some () -> of_string "i" :: l | None -> l in let l = match multiline with Some () -> of_string "m" :: l | None -> l in regexp_internal txt ~flags:(concat (of_string "") l) () ] val global: regexp -> bool val ignore_case: regexp -> bool val multiline: regexp -> bool val source: regexp -> string val last_index: regexp -> int val exec: regexp -> t -> t array option val test: regexp -> t -> bool ================================================ FILE: examples/misc/test_jquery.html ================================================ One Two
Blabla
================================================ FILE: examples/misc/test_jquery.ml ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** A toy application built with jQuery *) open Jquery include [%js: val alert: string -> unit [@@js.global] ] let ( !! ) = Jquery.selector let block s ?text ?(classes = []) ?(ons = []) ?(css = []) ?(props = []) children = let element = Jquery.selector (Printf.sprintf "<%s>" s) in begin match text with | None -> () | Some text -> Jquery.set_text element text end; List.iter (fun c -> Jquery.add_class element c) classes; List.iter (fun (key, value) -> Jquery.set_css_value element key value) css; List.iter (fun (key, value) -> Jquery.set_prop element key value) props; List.iter (fun (event, f) -> Jquery.on element event f) ons; begin match children with | [] -> () | _ :: _-> Jquery.append element children end; element let ajax_test () = let open Ajax in let complete h = function | "success" -> let pre = block "pre" ~text:(response_text h) [] in hide pre; append !!"body" [pre]; fade_in pre ~duration:2000 ~finished:(fun () -> fade_out pre ~finished:(fun () -> detach pre) () ) () | status -> alert (Printf.sprintf "status = %s" status) in run (settings ~meth:`GET ~url:"test_jquery.ml" ~data_type:"text" ~complete ()) let on_ready () = let main = !!"#main" in print_endline (text main); set_text main "Hello world!"; append_html main "in bold"; let elts = !!".tofill" in update_text elts (Printf.sprintf "[%i:%s]"); append main [elts; !! "XXX"]; let on_click evt = let open Event in append_html main (Printf.sprintf "
x=%f,y=%f,type=%s" (page_x evt) (page_y evt) (type_ evt) ) in on main "click" on_click; let div = block "div" [] in let input = block "input" [] in on input "input" (fun _ -> set_text div (get_val input)); append main [input; div]; let btn = block "button" ~text:"SHOW SOURCE CODE" [] ~ons:["click", (fun _ -> ajax_test ())] in append main [btn] let () = ready on_ready ================================================ FILE: examples/test/dune ================================================ (executables (names main) (libraries ojs) (preprocess (pps gen_js_api.ppx)) (modes js)) (rule (targets test_bindings.ml) (deps test_bindings.mli) (action (run gen_js_api %{deps}))) (rule (targets main.js) (deps main.bc.js) (action (run cp %{deps} %{targets}))) (alias (name DEFAULT) (deps main.js main.html)) ================================================ FILE: examples/test/main.html ================================================
Blabla
Bla ================================================ FILE: examples/test/main.ml ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** Some ad hoc code to illustrate and test various aspects of gen_js_api *) [@@@ocaml.warning "-32-34"] open Test_bindings [@@@ocaml.warning "-22"] include [%js: val wrapper: (int -> int -> int) -> (int -> int -> int [@js.dummy]) [@@js.global "wrapper"] val caller: (unit -> int) -> int [@@js.global "caller"] val caller_unit: (unit -> unit) -> unit [@@js.global "caller"] val test_variadic: ((int list [@js.variadic]) -> int) -> unit val test_variadic2: (string -> (int list [@js.variadic]) -> int) -> unit ] module LocalBindings = [%js: type myType = { x : a; y : b [@js "Y"]} and a = int option and b = { s : string; i : int } ] let () = let s = [%js.of: int list] [10; 20; 30] in Printf.printf "%i\n%!" ([%js.to: int] (Ojs.array_get s 0)); Printf.printf "%i\n%!" ([%js.to: int] (Ojs.array_get s 1)); Printf.printf "%i\n%!" ([%js.to: int] (Ojs.array_get s 2)) let () = let sum xs = List.fold_left ( + ) 0 xs in test_variadic sum; test_variadic2 (fun msg xs -> Printf.printf "%s\n%!" msg; sum xs) include [%js: val myArray: int array [@@js] val myArray2: Ojs.t [@@js.global "myArray"] val alert_bool: bool -> unit [@@js.global "alert"] val alert_float: float -> unit [@@js.global "alert"] val test_opt_args: (?foo:int -> ?bar:int -> unit-> string) -> unit [@@js.global] ] let doc = Window.document window let elt name ?(attrs = []) ?onclick subs = let e = Document.createElement doc name in List.iter (fun (k, v) -> Element.setAttribute e k v) attrs; List.iter (Element.appendChild e) subs; begin match onclick with | Some f -> Element.set_onclick e f | None -> () end; e let txt = Document.createTextNode doc let button ?attrs s onclick = elt "button" ?attrs ~onclick [ txt s ] let div = elt "div" let () = Array.iter (Printf.printf "[%i]\n") myArray; Ojs.array_set myArray2 0 (Ojs.int_to_js 10); Ojs.array_set myArray2 1 (Ojs.array_to_js Ojs.int_to_js [| 100; 200; 300 |]); (* Ojs.array_set myArray2 1 ([%to_js: int array] [| 100; 200; 300 |]); *) (* Printf.printf "%0.2f\n" 3.1415; *) (* Document.set_title doc "MyTitle"; Document.set_title doc (Document.title doc ^ " :-)"); *) (* let main = Document.getElementById doc "main" in *) (* print_endline (Element.innerHTML main); *) (* alert (Element.innerHTML main); *) (* Element.set_innerHTML main "Blablabla"; *) let draw () = let canvas_elt = Document.getElementById doc "canvas" in let canvas = Canvas.of_element canvas_elt in let ctx = Canvas.getContext_2d canvas in Canvas.RenderingContext2D.(begin set_fillStyle ctx "rgba(0,0,255,0.1)"; fillRect ctx 30 30 50 50 end); Element.set_onclick canvas_elt (fun () -> alert "XXX"); in alert_bool true; alert_float 3.1415; let f = wrapper (fun x y -> Printf.printf "IN CALLBACK, x = %i, y = %i\n%!" x y; x + y ) in Printf.printf "Result -> %i\n%!" (f 42 1); let uid = ref 0 in let f () = incr uid; Printf.printf "uid = %i\n%!" !uid; !uid in Printf.printf "Caller result -> %i, %i, %i\n%!" (caller f) (caller f) (caller f); caller_unit (fun () -> ignore (f ())); caller_unit (fun () -> ignore (f ())); caller_unit (fun () -> ignore (f ())); let alice = Person.create "Alice" Person.Foo.Foo in let bob = Person.create "Bob" Person.Foo.Bar in let charlie = Person.create "Charlie" (Person.Foo.OtherString "bla") in let eve = Person.create "Eve" (Person.Foo.OtherInt 2713) in Ojs.iter_properties (Person.cast alice) (Format.printf "%s\n%!"); let alice_obj = PersonObj.create "Alice" Person.Foo.Foo in let bob_obj = PersonObj.of_person bob in let dave_obj = new PersonObj.person "Dave" Person.Foo.Bar [1; 2; 3] in let string_of_foo = function | Person.Foo.Foo -> "foo" | Person.Foo.Bar -> "bar" | Person.Foo.OtherInt n -> Printf.sprintf "other = %d" n | Person.Foo.OtherString s -> Printf.sprintf "other = %s" s in let string_of_name_foo name foo = Printf.sprintf "%s <%s>" name (string_of_foo foo) in let string_of_person x = string_of_name_foo (Person.name x) (Person.foo x) in let string_of_person_obj x = string_of_name_foo (x # name) (x # foo) in let hack_person x = let name, foo = Person.get x () in Printf.printf "before: %s <%s>\n" name (string_of_foo foo); Person.set x ("Dave", Person.Foo.OtherString "bar"); let name, foo = Person.get x () in Printf.printf "after: %s <%s>\n" name (string_of_foo foo); in let body = Document.body doc in setTimeout (fun () -> Element.setAttribute body "bgcolor" "red") 2000; Element.appendChild body (Document.createTextNode doc "ABC"); Element.appendChild body (div ~attrs:["style", "color: blue"] [ txt "!!!!"; elt "b" [txt "XXX"]]); Element.appendChild body (div (List.map (fun x -> txt (string_of_person x)) [alice; bob; charlie; eve])); hack_person eve; Element.appendChild body (div (List.map (fun x -> txt (string_of_person x)) [alice; bob; charlie; eve])); Element.appendChild body (div (List.map (fun x -> txt (string_of_person_obj x)) [alice_obj; bob_obj; dave_obj])); let s = (new Str.str "") # concat [Str.create "Hello"; Str.create ", "; Str.create "world"; Str.create "!"] in Console.log_string console (s # to_string); Console.log_string console (Date.to_string (Date.create ~year:2015 ~month:4 ~day:10 ())); let l = Document.getElementsByClassName doc "myClass" in Array.iter (fun e -> Printf.printf "- [%s]\n" (Element.innerHTML e); (* OK *) print_string (Printf.sprintf "+ [%s]\n" (Element.innerHTML e)); (* BAD *) Element.appendChild e (button "Click!" draw); Element.appendChild e (button "XXX" (fun () -> ())); ) l; test_opt_args (fun ?(foo = 0) ?(bar = 0) () -> string_of_int foo ^ "/" ^ string_of_int bar); print_endline Person2.(to_json (mk ~children:[mk ~age:6 "Johnny"] ~age:42 "John Doe")) (* Custom mapping between association lists and JS objects *) module Dict : sig type 'a t = (string * 'a) list val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t end = struct type 'a t = (string * 'a) list let t_to_js ml2js l = let o = Ojs.empty_obj () in List.iter (fun (k, v) -> Ojs.set_prop o (Ojs.string_to_js k) (ml2js v)) l; o let t_of_js js2ml o = let l = ref [] in Ojs.iter_properties o (fun k -> l := (k, js2ml (Ojs.get_prop o (Ojs.string_to_js k))) :: !l); !l end include [%js: val int_dict_to_json_string: int Dict.t -> string [@@js.global "JSON.stringify"] val myDict: string Dict.t [@@js.global "myDict"] val set_x: int -> unit [@@js.set "x"] val get_x: unit -> int [@@js.get "x"] ] let () = print_endline (int_dict_to_json_string ["hello", 1; "world", 2]); List.iter (fun (k, v) -> Printf.printf "%s -> %s\n%!" k v) myDict; set_x 42; print_endline (string_of_int (get_x ())) module Sum = struct include [%js: type t = | A | B of int | C of int * string | D of {age:int; name:string} [@@js.sum] val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t ] let print = function | A -> print_endline "A" | B n -> print_endline (Format.sprintf "B %d" n) | C (n, s) -> print_endline (Format.sprintf "C (%d, %S)" n s) | D {age; name} -> print_endline (Format.sprintf "D {age = %d; name = %S}" age name) include [%js: val set_print_sum: (t -> unit) -> unit [@@js.set "print_sum"] val test_sum: unit -> unit [@@js.global "test_sum"] ] let () = set_print_sum print let () = test_sum () let () = Console.log console ([%js.of:t] A); Console.log console ([%js.of:t] (B 42)); Console.log console ([%js.of:t] (C (42, "foo"))); Console.log console ([%js.of:t] (D {age=42; name="foo"})) let () = Console3.log 1; Console3.log2 1 "two"; Console3.log3 1 "two" []; Console3.log4 1 "two" [] [|4|] let () = Console4.log (module Ojs.Int) 1; Console4.log2 (module Ojs.Int) (module Ojs.String) 1 "two"; Console4.log3 (module Ojs.Int) (module Ojs.String) (module Ojs.List(Ojs.Int)) 1 "two" [3] end include [%js: val test_flatten: ([`A | `B of int | `C of string | `D of int * string] [@js.enum]) -> unit [@@js.global "test_flatten"] ] let () = test_flatten `A; test_flatten (`B 42); test_flatten (`C "hello"); test_flatten (`D (42, "foo")) include [%js: val make_string : 'a -> string [@@js.global "String"] ] let () = Console3.log (make_string 1234); Console3.log (make_string "string"); Console3.log (make_string ["list"]); Console3.log (make_string [|"array"|]) include [%js: val test_typvars: 'a -> 'a * 'a [@@js.global "test_typvars"] ] let () = Console3.log (test_typvars `A); Console3.log (test_typvars 1234); Console3.log (test_typvars "string"); Console3.log (test_typvars ["list"]) let () = let t = Ref.make "foo" in Console3.log (Ref.current t); Ref.setCurrent t "bar"; Console3.log (Ref.current t) let () = let foo = Either.left "foo" in let foobar = Either.right ["foo"; "bar"] in let f x = Either.destruct x ~left:(fun s -> s) ~right:(String.concat "-") in Console3.log (Ojs.string_to_js (f foo)); Console3.log (Ojs.string_to_js (f foobar)) let () = let open Variants.M3 in let rec of_list = function | [] -> Empty | hd :: tl -> Cons (hd, of_list tl) in Console3.log ([%js.of: int t] (of_list [1;2;3])) ================================================ FILE: examples/test/test_bindings.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** Some ad hoc code to illustrate and test various aspects of gen_js_api *) [@@@js.implem [@@@ocaml.warning "-22"]] module Element : sig type t = private Ojs.t val appendChild: t -> t -> unit val set_innerHTML: t -> string -> unit val innerHTML: t -> string val set_onclick: t -> (unit -> unit) -> unit val setAttribute: t -> string -> string -> unit end module Canvas : sig module RenderingContext2D : sig type t = private Ojs.t val set_fillStyle: t -> string -> unit val fillRect: t -> int -> int -> int -> int -> unit end type t = private Ojs.t val of_element: Element.t -> t [@@js.cast] val getContext_2d: t -> RenderingContext2D.t [@@js.custom val get_context: t -> string -> Ojs.t [@@js.call] let getContext_2d x = get_context x "2d" ] end module Document : sig type t = private Ojs.t val set_title: t -> string -> unit val title: t -> string val getElementById: t -> string -> Element.t val getElementsByClassName: t -> string -> Element.t array val createElement: t -> string -> Element.t val createTextNode: t -> string -> Element.t val body: t -> Element.t end module Window : sig type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val document: t -> Document.t val set_onload: t -> (unit -> unit) -> unit end val window: Window.t val alert: string -> unit [@@js.global] val setTimeout: (unit -> unit) -> int -> unit module Console: sig type t = private Ojs.t val log: t -> Ojs.t -> unit val log_string: t -> string -> unit [@@js.call "log"] end val console: Console.t module Person: sig module Foo: sig type t = | Foo | Bar [@js 42] | OtherInt of int [@js.default] | OtherString of string [@js.default] [@@js.enum] end type t = private Ojs.t val create: string -> Foo.t -> t [@@js.new "Person"] val name: t -> string val foo: t -> Foo.t val get: t -> unit -> string * Foo.t [@@js.call] val set: t -> string * Foo.t -> unit [@@js.call] val cast: t -> Ojs.t [@@js.cast] end module PersonObj: sig class t: Ojs.t -> object inherit Ojs.obj method name: string method set_name: string -> unit method foo: Person.Foo.t method set_foo: Person.Foo.t -> unit method get: string * Person.Foo.t [@@js.call] method set: string * Person.Foo.t -> unit [@@js.call] end class person: string -> Person.Foo.t -> (int list [@js.variadic]) -> t val create: string -> Person.Foo.t -> t [@@js.new "Person"] val of_person: Person.t -> t [@@js.cast] end module Str: sig class t: Ojs.t -> object inherit Ojs.obj method concat: (t list [@js.variadic]) -> t method to_string: string [@@js.call] end class str: string -> t [@@js.new "String"] val create: string -> t [@@js.new "String"] end module Date: sig type t = private Ojs.t val create: year:int -> month:int -> ?day:(int[@js.default 0]) -> unit -> t [@@js.new "Date"] val to_string: t -> string [@@js.call] end module Person2: sig type t = private Ojs.t val mk: ?children:t list -> age:int -> (string[@js "name"]) -> t [@@js.builder] val to_json: t -> string [@@js.global "JSON.stringify"] end type int_or_string_or_null = | Int of int | String of string | Nothing [@@js.union] val f: ([`Int of int | `String of string | `Nothing] [@js.union]) -> unit val g: int_or_string_or_null -> unit [@@js.global] module Verb1: sig type t1 = { x_coord: int; y_coord: int; } class t2: Ojs.t -> object inherit Ojs.obj method x_coord: int method y_coord: int end end [@js.verbatim_names] module Verb2: sig type t1 = { x_coord: int; y_coord: int; } [@@js.verbatim_names] class t2: Ojs.t -> object inherit Ojs.obj method x_coord: int method y_coord: int end [@@js.verbatim_names] end module Console2: sig val log: string -> unit [@@js.global] end [@js.scope "console"] module Console3: sig val log: 'a -> unit [@@js.global "console.log"] val log2: 'a -> 'b -> unit [@@js.global "console.log"] val log3: 'a -> 'b -> 'c -> unit [@@js.global "console.log"] val log4: 'a -> 'b -> 'c -> 'd -> unit [@@js.global "console.log"] end module Console4: sig val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global "console.log"] val log2: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> 'a -> 'b -> unit [@@js.global "console.log"] val log3: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> (module[@js] Ojs.T with type t = 'c) -> 'a -> 'b -> 'c -> unit [@@js.global "console.log"] end module Location: sig val hash: unit -> string val set_hash: string -> unit end [@js.scope "location"] module Location2: sig val hash: unit -> string [@@js.get] val set_hash: string -> unit [@@js.set] end [@js.scope "location"] module Location3: sig val assign: string -> unit val reload: ?force:bool -> unit -> unit val replace: string -> unit end [@js.scope "location"] module Union: sig type close_path type moveto_abs type svg_path_seg = | Unknown of Ojs.t [@js.default] | Close_path of close_path [@js 1] | Moveto_abs of moveto_abs [@js 2] [@@js.union on_field "pathSegType"] end module Ref : sig type 'value t = private Ojs.t val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t val make: 'value -> 'value t [@@js.global "makeRef"] val current : 'value t -> 'value [@@js.get "current"] val setCurrent : 'value t -> 'value -> unit [@@js.set "current"] end module Either : sig type ('a, 'b) t val t_to_js: ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) t val left: 'a -> ('a, 'b) t [@@js.global "eitherLeft"] val right: 'b -> ('a, 'b) t [@@js.global "eitherRight"] val destruct: ('a, 'b) t -> left:('a -> 'c) -> right:('b -> 'c) -> 'c [@@js.global "eitherDestruct"] end module Alias : sig module Swap : sig type ('a, 'b) t = ('b, 'a) Either.t val t_to_js: ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) t end (* Error: Contravariant type parameter ! module E : sig type 'a t = 'a -> int end *) module Id : sig type 'a t = 'a val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t end module Arrow : sig type 'a t = ('a -> int) -> string val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t end module Record : sig type ('a, 'b) t = { x: 'a; y: 'b } val t_to_js: ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) t end end module Variants : sig module M1 : sig type 'a t = | X of 'a | Y of int [@@js.sum] end module M2 : sig type ('a, 'b) t = | X of 'a | Y of 'b [@@js.sum] end module M3 : sig type 'a t = | Empty | Cons of 'a * 'a t [@@js.sum] val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t end (* Error: Contravariant type parameter ! module E : sig type 'a t = | F of ('a -> int) [@@js.sum] end *) module M4 : sig type 'a t = | F of (('a -> int) -> int) [@@js.sum] end end ================================================ FILE: gen_js_api.opam ================================================ # This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.1.7" synopsis: "Easy OCaml bindings for JavaScript libraries" description: """ gen_js_api aims at simplifying the creation of OCaml bindings for JavaScript libraries. Authors of bindings write OCaml signatures for JavaScript libraries and the tool generates the actual binding code with a combination of implicit conventions and explicit annotations. gen_js_api is to be used with the js_of_ocaml compiler. """ maintainer: ["Alain Frisch "] authors: [ "Alain Frisch " "Sebastien Briais " ] license: "MIT" homepage: "https://github.com/LexiFi/gen_js_api" bug-reports: "https://github.com/LexiFi/gen_js_api/issues" depends: [ "dune" {>= "3.17"} "ocaml" {>= "4.13"} "ppxlib" {>= "0.37"} "js_of_ocaml-compiler" {with-test} "ojs" {= version} "odoc" {with-doc} ] conflicts: [ "js_of_ocaml-compiler" {< "6.3.0"} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/LexiFi/gen_js_api.git" ================================================ FILE: lib/dune ================================================ (library (public_name ojs) (synopsis "Runtime support for gen_js_api") (libraries js_of_ocaml-compiler.runtime) (wrapped false) (foreign_stubs (language c) (names ojs_runtime_stubs)) (modes byte) (js_of_ocaml (javascript_files ojs_runtime.js))) ================================================ FILE: lib/ojs.ml ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (* This module (mostly) abstracts away from js_of_ocaml encoding of OCaml values. It serves as a support library for the code generated by gen_js_api. The module could mostly be implemented on top of js_of_ocaml's Js module (and in particular Js.Unsafe), but we prefer to drop the dependency to js_of_ocaml's library and to rely only on its compiler and JS runtime code. *) type t external t_of_js: t -> t = "%identity" external t_to_js: t -> t = "%identity" external string_of_js: t -> string = "caml_js_to_string" external string_to_js: string -> t = "caml_js_from_string" external int_of_js: t -> int = "%identity" external int_to_js: int -> t = "%identity" external bool_of_js: t -> bool = "caml_js_to_bool" external bool_to_js: bool -> t = "caml_js_from_bool" external float_of_js: t -> float = "caml_js_to_float" external float_to_js: float -> t = "caml_js_from_float" external obj: (string * t) array -> t = "caml_js_object" external variable: string -> t = "caml_js_var" external get: t -> string -> t = "caml_js_get" external set: t -> string -> t -> unit = "caml_js_set" external delete: t -> string -> unit = "caml_js_delete" external get_prop: t -> t -> t = "caml_js_get" external set_prop: t -> t -> t -> unit = "caml_js_set" external delete_prop: t -> t -> unit = "caml_js_delete" external get_prop_ascii: t -> string -> t = "caml_js_get" external set_prop_ascii: t -> string -> t -> unit = "caml_js_set" external delete_prop_ascii: t -> string -> unit = "caml_js_delete" external internal_type_of: t -> t = "caml_js_typeof" let type_of x = string_of_js (internal_type_of x) external internal_instance_of: t -> t -> t = "caml_js_instanceof" let instance_of x ~constr = bool_of_js (internal_instance_of x constr) external pure_js_expr: string -> t = "caml_pure_js_expr" let null = pure_js_expr "null" let undefined = pure_js_expr "undefined" external equals: t -> t -> bool = "caml_js_equals" let global = pure_js_expr "globalThis" external new_obj: t -> t array -> t = "caml_js_new" external call: t -> string -> t array -> t = "caml_js_meth_call" external apply: t -> t array -> t = "caml_js_fun_call" let array_make n = new_obj (get_prop_ascii global "Array") [|int_to_js n|] let array_get t i = get_prop t (int_to_js i) let array_set t i x = set_prop t (int_to_js i) x let array_of_js_from f objs start = let n = int_of_js (get_prop_ascii objs "length") in Array.init (n - start) (fun i -> f (array_get objs (start + i))) let array_of_js f objs = array_of_js_from f objs 0 let array_to_js f arr = let n = Array.length arr in let a = array_make n in for i = 0 to n - 1 do array_set a i (f arr.(i)) done; a let list_of_js_from f objs start = Array.to_list (array_of_js_from f objs start) let list_of_js f objs = list_of_js_from f objs 0 let list_to_js f l = array_to_js f (Array.of_list l) let option_of_js f x = if equals x null then None else Some (f x) let option_to_js f = function | Some x -> f x | None -> null let unit_to_js () = undefined let unit_of_js _ = () class obj (x:t) = object method to_js = x end external fun_to_js: int -> (t -> 'a) -> t = "caml_js_wrap_callback_strict" external fun_to_js_args: (t -> 'a) -> t = "caml_ojs_wrap_fun_arguments" let has_property o x = type_of o = "object" && o != null && get_prop o (string_to_js x) != undefined external new_obj_arr: t -> t -> t = "caml_ojs_new_arr" let empty_obj () = new_obj (get_prop_ascii global "Object") [||] external iter_properties_untyped : t -> t -> unit = "caml_ojs_iterate_properties" let iter_properties x f = iter_properties_untyped x (fun_to_js 1 (fun x -> f (string_of_js x))) let apply_arr o arr = call o "apply" [| null; arr |] let call_arr o s arr = call (get_prop o (string_to_js s)) "apply" [| o; arr |] let is_null x = equals x null let obj_type x = string_of_js (call (pure_js_expr "Object.prototype.toString") "call" [|x|]) module type T = sig type js := t type t val t_to_js : t -> js val t_of_js : js -> t end (* Ojs.T instances for built-in types *) module Int = struct type t = int let t_to_js = int_to_js let t_of_js = int_of_js end module String = struct type t = string let t_to_js = string_to_js let t_of_js = string_of_js end module Bool = struct type t = bool let t_to_js = bool_to_js let t_of_js = bool_of_js end module Float = struct type t = float let t_to_js = float_to_js let t_of_js = float_of_js end module Array (A: T) = struct type t = A.t array let t_to_js = array_to_js A.t_to_js let t_of_js = array_of_js A.t_of_js end module List (A: T) = struct type t = A.t list let t_to_js = list_to_js A.t_to_js let t_of_js = list_of_js A.t_of_js end module Option (A: T) = struct type t = A.t option let t_to_js = option_to_js A.t_to_js let t_of_js = option_of_js A.t_of_js end ================================================ FILE: lib/ojs.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** Binding with JS values. *) type t (** The universal type representing arbitrary JS values. *) (** {2 Mapper for built-in types} *) external t_of_js: t -> t = "%identity" external t_to_js: t -> t = "%identity" external string_of_js: t -> string = "caml_js_to_string" external string_to_js: string -> t = "caml_js_from_string" external int_of_js: t -> int = "%identity" external int_to_js: int -> t = "%identity" external bool_of_js: t -> bool = "caml_js_to_bool" external bool_to_js: bool -> t = "caml_js_from_bool" external float_of_js: t -> float = "caml_js_to_float" external float_to_js: float -> t = "caml_js_from_float" val array_of_js: (t -> 'a) -> t -> 'a array val array_to_js: ('a -> t) -> 'a array -> t val list_of_js: (t -> 'a) -> t -> 'a list val list_to_js: ('a -> t) -> 'a list -> t val array_of_js_from: (t -> 'a) -> t -> int -> 'a array val list_of_js_from: (t -> 'a) -> t -> int -> 'a list val option_of_js: (t -> 'a) -> t -> 'a option (** Both [null] and [undefined] are mapped to [None]. *) val option_to_js: ('a -> t) -> 'a option -> t (** [None] is mapped to [null]. *) val unit_of_js: t -> unit val unit_to_js: unit -> t (** {2 Wrap OCaml functions as JS functions} *) external fun_to_js: int -> (t -> 'a) -> t = "caml_js_wrap_callback_strict" (** Wrap an OCaml function of known arity (>=1) into a JS function. Extra arguments are discarded and missing argument are filled with 'undefined'. *) external fun_to_js_args: (t -> 'a) -> t = "caml_ojs_wrap_fun_arguments" (** Wrap an OCaml function taking JS arguments as a JS array. *) (** {2 JS objects} *) external get: t -> string -> t = "caml_js_get" [@@ocaml.deprecated "Use Ojs.get_prop_ascii instead."] external set: t -> string -> t -> unit = "caml_js_set" [@@ocaml.deprecated "Use Ojs.set_prop_ascii instead."] external delete: t -> string -> unit = "caml_js_delete" [@@ocaml.deprecated "Use Ojs.delete_prop_ascii instead."] external get_prop_ascii: t -> string -> t = "caml_js_get" (** Get the property from an object (only works if the property key is a plain ascii string). *) external set_prop_ascii: t -> string -> t -> unit = "caml_js_set" (** Set an object property (only works if the property key is a plain ascii string). *) external delete_prop_ascii: t -> string -> unit = "caml_js_delete" (** Delete an object property (only works if the property key is a plain ascii string). *) external get_prop: t -> t -> t = "caml_js_get" (** Get the property from an object. *) external set_prop: t -> t -> t -> unit = "caml_js_set" (** Set an object property. *) external delete_prop: t -> t -> unit = "caml_js_delete" (** Delete an object property. *) external obj: (string * t) array -> t = "caml_js_object" val empty_obj: unit -> t val has_property: t -> string -> bool val iter_properties: t -> (string -> unit) -> unit (** {2 Calling JS functions} *) external call: t -> string -> t array -> t = "caml_js_meth_call" (** Call a method on an object (binding 'this' to the object). *) external apply: t -> t array -> t = "caml_js_fun_call" (** Call a function. *) external new_obj: t -> t array -> t = "caml_js_new" (** Call a constructor *) val call_arr: t -> string -> t -> t (** Variant of [Ojs.call] where the arguments are passed as an already built JS array. *) val apply_arr: t -> t -> t (** Variant of [Ojs.apply] where the arguments are passed as an already built JS array. *) external new_obj_arr: t -> t -> t = "caml_ojs_new_arr" (** Variant of [Ojs.new_obj] where the arguments are passed as an already built JS array. *) (** {2 Arrays} *) val array_make: int -> t val array_get: t -> int -> t val array_set: t -> int -> t -> unit (** {2 Misc} *) val global: t val null: t external variable: string -> t = "caml_js_var" val type_of: t -> string val instance_of: t -> constr:t -> bool class obj: t -> object method to_js: t end val is_null: t -> bool val obj_type: t -> string (** Returns: "[object Array]" "[object Object]" "[object Number]" "[object String]" "[object Null]" "[object Boolean]" *) module type T = sig type js := t type t val t_to_js : t -> js val t_of_js : js -> t end (* Ojs.T instances for built-in types *) module Int : T with type t = int module String : T with type t = string module Bool : T with type t = bool module Float : T with type t = float module Array (A: T) : T with type t = A.t array module List (A: T) : T with type t = A.t list module Option (A: T) : T with type t = A.t option ================================================ FILE: lib/ojs_exn.ml ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) type t = Jsoo_runtime.Error.t external coerce : t -> Ojs.t = "%identity" let name x = Ojs.string_of_js (Ojs.get_prop_ascii (coerce x) "name") let message x = Ojs.string_of_js (Ojs.get_prop_ascii (coerce x) "message") let stack x = Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop_ascii (coerce x) "stack") let to_string x = Ojs.string_of_js (Ojs.call (coerce x) "toString" [||]) exception Error = Jsoo_runtime.Error.Exn let () = Printexc.register_printer (function | Error x -> Some (to_string x) | _ -> None ) ================================================ FILE: lib/ojs_exn.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (** OCaml view on JS exceptions *) type t val name: t -> string val message: t -> string val stack: t -> string option val to_string: t -> string exception Error of t ================================================ FILE: lib/ojs_runtime.js ================================================ //Provides: caml_ojs_wrap_fun_arguments //Requires: caml_js_wrap_callback function caml_ojs_wrap_fun_arguments(f) { return function() { return caml_js_wrap_callback(f)(arguments); } } //Provides: caml_ojs_iterate_properties //Requires: caml_js_to_string function caml_ojs_iterate_properties(o, f) { var name; for(name in o) { if(o.hasOwnProperty(name)) { f(name); } } } ================================================ FILE: lib/ojs_runtime_stubs.c ================================================ #include #include void caml_ojs_wrap_fun_arguments () { fprintf(stderr, "Unimplemented JavaScript primitive caml_ojs_wrap_fun_arguments!\n"); exit(1); } void caml_ojs_iterate_properties () { fprintf(stderr, "Unimplemented JavaScript primitive caml_ojs_iterate_properties!\n"); exit(1); } ================================================ FILE: node-test/bindings/arrays.mli ================================================ module JsArray (E: Ojs.T): sig type t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val create: unit -> t [@@js.new "Array"] val push: t -> E.t -> unit [@@js.call] val pop: t -> E.t option [@@js.call] end module UntypedArray : sig include (module type of JsArray(Ojs)) end module StringArray : sig include (module type of JsArray(Ojs.String)) val join: t -> string -> string [@@js.call] end module[@js.scope "Array"] JsArray2: sig type 'a t val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t val create: unit -> 'a t [@@js.create] val create': (module[@js] Ojs.T with type t = 'a) -> ('a list [@js.variadic]) -> 'a t [@@js.create] val push: (module[@js] Ojs.T with type t = 'a) -> 'a t -> 'a -> unit [@@js.call] val pop: (module[@js] Ojs.T with type t = 'a) -> 'a t -> 'a option [@@js.call] val get: (module[@js] Ojs.T with type t = 'a) -> 'a t -> int -> 'a option [@@js.index_get] val set: (module[@js] Ojs.T with type t = 'a) -> 'a t -> int -> 'a -> unit [@@js.index_set] val join: string t -> string -> string [@@js.call] end ================================================ FILE: node-test/bindings/buffer.mli ================================================ [@@@js.scope "Buffer"] type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val alloc: int -> t[@@js.global] val from: string -> t[@@js.global] val concat: t list -> t[@@js.global] val length: t -> int [@@js.get] val get: t -> int -> int option [@@js.index_get] val set: t -> int -> int -> unit[@@js.index_set] val write: t -> string -> int[@@js.call] val slice: t -> int -> int -> t[@@js.call] val to_string: t -> string[@@js.call] val copy: t -> dst:t -> start:int -> dst_start:int -> dst_end:int -> int[@@js.call] ================================================ FILE: node-test/bindings/console.mli ================================================ [@@@js.scope "console"] val log: 'a -> unit [@@js.global] val error: 'a -> unit [@@js.global] module T : sig val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global] val error: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global] end ================================================ FILE: node-test/bindings/container.ml ================================================ module StringMap = struct include Map.Make(String) let t_to_js ml2js l = let o = Ojs.empty_obj () in iter (fun k v -> Ojs.set_prop o (Ojs.string_to_js k) (ml2js v)) l; o let t_of_js js2ml o = let l = ref empty in Ojs.iter_properties o (fun k -> l := add k (js2ml (Ojs.get_prop o (Ojs.string_to_js k))) !l); !l end ================================================ FILE: node-test/bindings/container.mli ================================================ module StringMap : sig include Map.S with type key = string val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t end ================================================ FILE: node-test/bindings/dune ================================================ (library (name node) (synopsis "Bindings") (libraries ojs) (preprocess (pps gen_js_api.ppx)) (modes byte) (js_of_ocaml (javascript_files imports.js)) (wasm_of_ocaml (javascript_files imports.js imports.wat))) (rule (targets imports.ml) (deps imports.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/imports.ml imports.ml))) (rule (targets errors.ml) (deps errors.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/errors.ml errors.ml))) (rule (targets global.ml) (deps global.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/global.ml global.ml))) (rule (targets promise.ml) (deps promise.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/promise.ml promise.ml))) (rule (targets buffer.ml) (deps buffer.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/buffer.ml buffer.ml))) (rule (targets fs.ml) (deps fs.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/fs.ml fs.ml))) (rule (targets path.ml) (deps path.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/path.ml path.ml))) (rule (targets process.ml) (deps process.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/process.ml process.ml))) (rule (targets console.ml) (deps console.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/console.ml console.ml))) (rule (targets arrays.ml) (deps arrays.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/arrays.ml arrays.ml))) (rule (targets number.ml) (deps number.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (action (diff expected/number.ml number.ml))) ================================================ FILE: node-test/bindings/errors.mli ================================================ module [@js.scope] Error : sig type t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val create: string -> t [@@js.create] val stack_trace_limit: int [@@js.global] val set_stack_trace_limit: int -> unit [@@js.set] val code: t -> string [@@js.get] val message: t -> string [@@js.get] val stack: t -> string [@@js.get] end ================================================ FILE: node-test/bindings/expected/arrays.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module JsArray(E:Ojs.T) = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let create : unit -> t = fun () -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||]) let push : t -> E.t -> unit = fun (x4 : t) (x3 : E.t) -> ignore (Ojs.call (t_to_js x4) "push" [|(E.t_to_js x3)|]) let pop : t -> E.t option = fun (x5 : t) -> Ojs.option_of_js E.t_of_js (Ojs.call (t_to_js x5) "pop" [||]) end module UntypedArray = struct include (JsArray)(Ojs) end module StringArray = struct include (JsArray)(Ojs.String) let join : t -> string -> string = fun (x8 : t) (x7 : string) -> Ojs.string_of_js (Ojs.call (t_to_js x8) "join" [|(Ojs.string_to_js x7)|]) end module JsArray2 = struct type 'a t = Ojs.t let rec t_of_js : 'a . (Ojs.t -> 'a) -> Ojs.t -> 'a t = fun (type __a) (__a_of_js : Ojs.t -> __a) -> fun (x10 : Ojs.t) -> x10 and t_to_js : 'a . ('a -> Ojs.t) -> 'a t -> Ojs.t = fun (type __a) (__a_to_js : __a -> Ojs.t) -> fun (x9 : Ojs.t) -> x9 let create : unit -> 'a t = fun () -> t_of_js Obj.magic (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||]) let create' : (module Ojs.T with type t = 'a) -> 'a list -> 'a t = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x12 : a list) -> t_of_js A.t_of_js (Ojs.new_obj_arr (Ojs.get_prop_ascii Ojs.global "Array") (let x13 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in List.iter (fun (x14 : a) -> ignore (Ojs.call x13 "push" [|(A.t_to_js x14)|])) x12; x13)) let push : (module Ojs.T with type t = 'a) -> 'a t -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x17 : a t) (x16 : a) -> ignore (Ojs.call (t_to_js A.t_to_js x17) "push" [|(A.t_to_js x16)|]) let pop : (module Ojs.T with type t = 'a) -> 'a t -> 'a option = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x19 : a t) -> Ojs.option_of_js A.t_of_js (Ojs.call (t_to_js A.t_to_js x19) "pop" [||]) let get : (module Ojs.T with type t = 'a) -> 'a t -> int -> 'a option = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x22 : a t) (x24 : int) -> Ojs.option_of_js A.t_of_js (Ojs.array_get (t_to_js A.t_to_js x22) x24) let set : (module Ojs.T with type t = 'a) -> 'a t -> int -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x26 : a t) (x28 : int) (x29 : a) -> Ojs.array_set (t_to_js A.t_to_js x26) x28 (A.t_to_js x29) let join : string t -> string -> string = fun (x31 : string t) (x30 : string) -> Ojs.string_of_js (Ojs.call (t_to_js Ojs.string_to_js x31) "join" [|(Ojs.string_to_js x30)|]) end ================================================ FILE: node-test/bindings/expected/buffer.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let alloc : int -> t = fun (x3 : int) -> t_of_js (Ojs.call (Ojs.get_prop_ascii Ojs.global "Buffer") "alloc" [|(Ojs.int_to_js x3)|]) let from : string -> t = fun (x4 : string) -> t_of_js (Ojs.call (Ojs.get_prop_ascii Ojs.global "Buffer") "from" [|(Ojs.string_to_js x4)|]) let concat : t list -> t = fun (x5 : t list) -> t_of_js (Ojs.call (Ojs.get_prop_ascii Ojs.global "Buffer") "concat" [|(Ojs.list_to_js t_to_js x5)|]) let length : t -> int = fun (x7 : t) -> Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x7) "length") let get : t -> int -> int option = fun (x8 : t) (x9 : int) -> Ojs.option_of_js Ojs.int_of_js (Ojs.array_get (t_to_js x8) x9) let set : t -> int -> int -> unit = fun (x11 : t) (x12 : int) (x13 : int) -> Ojs.array_set (t_to_js x11) x12 (Ojs.int_to_js x13) let write : t -> string -> int = fun (x15 : t) (x14 : string) -> Ojs.int_of_js (Ojs.call (t_to_js x15) "write" [|(Ojs.string_to_js x14)|]) let slice : t -> int -> int -> t = fun (x18 : t) (x16 : int) (x17 : int) -> t_of_js (Ojs.call (t_to_js x18) "slice" [|(Ojs.int_to_js x16);(Ojs.int_to_js x17)|]) let to_string : t -> string = fun (x19 : t) -> Ojs.string_of_js (Ojs.call (t_to_js x19) "toString" [||]) let copy : t -> dst:t -> start:int -> dst_start:int -> dst_end:int -> int = fun (x24 : t) ~dst:(x20 : t) ~start:(x21 : int) ~dst_start:(x22 : int) ~dst_end:(x23 : int) -> Ojs.int_of_js (Ojs.call (t_to_js x24) "copy" [|(t_to_js x20);(Ojs.int_to_js x21);(Ojs.int_to_js x22);(Ojs.int_to_js x23)|]) ================================================ FILE: node-test/bindings/expected/console.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] let log : 'a -> unit = fun (x1 : 'a) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log" [|(Obj.magic x1)|]) let error : 'a -> unit = fun (x2 : 'a) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "error" [|(Obj.magic x2)|]) module T = struct let log : (module Ojs.T with type t = 'a) -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x3 : a) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log" [|(A.t_to_js x3)|]) let error : (module Ojs.T with type t = 'a) -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x4 : a) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "error" [|(A.t_to_js x4)|]) end ================================================ FILE: node-test/bindings/expected/errors.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module Error = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let create : string -> t = fun (x3 : string) -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Error") [|(Ojs.string_to_js x3)|]) let stack_trace_limit : int = Ojs.int_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Error") "stackTraceLimit") let set_stack_trace_limit : int -> unit = fun (x4 : int) -> Ojs.set_prop_ascii (Ojs.get_prop_ascii Ojs.global "Error") "stackTraceLimit" (Ojs.int_to_js x4) let code : t -> string = fun (x5 : t) -> Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x5) "code") let message : t -> string = fun (x6 : t) -> Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x6) "message") let stack : t -> string = fun (x7 : t) -> Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x7) "stack") end ================================================ FILE: node-test/bindings/expected/fs.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module Dirent = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let name : t -> string = fun (x3 : t) -> Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x3) "name") let is_file : t -> bool = fun (x4 : t) -> Ojs.bool_of_js (Ojs.call (t_to_js x4) "isFile" [||]) let is_directory : t -> bool = fun (x5 : t) -> Ojs.bool_of_js (Ojs.call (t_to_js x5) "isDirectory" [||]) end module Dir = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x7 : Ojs.t) -> x7 and t_to_js : t -> Ojs.t = fun (x6 : Ojs.t) -> x6 let path : t -> string = fun (x8 : t) -> Ojs.string_of_js (Ojs.get_prop_ascii (t_to_js x8) "path") let close : t -> unit Promise.t = fun (x9 : t) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x9) "close" [||]) let read : t -> Dirent.t option Promise.t = fun (x11 : t) -> Promise.t_of_js (fun (x12 : Ojs.t) -> Ojs.option_of_js Dirent.t_of_js x12) (Ojs.call (t_to_js x11) "read" [||]) end module FileHandle = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x15 : Ojs.t) -> x15 and t_to_js : t -> Ojs.t = fun (x14 : Ojs.t) -> x14 type read = { bytes_read: int ; buffer: Buffer.t } let rec read_of_js : Ojs.t -> read = fun (x17 : Ojs.t) -> { bytes_read = (Ojs.int_of_js (Ojs.get_prop_ascii x17 "bytesRead")); buffer = (Buffer.t_of_js (Ojs.get_prop_ascii x17 "buffer")) } and read_to_js : read -> Ojs.t = fun (x16 : read) -> Ojs.obj [|("bytesRead", (Ojs.int_to_js x16.bytes_read));("buffer", (Buffer.t_to_js x16.buffer))|] let append_file : t -> Buffer.t -> unit Promise.t = fun (x19 : t) (x18 : Buffer.t) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x19) "appendFile" [|(Buffer.t_to_js x18)|]) let read : t -> Buffer.t -> int -> int -> int -> read Promise.t = fun (x25 : t) (x21 : Buffer.t) (x22 : int) (x23 : int) (x24 : int) -> Promise.t_of_js read_of_js (Ojs.call (t_to_js x25) "read" [|(Buffer.t_to_js x21);(Ojs.int_to_js x22);(Ojs.int_to_js x23);( Ojs.int_to_js x24)|]) let chmod : t -> int -> unit Promise.t = fun (x28 : t) (x27 : int) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x28) "chmod" [|(Ojs.int_to_js x27)|]) let chmown : t -> uid:int -> gid:int -> unit Promise.t = fun (x32 : t) ~uid:(x30 : int) ~gid:(x31 : int) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x32) "chmown" [|(Ojs.int_to_js x30);(Ojs.int_to_js x31)|]) let close : t -> unit Promise.t = fun (x34 : t) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x34) "close" [||]) let datasync : t -> unit Promise.t = fun (x36 : t) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (t_to_js x36) "datasync" [||]) let fd : t -> int = fun (x38 : t) -> Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x38) "fd") end let readdir : string -> string list Promise.t = fun (x39 : string) -> Promise.t_of_js (fun (x40 : Ojs.t) -> Ojs.list_of_js Ojs.string_of_js x40) (Ojs.call (Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs") "promises") "readdir" [|(Ojs.string_to_js x39)|]) let open_ : string -> flag:string -> FileHandle.t Promise.t = fun (x42 : string) ~flag:(x43 : string) -> Promise.t_of_js FileHandle.t_of_js (Ojs.call (Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs") "promises") "open" [|(Ojs.string_to_js x42);(Ojs.string_to_js x43)|]) let rmdir : string -> unit Promise.t = fun (x45 : string) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs") "promises") "rmdir" [|(Ojs.string_to_js x45)|]) let rename : string -> string -> unit Promise.t = fun (x47 : string) (x48 : string) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs") "promises") "rename" [|(Ojs.string_to_js x47);(Ojs.string_to_js x48)|]) let unlink : string -> unit Promise.t = fun (x50 : string) -> Promise.t_of_js Ojs.unit_of_js (Ojs.call (Ojs.get_prop_ascii (Jsoo_runtime.Js.runtime_value "node_fs") "promises") "unlink" [|(Ojs.string_to_js x50)|]) ================================================ FILE: node-test/bindings/expected/global.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type timeout_id = Ojs.t let rec timeout_id_of_js : Ojs.t -> timeout_id = fun (x2 : Ojs.t) -> x2 and timeout_id_to_js : timeout_id -> Ojs.t = fun (x1 : Ojs.t) -> x1 type interval_id = Ojs.t let rec interval_id_of_js : Ojs.t -> interval_id = fun (x4 : Ojs.t) -> x4 and interval_id_to_js : interval_id -> Ojs.t = fun (x3 : Ojs.t) -> x3 let set_interval : (unit -> unit) -> int -> interval_id = fun (x5 : unit -> unit) (x6 : int) -> interval_id_of_js (Ojs.call Ojs.global "setInterval" [|(Ojs.fun_to_js 1 (fun _ -> x5 ()));(Ojs.int_to_js x6)|]) let set_timeout : (unit -> unit) -> int -> timeout_id = fun (x7 : unit -> unit) (x8 : int) -> timeout_id_of_js (Ojs.call Ojs.global "setTimeout" [|(Ojs.fun_to_js 1 (fun _ -> x7 ()));(Ojs.int_to_js x8)|]) let clear_timeout : timeout_id -> unit = fun (x9 : timeout_id) -> ignore (Ojs.call Ojs.global "clearTimeout" [|(timeout_id_to_js x9)|]) let clear_interval : interval_id -> unit = fun (x10 : interval_id) -> ignore (Ojs.call Ojs.global "clearInterval" [|(interval_id_to_js x10)|]) ================================================ FILE: node-test/bindings/expected/imports.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] let path : Ojs.t = Jsoo_runtime.Js.runtime_value "node_path" ================================================ FILE: node-test/bindings/expected/number.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let toString : t -> ?radix:int -> unit -> float = fun (x6 : t) ?radix:(x3 : int option) () -> Ojs.float_of_js (let x7 = t_to_js x6 in Ojs.call (Ojs.get_prop_ascii x7 "toString") "apply" [|x7;((let x4 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in (match x3 with | Some x5 -> ignore (Ojs.call x4 "push" [|(Ojs.int_to_js x5)|]) | None -> ()); x4))|]) let toFixed : t -> ?fractionDigits:int -> unit -> float = fun (x11 : t) ?fractionDigits:(x8 : int option) () -> Ojs.float_of_js (let x12 = t_to_js x11 in Ojs.call (Ojs.get_prop_ascii x12 "toFixed") "apply" [|x12;((let x9 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in (match x8 with | Some x10 -> ignore (Ojs.call x9 "push" [|(Ojs.int_to_js x10)|]) | None -> ()); x9))|]) let toExponential : t -> ?fractionDigits:int -> unit -> float = fun (x16 : t) ?fractionDigits:(x13 : int option) () -> Ojs.float_of_js (let x17 = t_to_js x16 in Ojs.call (Ojs.get_prop_ascii x17 "toExponential") "apply" [|x17;((let x14 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in (match x13 with | Some x15 -> ignore (Ojs.call x14 "push" [|(Ojs.int_to_js x15)|]) | None -> ()); x14))|]) let toPrecision : t -> ?precision:int -> unit -> float = fun (x21 : t) ?precision:(x18 : int option) () -> Ojs.float_of_js (let x22 = t_to_js x21 in Ojs.call (Ojs.get_prop_ascii x22 "toPrecision") "apply" [|x22;((let x19 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in (match x18 with | Some x20 -> ignore (Ojs.call x19 "push" [|(Ojs.int_to_js x20)|]) | None -> ()); x19))|]) let valueOf : t -> float = fun (x23 : t) -> Ojs.float_of_js (Ojs.call (t_to_js x23) "valueOf" [||]) module Scoped = struct let create : 'any -> t = fun (x24 : 'any) -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Number") [|(Obj.magic x24)|]) let invoke : 'any -> float = fun (x25 : 'any) -> Ojs.float_of_js (Ojs.apply (Ojs.get_prop_ascii Ojs.global "Number") [|(Obj.magic x25)|]) let min_value : float = Ojs.float_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number") "MIN_VALUE") let max_value : float = Ojs.float_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number") "MAX_VALUE") let nan : float = Ojs.float_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number") "NaN") let negative_infinity : float = Ojs.float_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number") "NEGATIVE_INFINITY") let positive_infinity : float = Ojs.float_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "Number") "POSITIVE_INFINITY") end module Static = struct type number = t let rec number_of_js : Ojs.t -> number = fun (x27 : Ojs.t) -> t_of_js x27 and number_to_js : number -> Ojs.t = fun (x26 : t) -> t_to_js x26 type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x29 : Ojs.t) -> x29 and t_to_js : t -> Ojs.t = fun (x28 : Ojs.t) -> x28 let create : t -> 'any -> number = fun (x31 : t) (x30 : 'any) -> number_of_js (Ojs.new_obj (t_to_js x31) [|(Obj.magic x30)|]) let apply : t -> 'any -> float = fun (x33 : t) (x32 : 'any) -> Ojs.float_of_js (Ojs.apply (t_to_js x33) [|(Obj.magic x32)|]) let min_value : t -> float = fun (x34 : t) -> Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x34) "MIN_VALUE") let max_value : t -> float = fun (x35 : t) -> Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x35) "MAX_VALUE") let nan : t -> float = fun (x36 : t) -> Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x36) "NaN") let negative_infinity : t -> float = fun (x37 : t) -> Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x37) "NEGATIVE_INFINITY") let positive_infinity : t -> float = fun (x38 : t) -> Ojs.float_of_js (Ojs.get_prop_ascii (t_to_js x38) "POSITIVE_INFINITY") end let number : Static.t = Static.t_of_js (Ojs.get_prop_ascii Ojs.global "Number") ================================================ FILE: node-test/bindings/expected/path.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] let sep : string = Ojs.string_of_js (Ojs.get_prop_ascii Imports.path "sep") let dirname : string -> string = fun (x1 : string) -> Ojs.string_of_js (Ojs.call Imports.path "dirname" [|(Ojs.string_to_js x1)|]) let extname : string -> string = fun (x2 : string) -> Ojs.string_of_js (Ojs.call Imports.path "extname" [|(Ojs.string_to_js x2)|]) let is_absolute : string -> bool = fun (x3 : string) -> Ojs.bool_of_js (Ojs.call Imports.path "isAbsolute" [|(Ojs.string_to_js x3)|]) let join : string list -> string = fun (x4 : string list) -> Ojs.string_of_js (let x7 = Imports.path in Ojs.call (Ojs.get_prop_ascii x7 "join") "apply" [|x7;((let x5 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in List.iter (fun (x6 : string) -> ignore (Ojs.call x5 "push" [|(Ojs.string_to_js x6)|])) x4; x5))|]) let normalize : string -> string = fun (x8 : string) -> Ojs.string_of_js (Ojs.call Imports.path "normalize" [|(Ojs.string_to_js x8)|]) type parse_result = { dir: string ; root: string ; base: string ; name: string ; ext: string } let rec parse_result_of_js : Ojs.t -> parse_result = fun (x10 : Ojs.t) -> { dir = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "dir")); root = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "root")); base = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "base")); name = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "name")); ext = (Ojs.string_of_js (Ojs.get_prop_ascii x10 "ext")) } and parse_result_to_js : parse_result -> Ojs.t = fun (x9 : parse_result) -> Ojs.obj [|("dir", (Ojs.string_to_js x9.dir));("root", (Ojs.string_to_js x9.root)); ("base", (Ojs.string_to_js x9.base));("name", (Ojs.string_to_js x9.name)); ("ext", (Ojs.string_to_js x9.ext))|] let parse : string -> parse_result = fun (x11 : string) -> parse_result_of_js (Ojs.call Imports.path "parse" [|(Ojs.string_to_js x11)|]) ================================================ FILE: node-test/bindings/expected/process.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] let env : string Container.StringMap.t = Container.StringMap.t_of_js Ojs.string_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "process") "env") let version : string option = Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "process") "version") ================================================ FILE: node-test/bindings/expected/promise.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module UntypedPromise = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let resolve : Ojs.t -> Ojs.t = fun (x3 : Ojs.t) -> Ojs.call (Ojs.get_prop_ascii Ojs.global "Promise") "resolve" [|x3|] let reject : Ojs.t -> Ojs.t = fun (x4 : Ojs.t) -> Ojs.call (Ojs.get_prop_ascii Ojs.global "Promise") "reject" [|x4|] let then_ : Ojs.t -> success:(Ojs.t -> Ojs.t) -> error:(Ojs.t -> Ojs.t) -> Ojs.t = fun (x9 : Ojs.t) ~success:(x5 : Ojs.t -> Ojs.t) ~error:(x7 : Ojs.t -> Ojs.t) -> Ojs.call x9 "then" [|(Ojs.fun_to_js 1 x5);(Ojs.fun_to_js 1 x7)|] let all : Ojs.t list -> Ojs.t = fun (x10 : Ojs.t list) -> Ojs.call (Ojs.get_prop_ascii Ojs.global "Promise") "all" [|(Ojs.list_to_js (fun (x11 : Ojs.t) -> x11) x10)|] include struct type wrap = { content: Ojs.t } [@@@ocaml.warning "-7-32-39"] let rec wrap_of_js : Ojs.t -> wrap = fun (x13 : Ojs.t) -> { content = (Ojs.get_prop_ascii x13 "content") } and wrap_to_js : wrap -> Ojs.t = fun (x12 : wrap) -> Ojs.obj [|("content", (x12.content))|] end let is_promise o = (resolve o) == o let wrap o = if is_promise o then wrap_to_js { content = o } else o let unwrap o = if Ojs.has_property o "content" then Ojs.get_prop_ascii o "content" else o let return x = resolve (wrap x) let fail err = reject (wrap err) let bind ?(error= fail) p f = then_ p ~success:(fun x -> f (unwrap x)) ~error:(fun x -> error (unwrap x)) end type 'a t = UntypedPromise.t type error = Ojs.t let fail error = UntypedPromise.fail error let return x = UntypedPromise.return (Obj.magic x) let bind ?error p f = UntypedPromise.bind ?error p (fun x -> f (Obj.magic x)) let prod p1 p2 = bind (UntypedPromise.all [p1; p2]) (fun ojs -> match Ojs.list_of_js Ojs.t_of_js ojs with | x1::x2::[] -> return (x1, x2) | _ -> assert false) let map f p = bind p (fun x -> return (f x)) let t_to_js f p = UntypedPromise.t_to_js (map f p) let t_of_js f p = map f (UntypedPromise.t_of_js p) let (let+) p f = map f p let (and+) = prod let ( let* ) p f = bind p f let ( and* ) = prod let catch p error = bind p ~error return ================================================ FILE: node-test/bindings/fs.mli ================================================ [@@@js.scope "@node_fs.promises"] module Dirent : sig type t = Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val name: t -> string [@@js.get] val is_file: t -> bool [@@js.call] val is_directory: t -> bool [@@js.call] end module Dir : sig type t = Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val path: t -> string [@@js.get] val close: t -> unit Promise.t [@@js.call] val read:t -> Dirent.t option Promise.t [@@js.call] end module FileHandle : sig type t = Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t type read = { bytes_read: int; buffer: Buffer.t; } val append_file: t -> Buffer.t -> unit Promise.t [@@js.call] val read: t -> Buffer.t -> int -> int -> int -> read Promise.t [@@js.call] val chmod: t -> int -> unit Promise.t [@@js.call] val chmown: t -> uid:int -> gid:int -> unit Promise.t [@@js.call] val close: t -> unit Promise.t [@@js.call] val datasync: t -> unit Promise.t [@@js.call] val fd: t -> int [@@js.get] end val readdir: string -> string list Promise.t [@@js.global] val open_: string -> flag:string -> FileHandle.t Promise.t [@@js.global] val rmdir: string -> unit Promise.t [@@js.global] val rename: string -> string -> unit Promise.t [@@js.global] val unlink: string -> unit Promise.t [@@js.global] ================================================ FILE: node-test/bindings/global.mli ================================================ type timeout_id val timeout_id_to_js: timeout_id -> Ojs.t val timeout_id_of_js: Ojs.t -> timeout_id type interval_id val interval_id_to_js: interval_id -> Ojs.t val interval_id_of_js: Ojs.t -> interval_id val set_interval: (unit -> unit) -> int -> interval_id [@@js.global] val set_timeout: (unit -> unit) -> int -> timeout_id [@@js.global] val clear_timeout: timeout_id -> unit [@@js.global] val clear_interval: interval_id -> unit [@@js.global] ================================================ FILE: node-test/bindings/imports.js ================================================ //Provides: node_path var node_path = require('path'); //Provides: node_fs var node_fs = require('fs'); ================================================ FILE: node-test/bindings/imports.mli ================================================ val path: Ojs.t [@@js.global "@node_path"] ================================================ FILE: node-test/bindings/imports.wat ================================================ (global (export "_node_path") (import "js" "node_path") anyref) (global (export "_node_fs") (import "js" "node_fs") anyref) ================================================ FILE: node-test/bindings/number.mli ================================================ type t = private Ojs.t val toString: t -> ?radix:int -> unit -> float [@@js.call] val toFixed: t -> ?fractionDigits:int -> unit -> float [@@js.call] val toExponential: t -> ?fractionDigits:int -> unit -> float [@@js.call] val toPrecision: t -> ?precision:int -> unit -> float [@@js.call] val valueOf: t -> float [@@js.call] (* scoped *) module [@js.scope "Number"] Scoped : sig val create: 'any -> t [@@js.create] val invoke: 'any -> float [@@js.invoke] val min_value: float [@@js.global "MIN_VALUE"] val max_value: float [@@js.global "MAX_VALUE"] val nan: float [@@js.global "NaN"] val negative_infinity: float [@@js.global "NEGATIVE_INFINITY"] val positive_infinity: float [@@js.global "POSITIVE_INFINITY"] end (* non-scoped *) module Static : sig type number = t type t = private Ojs.t val create: t -> 'any -> number [@@js.apply_newable] val apply: t -> 'any -> float [@@js.apply] val min_value: t -> float [@@js.get "MIN_VALUE"] val max_value: t -> float [@@js.get "MAX_VALUE"] val nan: t -> float [@@js.get "NaN"] val negative_infinity: t -> float [@@js.get "NEGATIVE_INFINITY"] val positive_infinity: t -> float [@@js.get "POSITIVE_INFINITY"] end val number: Static.t [@@js.global "Number"] ================================================ FILE: node-test/bindings/path.mli ================================================ [@@@js.scope Imports.path] val sep: string [@@js.global] val dirname: string -> string [@@js.global] val extname: string -> string [@@js.global] val is_absolute: string -> bool [@@js.global] val join: (string list [@js.variadic]) -> string [@@js.global] val normalize: string -> string [@@js.global] type parse_result = { dir: string; root: string; base: string; name: string; ext: string } val parse: string -> parse_result [@@js.global] ================================================ FILE: node-test/bindings/process.mli ================================================ [@@@js.scope "process"] val env : string Container.StringMap.t [@@js.global] val version: string option [@@js.global] ================================================ FILE: node-test/bindings/promise.mli ================================================ module UntypedPromise : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t [@@@js.stop] val return: Ojs.t -> t val fail: Ojs.t -> t val bind: ?error:(Ojs.t -> t) -> t -> (Ojs.t -> t) -> t val all: Ojs.t list -> t [@@@js.start] [@@@js.implem val resolve: Ojs.t -> Ojs.t [@@js.global "Promise.resolve"] val reject: Ojs.t -> Ojs.t [@@js.global "Promise.reject"] val then_: Ojs.t -> success:(Ojs.t -> Ojs.t) -> error:(Ojs.t -> Ojs.t) -> Ojs.t [@@js.call "then"] val all: Ojs.t list -> Ojs.t [@@js.global "Promise.all"] type wrap = { content: Ojs.t }[@@js] let is_promise o = resolve o == o let wrap o = if is_promise o then wrap_to_js { content = o } else o let unwrap o = if Ojs.has_property o "content" then Ojs.get_prop_ascii o "content" else o let return x = resolve (wrap x) let fail err = reject (wrap err) let bind ?(error = fail) p f = then_ p ~success:(fun x -> f (unwrap x)) ~error:(fun x -> error (unwrap x)) ] end [@@@js.stop] type 'a t val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t type error = Ojs.t val fail: error -> 'a t val return: 'a -> 'a t val bind: ?error:(error -> 'b t) -> 'a t -> ('a -> 'b t) -> 'b t val map: ('a -> 'b) -> 'a t -> 'b t val prod: 'a t -> 'b t -> ('a * 'b) t val ( let+ ): 'a t -> ('a -> 'b) -> 'b t val ( and+ ): 'a t -> 'b t -> ('a * 'b) t val ( let* ): 'a t -> ('a -> 'b t) -> 'b t val ( and* ): 'a t -> 'b t -> ('a * 'b) t val catch: 'a t -> (error -> 'a t) -> 'a t [@@@js.start] [@@@js.implem type 'a t = UntypedPromise.t type error = Ojs.t let fail error = UntypedPromise.fail error let return x = UntypedPromise.return (Obj.magic x) let bind ?error p f = UntypedPromise.bind ?error p (fun x -> f (Obj.magic x)) let prod p1 p2 = bind (UntypedPromise.all [p1; p2]) (fun ojs -> match Ojs.list_of_js Ojs.t_of_js ojs with | [x1; x2] -> return (x1, x2) | _ -> assert false ) let map f p = bind p (fun x -> return (f x)) let t_to_js f p = UntypedPromise.t_to_js (map f p) let t_of_js f p = map f (UntypedPromise.t_of_js p) let (let+) p f = map f p let (and+) = prod let (let*) p f = bind p f let (and*) = prod let catch p error = bind p ~error return ] ================================================ FILE: node-test/runtime_primitives/bindings.mli ================================================ module [@js.scope "@node_fs"] Fs : sig val write_file_sync : string -> string -> unit [@@js.global "writeFileSync"] val read_file_sync : string -> encoding:string -> string [@@js.global "readFileSync"] val readdir_sync : string -> string array [@@js.global "readdirSync"] val append_file_sync : string -> string -> unit [@@js.global "appendFileSync"] end module [@js.scope "@node_path"] Path : sig val separator: string [@@js.global "sep"] val join : (string list [@js.variadic]) -> string [@@js.global "join"] end val node_version : string [@@js.global "@node_version"] val log : string -> unit [@@js.global "@node_console"] ================================================ FILE: node-test/runtime_primitives/dune ================================================ (rule (targets bindings.ml) (deps bindings.mli) (action (run gen_js_api %{deps}))) (executable (name example) (libraries ojs) (preprocess (pps gen_js_api.ppx)) (modes js wasm) (js_of_ocaml (javascript_files imports.js)) (wasm_of_ocaml (javascript_files imports.js imports.wat))) (rule (alias runtest) (enabled_if %{bin-available:node}) (action (run node %{dep:./example.bc.js}))) (rule (alias runtest-wasm) (enabled_if %{bin-available:node}) (action (run node %{dep:./example.bc.wasm.js}))) ================================================ FILE: node-test/runtime_primitives/example.ml ================================================ open Bindings let initial_content = "Hello, Node.js!" let appended_line = "\nAppending a new line." let encoding = "utf-8" let filename = "example.txt" let run () = let file = Path.join ["."; filename] in Fs.write_file_sync file initial_content; let content = Fs.read_file_sync file ~encoding in if content <> initial_content then failwith "Unexpected initial content"; log ("File content: " ^ content); let files = Fs.readdir_sync "." |> Array.to_list in if not (List.mem filename files) then failwith "example.txt missing from directory listing"; log ("Files in current directory: " ^ String.concat ", " files); Fs.append_file_sync file appended_line; let updated = Fs.read_file_sync file ~encoding in if updated <> initial_content ^ appended_line then failwith "Append failed"; log ("Updated content: " ^ updated); log ("Path separator reported by Node: " ^ Path.separator); log ("Node.js version: " ^ node_version) let () = run () ================================================ FILE: node-test/runtime_primitives/imports.js ================================================ 'use strict'; //Provides: node_path var node_path = require('path'); //Provides: node_fs var node_fs = require('fs'); //Provides: node_version var node_version = require('process').version; //Provides: node_console var node_console = console.log; ================================================ FILE: node-test/runtime_primitives/imports.wat ================================================ (global (export "_node_path") (import "js" "node_path") anyref) (global (export "_node_fs") (import "js" "node_fs") anyref) (global (export "_node_version") (import "js" "node_version") anyref) (global (export "_node_console") (import "js" "node_console") anyref) ================================================ FILE: node-test/test1/dune ================================================ (executable (name test) (libraries ojs node) (preprocess (pps gen_js_api.ppx)) (modes js wasm) (js_of_ocaml (javascript_files recursive.js)) (wasm_of_ocaml (javascript_files recursive.js))) (rule (targets recursive.ml) (deps recursive.mli) (action (run gen_js_api %{deps}))) (rule (alias runtest) (enabled_if %{bin-available:node}) (action (run node %{dep:./test.bc.js}))) (rule (alias runtest-wasm) (enabled_if %{bin-available:node}) (action (run node %{dep:./test.bc.wasm.js}))) ================================================ FILE: node-test/test1/recursive.js ================================================ var Foo = /*#__PURE__*/function () { "use strict"; function Foo(name) { this.name = name; } var _proto = Foo.prototype; _proto.describe = function describe() { return "Foo:".concat(this.name); }; _proto.toBar = function toBar() { return new Bar(this.name); }; return Foo; }(); var Bar = /*#__PURE__*/function () { "use strict"; function Bar(name) { this.name = name; } var _proto2 = Bar.prototype; _proto2.describe = function describe() { return "Bar:".concat(this.name); }; _proto2.toFoo = function toFoo() { return new Foo(this.name); }; return Bar; }(); globalThis.Foo = Foo globalThis.Bar = Bar ================================================ FILE: node-test/test1/recursive.mli ================================================ module [@js.scope "Foo"] rec Foo : sig type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val create: string -> t [@@js.create] val describe: t -> string [@@js.call "describe"] val to_bar: t -> Bar.t [@@js.call "toBar"] end and [@js.scope "Bar"] Bar : sig type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val create: string -> t [@@js.create] val describe: t -> string [@@js.call "describe"] val to_foo: t -> Foo.t [@@js.call "toFoo"] end ================================================ FILE: node-test/test1/test.ml ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) [@@@ocaml.warning "-32-34"] open Node let check_node_version version = let major_version = function | Some s when String.length s > 0 && s.[0] = 'v' -> begin match String.sub s 1 (String.length s - 1) |> String.split_on_char '.' with | [] -> None | hd :: _ -> int_of_string_opt hd end | _ -> None in if Option.value ~default:(-1) (major_version Process.version) < version then begin Printf.eprintf "[WARNING] Ignoring test: it requires Node > %d; please upgrade (current version %s)" version (Option.value ~default:"???" Process.version); exit 0 end let () = check_node_version 18 (** Buffer **) let caml_from_set s = let len = String.length s in let buf = Buffer.alloc len in String.iteri (fun k x -> Buffer.set buf k (Char.code x) ) s; buf let caml_from_write s = let len = String.length s in let buf = Buffer.alloc len in let written = Buffer.write buf s in assert (written = len); buf let assert_equal_buffer b1 b2 = let len1 = Buffer.length b1 in let len2 = Buffer.length b2 in assert (len1 = len2); for k = 0 to len1 -1 do assert (Buffer.get b1 k = Buffer.get b2 k) done let copy src = let len = Buffer.length src in let dst = Buffer.alloc len in let written = Buffer.copy src ~dst ~start:0 ~dst_start:0 ~dst_end:len in assert (len = written); dst let () = let test = "test" in let native = Buffer.from test in let from_set = caml_from_set test in let from_write = caml_from_write test in let from_copy = copy native in assert_equal_buffer native from_set; assert_equal_buffer native from_write; assert_equal_buffer native from_copy (** Process **) let () = Container.StringMap.iter (fun key value -> assert (Sys.getenv key = value) ) Process.env let uncaught_handler p = let open Promise in catch p (fun error -> Printf.eprintf "Uncaught exception: %s\n" (Printexc.to_string (Obj.magic error)); exit 1 ) (** FileSystem **) let root : unit Promise.t = let open Promise in uncaught_handler begin let* contents = Fs.readdir "." and+ contents' = Fs.readdir "." in List.iter2 (fun x y -> assert (x = y)) contents contents'; return () end (*** Index signature **) include [%js: module ArrayLike (K : Ojs.T) : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val create: unit -> t [@@js.builder] val get: t -> int -> K.t option [@@js.index_get] val set: t -> int -> K.t -> unit [@@js.index_set] end module MapLike (K : Ojs.T) : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val create: unit -> t [@@js.builder] val get: t -> string -> K.t option [@@js.index_get] val set: t -> string -> K.t -> unit [@@js.index_set] end module Symbol : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val fresh: unit -> t [@@js.global "Symbol"] end module SymbolMap (K : Ojs.T) : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val create: unit -> t [@@js.builder] val get: t -> Symbol.t -> K.t option [@@js.index_get] val set: t -> Symbol.t -> K.t -> unit [@@js.index_set] end ] let () = let module M = MapLike([%js: type t = string val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t]) in let map_str = M.create () in M.set map_str "foo" "bar"; assert (M.get map_str "foo" = Some "bar"); M.set map_str "baz" "boo"; assert (M.get map_str "baz" = Some "boo"); let module A = ArrayLike([%js: type t = int val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t]) in let map_int = A.create () in let len = 10 in for k = 0 to len - 1 do A.set map_int k k; assert (A.get map_int k = Some k); A.set map_int k (k * k); assert (A.get map_int k = Some (k * k)); done; let module M = SymbolMap([%js: type t = string val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t]) in let a = Symbol.fresh () in let b = Symbol.fresh () in let map_str = M.create () in M.set map_str a "bar"; assert (M.get map_str a = Some "bar"); M.set map_str b "boo"; assert (M.get map_str b = Some "boo") (*** Function signature **) include [%js: module Concat : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val apply: t -> (string list [@js.variadic]) -> string [@@js.apply] end module [@js.scope Imports.path] Path2 : sig val join: Concat.t [@@js.global "join"] end ] let () = let args = ["foo"; "bar"; "baz"] in let res1 = Path.join args in let res2 = Concat.apply Path2.join args in assert (res1 = res2); () (*** Newable function *) include [%js: module Date: sig type t = private Ojs.t val getUTCFullYear: t -> float [@@js.call "getUTCFullYear"] val getUTCMonth: t -> float [@@js.call "getUTCMonth"] val getUTCDate: t -> float [@@js.call "getUTCDate"] end module DateConstructor: sig type t = private Ojs.t val utc: t -> year:int -> month:int -> ?date:int -> ?hours:int -> ?minutes:int -> ?seconds:int -> ?ms:int -> unit -> float [@@js.call "UTC"] val new_: t -> float -> Date.t [@@js.apply_newable] end val date: DateConstructor.t [@@js.global "Date"] ] let () = let d = DateConstructor.new_ date (DateConstructor.utc date ~year:1999 ~month:11 ~date:31 ()) in assert (int_of_float (Date.getUTCFullYear d) == 1999); assert (int_of_float (Date.getUTCMonth d) == 11); assert (int_of_float (Date.getUTCDate d) == 31); () (** Arrays **) let () = let open Arrays.StringArray in let a = create () in for k = 0 to 10 do push a (string_of_int k); done; let s = join a "," in List.iteri (fun k x -> assert (string_of_int k = x) ) (String.split_on_char ',' s) (** Invoking a global object **) (** https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/Number **) let () = let check (a: Number.t) (b: float) = assert (Ojs.instance_of (a :> Ojs.t) ~constr:(Number.number :> Ojs.t)); assert (not (Ojs.instance_of (Ojs.float_to_js b) ~constr:(Number.number :> Ojs.t))); assert (Number.valueOf a = b); () in let s = Ojs.string_to_js "123" in check (Number.Scoped.create s) (Number.Scoped.invoke s); check (Number.Static.create Number.number s) (Number.Static.apply Number.number s); assert (Number.Scoped.max_value = Number.Static.max_value Number.number); () (** Using recursive modules **) let () = let open Recursive in let fooA = Foo.create "A" in assert (Foo.describe fooA = "Foo:A"); let barA = Foo.to_bar fooA in assert (Bar.describe barA = "Bar:A"); let fooA' = Bar.to_foo barA in assert (Foo.describe fooA' = "Foo:A"); () (** Using first class modules **) include [%js: val to_string: (module[@js] Ojs.T with type t = 'a) -> 'a -> string [@@js.call "toString"] ] let () = let check (type a) (module A : Ojs.T with type t = a) (value: a) (expected: string) = let str = to_string (module A) value in assert (str = expected) in check (module Ojs.Int) 42 "42"; check (module Ojs.Float) 4.2 "4.2"; check (module Ojs.String) "hello" "hello"; check (module Ojs.List(Ojs.Int)) [4;2] "4,2"; check (module Ojs.List(Ojs.String)) ["hello"; "world"] "hello,world"; () let () = let open Arrays.JsArray2 in let a = create () in for k = 0 to 10 do push (module Ojs.String) a (string_of_int k); done; let sa = join a "," in List.iteri (fun k x -> assert (string_of_int k = x) ) (String.split_on_char ',' sa); let b = let orig = List.init 11 string_of_int in create' (module Ojs.String) orig in let sb = join b "," in assert (sa = sb); assert (get (module Ojs.String) a 0 = Some "0"); set (module Ojs.String) a 1 "foo"; assert (get (module Ojs.String) a 1 = Some "foo"); () ================================================ FILE: ojs.opam ================================================ # This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "1.1.7" synopsis: "Runtime Library for gen_js_api generated libraries" description: "To be used in conjunction with gen_js_api" maintainer: ["Alain Frisch "] authors: [ "Alain Frisch " "Sebastien Briais " ] license: "MIT" homepage: "https://github.com/LexiFi/gen_js_api" bug-reports: "https://github.com/LexiFi/gen_js_api/issues" depends: [ "dune" {>= "3.17"} "ocaml" {>= "4.13"} "js_of_ocaml-compiler" {>= "6.3.0"} "odoc" {with-doc} ] dev-repo: "git+https://github.com/LexiFi/gen_js_api.git" build: [ ["dune" "subst"] {dev} ["dune" "build" "-p" name "-j" jobs "@install" "@doc" {with-doc}] ] ================================================ FILE: ojs.opam.template ================================================ build: [ ["dune" "subst"] {dev} ["dune" "build" "-p" name "-j" jobs "@install" "@doc" {with-doc}] ] ================================================ FILE: ppx-driver/dune ================================================ (library (name gen_js_api_ppx_driver) (public_name gen_js_api.ppx) (synopsis "Syntactic support for gen_js_api") (libraries gen_js_api.lib ppxlib.ast ppxlib) (kind ppx_rewriter) (ppx_runtime_libraries ojs) (preprocess no_preprocessing)) ================================================ FILE: ppx-driver/gen_js_api_ppx_driver.ml ================================================ let check_attributes_with_ppxlib = false let check_locations_with_ppxlib = false let () = if check_attributes_with_ppxlib then ( Ppxlib.Driver.enable_checks (); Gen_js_api_ppx.check_attribute := false ); if check_locations_with_ppxlib then ( Ppxlib.Driver.enable_location_check () ); let mapper_for_sig = Gen_js_api_ppx.mark_attributes_as_used in let mapper_for_str = Gen_js_api_ppx.mark_attributes_as_used in let module_expr_ext = let rewriter ~loc ~path:_ si = Gen_js_api_ppx.module_expr_rewriter ~loc ~attrs:[] si in Ppxlib.Extension.declare "js" Ppxlib.Extension.Context.Module_expr Ppxlib.(Ast_pattern.psig Ast_pattern.__) rewriter |> Ppxlib.Context_free.Rule.extension in let ext_to = let rewriter ~loc ~path:_ core_type = Gen_js_api_ppx.js_to_rewriter ~loc core_type in Ppxlib.Extension.declare "js.to" Ppxlib.Extension.Context.Expression Ppxlib.(Ast_pattern.ptyp Ast_pattern.__) rewriter |> Ppxlib.Context_free.Rule.extension in let ext_of = let rewriter ~loc ~path:_ core_type = Gen_js_api_ppx.js_of_rewriter ~loc core_type in Ppxlib.Extension.declare "js.of" Ppxlib.Extension.Context.Expression Ppxlib.(Ast_pattern.ptyp Ast_pattern.__) rewriter |> Ppxlib.Context_free.Rule.extension in let attr_typ = let rewriter ~ctxt (rec_flag : Ppxlib.Asttypes.rec_flag) tdl _ = Gen_js_api_ppx.type_decl_rewriter ~loc:(Ppxlib.Expansion_context.Deriver.derived_item_loc ctxt) rec_flag tdl in Ppxlib.Context_free.Rule.attr_str_type_decl (Ppxlib.Attribute.declare "js" Ppxlib.Attribute.Context.type_declaration Ppxlib.(Ast_pattern.pstr Ast_pattern.nil) ()) rewriter in Ppxlib.Driver.register_transformation "gen_js_api" ~rules:[module_expr_ext; ext_of; ext_to; attr_typ ] ~impl:(mapper_for_str # structure) ~intf:(mapper_for_sig # signature) ================================================ FILE: ppx-lib/dune ================================================ (library (name gen_js_api_ppx) (public_name gen_js_api.lib) (libraries compiler-libs.common ppxlib) (ppx_runtime_libraries ojs) (preprocess no_preprocessing)) ================================================ FILE: ppx-lib/gen_js_api_ppx.ml ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) open Ppxlib open Asttypes open Parsetree open Longident open Ast_helper open Location let mkloc txt loc = { txt; loc } let mknoloc txt = mkloc txt !Ast_helper.default_loc (** Errors *) type error = | Expression_expected | Identifier_expected | Structure_expected | Invalid_expression | Multiple_binding_declarations | Binding_type_mismatch | Cannot_parse_type | Cannot_parse_sigitem | Cannot_parse_classdecl | Cannot_parse_classfield | Implicit_name of string | Not_supported_here of string | Record_expected of string | Record_constructor_in_union | Unknown_union_method | Non_constant_constructor_in_enum | Multiple_default_case | Duplicate_case_value of location * location | Invalid_variadic_type_arg | No_input | Multiple_inputs | Unlabelled_argument_in_builder | Spurious_attribute of label | Sum_kind_args | Union_without_discriminator | Contravariant_type_parameter of string | Cannot_set_runtime_value of string exception Error of Location.t * error let is_ascii s = let exception Break in try String.iter (fun c -> if Char.code c > 127 then raise Break) s; true with Break -> false let check_attribute = ref true let used_attributes_tbl = Hashtbl.create 16 (* [merlin_hide] tells merlin to not look at a node, or at any of its descendants. *) let merlin_hide = { attr_name = { txt = "merlin.hide"; loc = Location.none } ; attr_payload = PStr [] ; attr_loc = Location.none } let register_loc attr = Ppxlib.Attribute.mark_as_handled_manually attr; Hashtbl.replace used_attributes_tbl attr.attr_name.loc () let is_registered_loc loc = Hashtbl.mem used_attributes_tbl loc let error loc err = raise (Error (loc, err)) let filter_attr_name key attr = if attr.attr_name.txt = key then begin register_loc attr; true end else false let filter_extension key name = name.txt = key let has_attribute key attrs = List.exists (filter_attr_name key) attrs let get_attribute key attrs = match List.find (filter_attr_name key) attrs with | exception Not_found -> None | attr -> Some attr let unoption = function | Some x -> x | None -> assert false let expr_of_stritem = function | {pstr_desc=Pstr_eval (e, _); _} -> e | p -> error p.pstr_loc Expression_expected let expr_of_payload {attr_loc; attr_payload; _} = match attr_payload with | PStr [x] -> expr_of_stritem x | _ -> error attr_loc Expression_expected let str_of_payload {attr_loc; attr_payload; _} = match attr_payload with | PStr x -> x | _ -> error attr_loc Structure_expected let id_of_expr = function | {pexp_desc=Pexp_constant (Pconst_string (s, _, _)); _} -> s | e -> error e.pexp_loc Identifier_expected let get_expr_attribute key attrs = match get_attribute key attrs with | None -> None | Some payload -> Some (expr_of_payload payload) let get_string_attribute key attrs = match get_attribute key attrs with | None -> None | Some payload -> Some (id_of_expr (expr_of_payload payload)) let get_string_attribute_default key default attrs = match get_attribute key attrs with | None -> default | Some payload -> payload.attr_loc, id_of_expr (expr_of_payload payload) let print_error ppf = function | Expression_expected -> Format.fprintf ppf "Expression expected" | Structure_expected -> Format.fprintf ppf "Structure expected" | Identifier_expected -> Format.fprintf ppf "String literal expected" | Invalid_expression -> Format.fprintf ppf "Invalid expression" | Multiple_binding_declarations -> Format.fprintf ppf "Multiple binding declarations" | Binding_type_mismatch -> Format.fprintf ppf "Binding declaration and type are not compatible" | Cannot_parse_type -> Format.fprintf ppf "Cannot parse type" | Cannot_parse_sigitem -> Format.fprintf ppf "Cannot parse signature item" | Cannot_parse_classdecl -> Format.fprintf ppf "Cannot parse class declaration" | Cannot_parse_classfield -> Format.fprintf ppf "Cannot parse class field" | Implicit_name prefix -> Format.fprintf ppf "Implicit name must start with '%s' and cannot be empty" prefix | Not_supported_here msg -> Format.fprintf ppf "%s not supported in this context" msg | Non_constant_constructor_in_enum -> Format.fprintf ppf "Constructors in enums cannot take arguments" | Multiple_default_case -> Format.fprintf ppf "At most one default constructor is supported in variants" | Duplicate_case_value (loc1, loc2) -> let line1, line2 = loc1.loc_start.pos_lnum, loc2.loc_start.pos_lnum in let line1, line2 = if line1 < line2 then line1, line2 else line2, line1 in Format.fprintf ppf "This case value is used twice at lines %d and %d" line1 line2 | Invalid_variadic_type_arg -> Format.fprintf ppf "A variadic function argument must be of type list" | No_input -> Format.fprintf ppf "An input file must be provided" | Multiple_inputs -> Format.fprintf ppf "A single input file must be provided" | Unlabelled_argument_in_builder -> Format.fprintf ppf "Arguments of builder must be named" | Spurious_attribute label -> Format.fprintf ppf "Spurious %s attribute" label | Sum_kind_args -> Format.fprintf ppf "Incompatible label name for 'kind' and constructor arguments." | Record_constructor_in_union -> Format.fprintf ppf "Constructors in unions must not be an inline record." | Unknown_union_method -> Format.fprintf ppf "Unknown method to discriminate unions." | Union_without_discriminator -> Format.fprintf ppf "js.union without way to discriminate values." | Contravariant_type_parameter label -> Format.fprintf ppf "Contravariant type parameter '%s is not allowed." label | Record_expected shape -> Format.fprintf ppf "Record %s expected." shape | Cannot_set_runtime_value name -> Format.fprintf ppf "Cannot set runtime value '%s'." name let () = Location.Error.register_error_of_exn (function | Error (loc, err) -> let createf ~loc fmt = Format.kasprintf (fun str -> Location.Error.make ~loc ~sub:[] str) fmt in Some (createf ~loc "%a" print_error err) | _ -> None ) (* let show_attrs attrs = prerr_endline "==========="; prerr_endline "attributes:"; List.iter (fun ({txt; loc = _}, _) -> prerr_endline txt) attrs *) let js_name ~global_attrs ?(capitalize = false) name = if has_attribute "js.verbatim_names" global_attrs then if capitalize then String.capitalize_ascii name else name else let n = String.length name in let buf = Buffer.create n in let capitalize = ref capitalize in for i = 0 to n-1 do let c = name.[i] in if c = '_' then capitalize := true else if !capitalize then begin Buffer.add_char buf (Char.uppercase_ascii c); capitalize := false end else Buffer.add_char buf c done; Buffer.contents buf let get_js_constr ~global_attrs name attributes = match get_attribute "js" attributes with | None -> `String (js_name ~global_attrs name) | Some payload -> begin match (expr_of_payload payload).pexp_desc with | Pexp_constant (Pconst_string (s, _, _)) -> `String s | Pexp_constant (Pconst_integer (n, _)) -> `Int n | Pexp_constant (Pconst_float (f, _)) -> `Float f | Pexp_construct (ident_loc, _) -> begin match ident_loc.txt with | Lident "true" -> `Bool true | Lident "false" -> `Bool false | _ -> error ident_loc.loc Invalid_expression end | _ -> error payload.attr_loc Invalid_expression end (** AST *) type typ = | Arrow of arrow_params | Unit of Location.t | Js | Name of string * typ list | Variant of { location: Location.t; global_attrs:attributes; attributes:attributes; constrs:constructor list } | Tuple of typ list | Typ_var of string | Packaged_type of { local_name: string; (* `a` specified by `(type a)`*) module_name: string (* `A` as in `(module A : Ojs.T with type t = a)` *) } and lab = | Arg | Lab of {ml: string} | Opt of {ml: string; def: Parsetree.expression option} and arg = { lab: lab; att: attributes; typ: typ; } and arrow_params = { ty_args: arg list; ty_vararg: arg option; unit_arg: bool; ty_res: typ; } and constructor_arg = | Constant | Unary of typ | Nary of typ list | Record of (Location.t * lid * string * typ) list and constructor = { mlconstr: string; arg: constructor_arg; attributes: attributes; location: Location.t; } let arg_label = function | Arg -> Nolabel | Lab {ml; _} -> Labelled ml | Opt {ml; _} -> Optional ml type apply_type = | Function (* f(..) *) | NewableFunction (* new f(..) *) type valdef = | Cast | Ignore | PropGet of string | PropSet of string | IndexGet | IndexSet | MethCall of string | Apply of apply_type | Invoke | Global of string | New of string option | Builder of attributes | Auto of valdef let rec string_of_valdef = function | Cast -> "js.cast" | Ignore -> "js.ignore" | PropGet _ -> "js.get" | PropSet _ -> "js.set" | IndexGet -> "js.index_get" | IndexSet -> "js.index_set" | MethCall _ -> "js.call" | Apply Function -> "js.apply" | Apply NewableFunction -> "js.apply_newable" | Invoke -> "js.invoke" | Global _ -> "js.global" | New None -> "js.create" | New (Some _) -> "js.new" | Builder _ -> "js.builder" | Auto valdef -> string_of_valdef valdef let auto_deprecation_attribute loc valdef = let message = Printf.sprintf "Heuristic for automatic binding is deprecated; please add the '@%s' attribute." (string_of_valdef valdef) in attribute_of_warning loc message type methoddef = | Getter of string | Setter of string | IndexGetter | IndexSetter | MethodCall of string | ApplyAsFunction of apply_type type method_decl = { method_name: string; method_typ: typ; method_def: methoddef; method_loc: Location.t; method_attrs: attributes } type class_field = | Method of method_decl | Inherit of Longident.t Location.loc type classdecl = | Declaration of { class_name: string; class_fields: class_field list } | Constructor of { class_name: string; js_class_name: string; class_arrow: arrow_params } type decl = | Module of functor_parameter list * string * decl list | RecModule of (module_type * functor_parameter list * string * decl list) list | ModuleAlias of string * Longident.t Location.loc | Type of rec_flag * Parsetree.type_declaration list * attributes | Val of { name:string; ty: typ; decl: valdef; loc: Location.t; packages: (string * string) list; (** reversed order, (local_name, module_name) *) global_attrs: attributes } | Class of classdecl list | Implem of Parsetree.structure | Open of Parsetree.open_description | Include of Parsetree.module_expr Parsetree.include_infos (** Parsing *) let local_type_of_type_var label = "__"^label let neg_variance = function | -1 -> 1 | 0 | 1 -> -1 | _ -> invalid_arg "neg_variance" let no_attributes attributes = List.iter (fun attr -> ignore (filter_attr_name "js.dummy" attr) ) attributes; attributes = [] type type_context = { type_params: string list; packages: (string * string) list; (** reversed order, (local_name, module_name) *) } let empty_type_context = { type_params = []; packages = [] } let rec parse_arg ~variance (ctx: type_context) lab ~global_attrs ty = let lab = match lab with | Nolabel -> Arg | Labelled ml -> Lab {ml} | Optional ml -> Opt {ml; def=get_expr_attribute "js.default" ty.ptyp_attributes} in { lab; att=ty.ptyp_attributes; typ = parse_typ ~variance:(neg_variance variance) ctx ~global_attrs ty; } and parse_typ ~variance ctx ~global_attrs ty = match ty.ptyp_desc with | Ptyp_arrow (lab, t1, t2) when has_attribute "js.variadic" t1.ptyp_attributes -> begin match parse_arg ~variance ctx lab ~global_attrs t1 with | {lab; att; typ=Name ("list", [typ])} -> let ty_vararg = Some {lab; att; typ} in begin match parse_typ ~variance ctx ~global_attrs t2 with | Arrow ({ty_args = []; ty_vararg = None; unit_arg = _; ty_res = _} as params) when no_attributes t2.ptyp_attributes -> Arrow {params with ty_vararg} | Arrow _ when t2.ptyp_attributes = [] -> error ty.ptyp_loc Cannot_parse_type | tres -> Arrow {ty_args = []; ty_vararg; unit_arg = false; ty_res = tres} end | _ -> error t1.ptyp_loc Invalid_variadic_type_arg end | Ptyp_arrow (lab, t1, t2) -> let t1 = parse_arg ~variance ctx lab ~global_attrs t1 in begin match parse_typ ~variance ctx ~global_attrs t2 with | Arrow ({ty_args; ty_vararg = _; unit_arg = _; ty_res = _} as params) when no_attributes t2.ptyp_attributes -> Arrow {params with ty_args = t1 :: ty_args} | tres -> begin match t1 with | {lab=Arg; att=[]; typ=Unit _} -> Arrow {ty_args = []; ty_vararg = None; unit_arg = true; ty_res = tres} | _ -> Arrow {ty_args = [t1]; ty_vararg = None; unit_arg = false; ty_res = tres} end end | Ptyp_constr ({txt = lid; loc = _}, tl) -> begin match String.concat "." (Longident.flatten_exn lid), tl with | "unit", [] -> Unit ty.ptyp_loc | "Ojs.t", [] -> Js | s, tl -> Name (s, List.map (parse_typ ~variance ctx ~global_attrs) tl) end | Ptyp_variant (rows, Closed, None) -> let location = ty.ptyp_loc in let prepare_row = function | {prf_desc = Rtag ({txt = mlconstr; _}, true, []); prf_attributes = attributes; prf_loc = location} -> { mlconstr; arg = Constant; attributes; location } | {prf_desc = Rtag ({txt = mlconstr; _}, false, [typ]); prf_attributes = attributes; prf_loc = location} -> begin match parse_typ ~variance ctx ~global_attrs typ with | Tuple typs -> { mlconstr; arg = Nary typs; attributes; location } | typ -> { mlconstr; arg = Unary typ; attributes; location } end | _ -> error location Cannot_parse_type in Variant {location; global_attrs; attributes = ty.ptyp_attributes; constrs = List.map prepare_row rows} | Ptyp_tuple typs -> let typs = List.map (parse_typ ~variance ctx ~global_attrs) typs in Tuple typs | Ptyp_var label -> if List.mem label ctx.type_params then if variance < 0 then error ty.ptyp_loc (Contravariant_type_parameter label) else Name (local_type_of_type_var label, []) else begin match List.assoc_opt label ctx.packages with | Some module_name -> Packaged_type { local_name = label; module_name } | None -> Typ_var label end | _ -> error ty.ptyp_loc Cannot_parse_type let parse_typ = parse_typ ~variance:0 let check_prefix ~prefix s = let l = String.length prefix in if l <= String.length s && String.sub s 0 l = prefix then Some (String.sub s l (String.length s - l)) else None let has_prefix ~prefix s = check_prefix ~prefix s <> None let drop_prefix ~prefix s = match check_prefix ~prefix s with | Some x -> x | None -> assert false let check_suffix ~suffix s = let l = String.length suffix in if l <= String.length s && String.sub s (String.length s - l) l = suffix then Some (String.sub s 0 (String.length s - l)) else None let rec choose f = function | [] -> [] | x :: xs -> begin match f x with | None -> choose f xs | Some y -> y :: choose f xs end let derived_from_type s ty = match ty with | Arrow {ty_args; ty_vararg = None; unit_arg = false; ty_res = Js} -> begin match List.rev ty_args with | {lab=Arg; att=_; typ=Name (t, _);} :: _ -> check_suffix ~suffix:"_to_js" s = Some t | _ -> false end | Arrow {ty_res = Name (t, _); ty_vararg = None; unit_arg = false; ty_args } -> begin match List.rev ty_args with | {lab=Arg; att=_; typ= Js;} :: _ -> check_suffix ~suffix:"_of_js" s = Some t | _ -> false end | _ -> false let auto ~global_attrs s ty = if derived_from_type s ty then Ignore else Auto begin match ty with | Arrow {ty_args = _; ty_vararg = None; unit_arg = _; ty_res = Name _} when s = "create" -> New None | Arrow {ty_args = _; ty_vararg = None; unit_arg = _; ty_res = Name _} when has_prefix ~prefix:"new_" s -> New (Some (js_name ~capitalize:true ~global_attrs (drop_prefix ~prefix:"new_" s))) | Arrow {ty_args = [_]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when has_prefix ~prefix:"set_" s -> PropSet (js_name ~global_attrs (drop_prefix ~prefix:"set_" s)) | Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}; _; _]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when s = "set" -> IndexSet | Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}; _]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when has_prefix ~prefix:"set_" s -> PropSet (js_name ~global_attrs (drop_prefix ~prefix:"set_" s)) | Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} -> MethCall (js_name ~global_attrs s) | Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}; _]; ty_vararg = None; unit_arg = false; ty_res = _} when s = "get" -> IndexGet | Arrow {ty_args = [{lab=Arg; att=_; typ=Name _}]; ty_vararg = None; unit_arg = false; ty_res = _} -> PropGet (js_name ~global_attrs s) | Arrow {ty_args = []; ty_vararg = None; unit_arg = true; ty_res = _} -> PropGet (js_name ~global_attrs s) | Arrow {ty_args = {lab=Arg; att=_; typ=Name _} :: _; ty_vararg = _; unit_arg = _; ty_res = _} when s = "apply" -> Apply Function | Arrow {ty_args = {lab=Arg; att=_; typ=Name _} :: _; ty_vararg = _; unit_arg = _; ty_res = _} -> MethCall (js_name ~global_attrs s) | _ -> Global (js_name ~global_attrs s) end let auto_in_object ~global_attrs s typ = Auto begin match typ with | Arrow {ty_args = [{lab=Arg; att=_; typ=_}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when has_prefix ~prefix:"set_" s -> PropSet (js_name ~global_attrs (drop_prefix ~prefix:"set_" s)) | Arrow {ty_args = [_]; ty_vararg = None; unit_arg = _; ty_res = _} when s = "get" -> IndexGet | Arrow {ty_args = [_; _]; ty_vararg = None; unit_arg = false; ty_res = Unit _} when s = "set" -> IndexSet | Arrow _ when s = "apply" -> Apply Function | Arrow _ -> MethCall (js_name ~global_attrs s) | Unit _ -> MethCall (js_name ~global_attrs s) | _ -> PropGet (js_name ~global_attrs s) end let parse_attr ~global_attrs (s, loc, auto) attribute = let opt_name ?(prefix = "") ?(capitalize = false) () = match attribute.attr_payload with | PStr [] -> begin match check_prefix ~prefix s with | None | Some "" -> error loc (Implicit_name prefix) | Some s -> js_name ~global_attrs ~capitalize s end | _ -> id_of_expr (expr_of_payload attribute) in let actions = [ "js.cast", (fun () -> Cast); "js.get", (fun () -> PropGet (opt_name ())); "js.set", (fun () -> PropSet (opt_name ~prefix:"set_" ())); "js.index_get", (fun () -> IndexGet); "js.index_set", (fun () -> IndexSet); "js.call", (fun () -> MethCall (opt_name ())); "js.apply", (fun () -> Apply Function); "js.apply_newable", (fun () -> Apply NewableFunction); "js.invoke", (fun () -> Invoke); "js.global", (fun () -> Global (opt_name ())); "js", (fun () -> auto ()); "js.create", (fun () -> New None); "js.new", (fun () -> New (Some (opt_name ~prefix:"new_" ~capitalize:true ()))); "js.builder", (fun () -> Builder global_attrs); ] in match List.find (fun (name, _) -> filter_attr_name name attribute) actions with | exception Not_found -> None | _, f -> Some (f ()) let extract_packages_for_valdecl ty = let is_ojs_T (ojs_T: Longident.t) (t: Longident.t) = match Longident.flatten_exn ojs_T, Longident.flatten_exn t with | ["Ojs"; "T"], ["t"] -> true | _ -> false in let rec go acc ty = match ty.ptyp_desc with | Ptyp_arrow (Nolabel, { ptyp_desc = Ptyp_package ({ txt = ojs_T; _ }, [{ txt = t; _}, { ptyp_desc = Ptyp_var tyvar; _ }]); ptyp_attributes; _ }, rest) when is_ojs_T ojs_T t && has_attribute "js" ptyp_attributes -> let module_name = String.mapi (fun i c -> if i = 0 then Char.uppercase_ascii c else c) tyvar in go ((tyvar, module_name) :: acc) rest | _ -> ty, acc in go [] ty let parse_valdecl ~global_attrs ~in_sig vd = let attributes = vd.pval_attributes in let global_attrs = attributes @ global_attrs in let s = vd.pval_name.txt in let loc = vd.pval_loc in let ty, packages = extract_packages_for_valdecl vd.pval_type in let ty = parse_typ { empty_type_context with packages } ~global_attrs ty in let auto () = auto ~global_attrs s ty in let defs = choose (parse_attr ~global_attrs (s, loc, auto)) attributes in let r = match defs with | [x] -> x | [] when in_sig -> auto () | [] -> raise Exit | _ -> error loc Multiple_binding_declarations in Val { name = s; ty; decl = r; packages; loc; global_attrs } let rec functor_of_module_type = function | {pmty_desc = Pmty_signature si; pmty_attributes; _} -> Some ([], si, pmty_attributes) | {pmty_desc = Pmty_functor (params, body); _} -> begin match functor_of_module_type body with | Some (parameters, si, attrs) -> Some (params :: parameters, si, attrs) | None -> None end | _ -> None let rec parse_sig_item ~global_attrs rest s = let parse_module_declaration = function | {pmd_name = { txt = Some name; _}; pmd_type; pmd_loc = _; pmd_attributes} -> begin match functor_of_module_type pmd_type with | None -> error s.psig_loc Cannot_parse_sigitem | Some (functor_parameters, si, attrs) -> let global_attrs = push_module_attributes name attrs (push_module_attributes name pmd_attributes global_attrs) in (functor_parameters, name, parse_sig ~global_attrs si) end | _ -> error s.psig_loc Cannot_parse_sigitem in match s.psig_desc with | Psig_value vd when vd.pval_prim = [] -> parse_valdecl ~global_attrs ~in_sig:true vd :: rest ~global_attrs | Psig_type (rec_flag, decls) -> Type (rec_flag, decls, global_attrs) :: rest ~global_attrs | Psig_module {pmd_name = {txt = Some name; _}; pmd_type = {pmty_desc = Pmty_alias lid; _}; _} -> ModuleAlias (name, lid) :: rest ~global_attrs | Psig_module md -> let functor_parameters, name, decls = parse_module_declaration md in Module (functor_parameters, name, decls) :: rest ~global_attrs | Psig_recmodule mds -> let mapper md = let functor_parameters, name, decls = parse_module_declaration md in let module_type = md.pmd_type in (module_type, functor_parameters, name, decls) in RecModule (List.map mapper mds) :: rest ~global_attrs | Psig_class cs -> Class (List.map (parse_class_decl ~global_attrs) cs) :: rest ~global_attrs | Psig_attribute ({attr_payload = PStr str; _} as attribute) when filter_attr_name "js.implem" attribute -> Implem str :: rest ~global_attrs | Psig_attribute attribute -> let global_attrs = attribute :: global_attrs in rest ~global_attrs | Psig_open descr -> Open descr :: rest ~global_attrs | Psig_include ({pincl_mod; _} as info) -> let rec module_expr mod_typ = match mod_typ.pmty_desc with | Pmty_typeof module_expr -> module_expr | Pmty_with (t, _) -> module_expr t | _ -> error s.psig_loc Cannot_parse_sigitem in Include {info with pincl_mod = module_expr pincl_mod} :: rest ~global_attrs | _ -> error s.psig_loc Cannot_parse_sigitem and push_module_attributes module_name module_attributes global_attrs = let rec rev_append acc = function | ({attr_name = {txt = "js.scope"; _}; attr_payload = PStr []; _}) as attribute :: tl -> rev_append ({ attribute with attr_payload = PStr [Str.eval (Exp.constant (Pconst_string (module_name, Location.none, None)))] } :: acc) tl | hd :: tl -> rev_append (hd :: acc) tl | [] -> acc in rev_append global_attrs (List.rev module_attributes) and parse_sig ~global_attrs = function | [] -> [] | {psig_desc = Psig_attribute attribute; _} :: rest when filter_attr_name "js.stop" attribute -> parse_sig_verbatim ~global_attrs rest | {psig_desc = Psig_value vd; _} :: rest when has_attribute "js.custom" vd.pval_attributes -> let attribute = unoption (get_attribute "js.custom" vd.pval_attributes) in let str = str_of_payload attribute in Implem str :: parse_sig ~global_attrs rest | s :: rest -> parse_sig_item ~global_attrs (parse_sig rest) s and parse_sig_verbatim ~global_attrs = function | [] -> [] | {psig_desc = Psig_attribute attribute; _} :: rest when filter_attr_name "js.start" attribute -> parse_sig ~global_attrs rest | _ :: rest -> parse_sig_verbatim ~global_attrs rest and parse_class_decl ~global_attrs = function | {pci_virt = Concrete; pci_params = []; pci_name; pci_expr = {pcty_desc = Pcty_arrow (Nolabel, {ptyp_desc = Ptyp_constr ({txt = Longident.Ldot (Lident "Ojs", "t"); loc = _}, []); ptyp_loc = _; ptyp_attributes = _; ptyp_loc_stack = _}, {pcty_desc = Pcty_signature {pcsig_self = {ptyp_desc = Ptyp_any; _}; pcsig_fields}; pcty_loc = _; pcty_attributes = _}); _}; pci_attributes; pci_loc = _} -> let global_attrs = pci_attributes @ global_attrs in let class_name = pci_name.txt in Declaration { class_name; class_fields = List.map (parse_class_field ~global_attrs) pcsig_fields } | {pci_virt = Concrete; pci_params = []; pci_name; pci_expr; pci_attributes; pci_loc} -> let global_attrs = pci_attributes @ global_attrs in let rec convert_typ = function | { pcty_desc = Pcty_constr (id, typs); pcty_attributes; pcty_loc } -> Typ.constr ~loc:pcty_loc ~attrs:pcty_attributes id typs | { pcty_desc = Pcty_arrow (label, typ, ct); pcty_attributes; pcty_loc } -> Typ.arrow ~loc:pcty_loc ~attrs:pcty_attributes label typ (convert_typ ct) | _ -> error pci_loc Cannot_parse_classdecl in let class_arrow = match parse_typ empty_type_context ~global_attrs (convert_typ pci_expr) with | Arrow ({ty_args = _; ty_vararg = _; unit_arg = _; ty_res = Name (_, [])} as params) -> params | (Name (_, []) as ty_res) -> {ty_args = []; ty_vararg = None; unit_arg = false; ty_res} | _ -> error pci_loc Cannot_parse_classdecl in let class_name = pci_name.txt in let js_class_name = match get_string_attribute "js.new" pci_attributes with | None -> js_name ~global_attrs ~capitalize:true class_name | Some s -> s in Constructor {class_name; js_class_name; class_arrow} | {pci_loc; _} -> error pci_loc Cannot_parse_classdecl and parse_class_field ~global_attrs = function | {pctf_desc = Pctf_method ({txt = method_name; _}, Public, Concrete, typ); pctf_loc; pctf_attributes} -> let ty = parse_typ empty_type_context ~global_attrs typ in let auto () = auto_in_object ~global_attrs method_name ty in let defs = choose (parse_attr ~global_attrs (method_name, pctf_loc, auto)) pctf_attributes in let kind = match defs with | [x] -> x | [] -> auto () | _ -> error pctf_loc Multiple_binding_declarations in let rec method_def = function | Auto def -> method_def def | PropGet s -> Getter s | PropSet s -> Setter s | IndexGet -> IndexGetter | IndexSet -> IndexSetter | MethCall s -> MethodCall s | Apply t -> ApplyAsFunction t | _ -> error pctf_loc Cannot_parse_classfield in let method_attrs = match kind with | Auto _ -> [ auto_deprecation_attribute pctf_loc kind ] | _ -> [] in Method { method_name; method_typ = ty; method_def = method_def kind; method_loc = pctf_loc; method_attrs; } | {pctf_desc = Pctf_inherit {pcty_desc = Pcty_constr (id, []); _}; _} -> Inherit id | {pctf_loc; _} -> error pctf_loc Cannot_parse_classfield (** Code generation *) let longident_parse x = Longident.parse x [@@ocaml.alert "-deprecated"] let var x = Exp.ident (mknoloc (longident_parse x)) let str s = Exp.constant (Pconst_string (s, Location.none, None)) let int_of_repr n = Exp.constant (Pconst_integer (n, None)) let int n = int_of_repr (string_of_int n) let float_of_repr f = Exp.constant (Pconst_float (f, None)) let bool b = Exp.construct (mknoloc (longident_parse (if b then "true" else "false"))) None let pat_int n = Pat.constant (Pconst_integer (n, None)) let pat_float f = Pat.constant (Pconst_float (f, None)) let pat_str s = Pat.constant (Pconst_string (s, Location.none, None)) let pat_bool b = Pat.construct (mknoloc (longident_parse (if b then "true" else "false"))) None let attr s e = (Attr.mk (mknoloc s) (PStr [Str.eval e])) let disable_warnings = Str.attribute (attr "ocaml.warning" (str "-7-32-39")) (* 7: method overridden. 32: unused value declarations (when *_of_js, *_to_js are not needed) 39: unused rec flag (for *_of_js, *_to_js functions, when the type is not actually recursive) *) let incl = function | [x] -> x | str -> Str.include_ (Incl.mk (Mod.structure str)) let nolabel args = List.map (function x -> Nolabel, x) args let ojs_typ = Typ.constr (mknoloc (longident_parse "Ojs.t")) [] let ojs_var s = Exp.ident (mknoloc (Ldot (Lident "Ojs", s))) let ojs s args = Exp.apply (ojs_var s) (nolabel args) let ojs_null = ojs_var "null" let list_iter f x = Exp.apply (Exp.ident (mknoloc (longident_parse "List.iter"))) (nolabel [f; x]) let fun_ ?(eta = true) (label, s, typ) e = match e.pexp_desc with | Pexp_apply (f, [Nolabel, {pexp_desc = Pexp_ident {txt = Lident x; loc = _}; _}]) when x = s && eta -> f | _ -> Ast_builder.Default.pexp_fun ~loc:Location.none label None (Pat.constraint_ (Pat.var (mknoloc s)) typ) e let fun_unit e = match e.pexp_desc with | Pexp_apply (f, [Nolabel, {pexp_desc = Pexp_construct ({txt = Lident "()"; loc = _}, None); _}]) -> f | _ -> Ast_builder.Default.pexp_fun ~loc:Location.none Nolabel None (Pat.construct (mknoloc (Lident "()")) None) e let func args unit_arg body = let body = if unit_arg then fun_unit body else body in List.fold_right (fun s rest -> fun_ s rest) args body let uid = ref 0 let fresh () = incr uid; Printf.sprintf "x%i" !uid let apply f args = Exp.apply f args let unit_lid = mknoloc (Lident "()") let unit_expr = Exp.construct unit_lid None let unit_pat = Pat.construct unit_lid None let some_pat arg = Pat.construct (mknoloc (longident_parse "Some")) (Some arg) let none_pat () = Pat.construct (mknoloc (longident_parse "None")) None let match_some_none ~some ~none exp = let s = fresh () in Exp.match_ exp [ Exp.case (some_pat (Pat.var (mknoloc s))) (some (var s)); Exp.case (none_pat ()) none; ] let app f args unit_arg = let args = if unit_arg then args @ [Nolabel, unit_expr] else args in apply f args let exp_ignore res = apply (var "ignore") [ Nolabel, res ] let split sep s = let n = String.length s in let rec aux start i = if i < n then if s.[i] = sep then String.sub s start (i - start) :: aux (i+1) (i+1) else aux start (i+1) else [String.sub s start (i - start)] in aux 0 0 let ojs_global = ojs_var "global" let ojs_get o s = if is_ascii s then ojs "get_prop_ascii" [o; str s] else ojs "get_prop" [o; ojs "string_to_js" [str s]] let ojs_set o s v = if is_ascii s then ojs "set_prop_ascii" [o; str s; v] else ojs "set_prop" [o; ojs "string_to_js" [str s]; v] let split_at s = if String.length s > 0 && s.[0] = '@' then Some (String.sub s 1 (String.length s - 1)) else None let runtime s = let runtime_value = Exp.ident (mknoloc (longident_parse "Jsoo_runtime.Js.runtime_value")) in Exp.apply runtime_value (nolabel [Exp.constant (Pconst_string (s, Location.none, None))]) let rec select_split_path o = function | [] -> assert false | (hd :: tl) as l -> let o, l = match split_at hd with | Some s -> runtime s, tl | None -> o, l in match l with | [] -> o, None | [x] -> o, Some x | hd :: tl -> select_split_path (ojs_get o hd) tl let select_path o s = select_split_path o (split '.' s) let get_path global_object s = let o, x = select_path global_object s in match x with | None -> o | Some x -> ojs_get o x let ojs_variable s = get_path ojs_global s let set_path ~loc global_object s v = let o, x = select_path global_object s in match x with | None -> error loc (Cannot_set_runtime_value s) | Some x -> ojs_set o x v let def ?packages s ty body = let ty, body = match packages with | None | Some [] -> ty, body | Some packages -> (* append module arguments *) let folder1 (ty, body) (local_name, module_name) = let package is_local = let t = if is_local then Typ.constr (mknoloc (Lident local_name)) [] else Typ.var local_name in Typ.package (mknoloc (Ldot (Lident "Ojs", "T"))) [mknoloc (Lident "t"), t] in let ty = Typ.arrow Nolabel (package false) ty in let body = let arg = Pat.constraint_ (Pat.unpack (mknoloc (Some module_name))) (package true) in Ast_builder.Default.pexp_fun ~loc:Location.none Nolabel None arg body in ty, body in (* append locally abstract types *) let folder2 (ty, body) (local_name, _) = ty, Exp.newtype (mknoloc local_name) body in List.fold_left folder2 (List.fold_left folder1 (ty, body) packages) packages in Str.value Nonrecursive [ Vb.mk ~value_constraint:(Pvc_constraint { locally_abstract_univars = []; typ = ty}) (Pat.var (mknoloc s)) body ] let builtin_type = function | "int" | "string" | "bool" | "float" | "array" | "list" | "option" -> true | _ -> false let let_exp_in exp f = let x = fresh () in let pat = Pat.var (mknoloc x) in Exp.let_ Nonrecursive [Vb.mk pat exp] (f (var x)) let ojs_apply_arr o = function | `Simple arr -> ojs "apply" [o; arr] | `Push arr -> ojs "call" [o; str "apply"; Exp.array [ ojs_null; arr ]] let ojs_call_arr o s meth = match s, meth with | None, `Simple arr -> ojs "apply" [o; arr] | Some s, `Simple arr -> ojs "call" [o; str s; arr] | None, `Push arr -> let_exp_in o (fun o -> ojs "call" [o; str "apply"; Exp.array [ ojs_null ; arr ]] ) | Some s, `Push arr -> let_exp_in o (fun o -> ojs "call" [ojs_get o s; str "apply"; Exp.array [ o; arr ]] ) let ojs_new_obj_arr cl = function | `Simple arr -> ojs "new_obj" [cl; arr] | `Push arr -> ojs "new_obj_arr" [cl; arr] let assert_false = Exp.assert_ (Exp.construct (mknoloc (longident_parse "false")) None) let clear_attr_mapper = object inherit Ast_traverse.map method! attributes attrs = let f {attr_name = {txt = _; loc}; _} = not (is_registered_loc loc) in List.filter f attrs end let rewrite_typ_decl t = let t = clear_attr_mapper # type_declaration {t with ptype_private = Public} in match t.ptype_manifest, t.ptype_kind with | None, Ptype_abstract -> {t with ptype_manifest = Some ojs_typ} | _ -> t let string_typ = Name ("string", []) let int_typ = Name ("int", []) let bool_typ = Name ("bool", []) let float_typ = Name ("float", []) let is_discriminator_type = function | Name (("string"|"int"|"float"|"bool"), []) -> true | _ -> false let is_simple_enum params = let p {mlconstr = _; arg; attributes; location = _} = match arg with | Constant -> true | Unary arg_typ when is_discriminator_type arg_typ -> has_attribute "js.default" attributes | Unary _ | Nary _ | Record _ -> false in List.for_all p params type union_discriminator = | No_discriminator | On_field of string let get_variant_kind loc attrs = if has_attribute "js.enum" attrs then `Enum else if has_attribute "js.union" attrs then begin match get_attribute "js.union" attrs with | None -> assert false | Some attribute -> begin match attribute.attr_payload with | PStr [] -> `Union No_discriminator | _ -> begin match expr_of_payload attribute with | {pexp_desc = Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "on_field";_}; _}, [Nolabel, {pexp_desc = Pexp_constant (Pconst_string (s, _, _)); _}]); _} -> `Union (On_field s) | _ -> error attribute.attr_loc Unknown_union_method end end end else if has_attribute "js.sum" attrs then begin match get_attribute "js.sum" attrs with | None -> assert false | Some attribute -> begin match attribute.attr_payload with | PStr [] -> `Sum "kind" | _ -> `Sum (id_of_expr (expr_of_payload attribute)) end end else error loc (Not_supported_here "Sum types without js.* attribute") type variant_cases = { int_default: case option; int_cases: float case_value list; float_default: case option; float_cases: float case_value list; string_default: case option; string_cases: string case_value list; bool_default: case option; bool_cases: bool case_value list; } and 'a case_value = { value: 'a; case: case; loc: location; } let case_value ~loc ~value pat x = let case = Exp.case pat x in { value; case; loc } let empty_variant_cases = { int_default = None; int_cases = []; float_default = None; float_cases = []; string_default = None; string_cases = []; bool_default = None; bool_cases = [] } let rec js2ml ty exp = match ty with | Js -> exp | Name (s, tl) -> let s = if builtin_type s then "Ojs." ^ s else s in let args = List.map (js2ml_fun ~eta:true) tl in app (var (s ^ "_of_js")) (nolabel (args @ [exp])) false | Arrow {ty_args; ty_vararg; unit_arg; ty_res} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res = ojs_apply_arr exp concrete_args in func formal_args unit_arg (js2ml_unit ty_res res) | Unit _ -> app (var "Ojs.unit_of_js") (nolabel [exp]) false | Variant {location; global_attrs; attributes; constrs} -> js2ml_of_variant ~variant:true location ~global_attrs attributes constrs exp | Tuple typs -> let f x = Exp.tuple (List.mapi (fun i typ -> js2ml typ (ojs "array_get" [x; int i])) typs) in let_exp_in exp f | Typ_var _ -> app (var ("Obj.magic")) (nolabel ([exp])) false | Packaged_type { module_name; _ } -> app (var (module_name ^ ".t_of_js")) (nolabel [exp]) false and js2ml_of_variant ~variant loc ~global_attrs attrs constrs exp = let variant_kind = get_variant_kind loc attrs in let check_label = match variant_kind with | `Sum kind -> (fun loc label -> if label = kind then error loc Sum_kind_args) | _ -> (fun _ _ -> ()) in let mkval = if variant then fun x arg -> Exp.variant x arg else fun x arg -> Exp.construct (mknoloc (Longident.Lident x)) arg in let f exp = let gen_cases (cases: variant_cases) {mlconstr; arg; attributes; location} = let case x = let loc = location in match get_js_constr ~global_attrs mlconstr attributes with | `String s -> { cases with string_cases = case_value ~loc ~value:s (pat_str s) x :: cases.string_cases } | `Int n -> { cases with int_cases = case_value ~loc ~value:(float_of_string n) (pat_int n) x :: cases.int_cases } | `Float f -> { cases with float_cases = case_value ~loc ~value:(float_of_string f) (pat_float f) x :: cases.float_cases } | `Bool b -> { cases with bool_cases = case_value ~loc ~value:b (pat_bool b) x :: cases.bool_cases } in let get_arg key typ = js2ml typ (ojs_get exp key) in match arg with | Constant -> case (mkval mlconstr None) | Unary arg_typ -> let otherwise() = match variant_kind with | `Enum -> error location Non_constant_constructor_in_enum | `Sum _ -> let loc, arg_field = get_string_attribute_default "js.arg" (location, "arg") attributes in check_label loc arg_field; case (mkval mlconstr (Some (get_arg arg_field arg_typ))) | `Union _ -> case (mkval mlconstr (Some (js2ml arg_typ exp))) in let process_default defs cont = match get_attribute "js.default" attributes with | None -> otherwise() | Some attribute -> if List.for_all ((=) None) defs then begin match variant_kind with | `Enum -> let x = fresh() in cont (Some (Exp.case (Pat.var (mknoloc x)) (mkval mlconstr (Some (var x))))) | `Sum _ | `Union _ -> cont (Some (Exp.case (Pat.any ()) (mkval mlconstr (Some (js2ml arg_typ exp))))) end else error attribute.attr_loc Multiple_default_case in begin match variant_kind with | `Enum when arg_typ = int_typ -> process_default [cases.int_default] (fun int_default -> { cases with int_default }) | `Enum when arg_typ = string_typ -> process_default [cases.string_default] (fun string_default -> { cases with string_default }) | `Enum when arg_typ = bool_typ -> process_default [cases.bool_default] (fun bool_default -> { cases with bool_default }) | `Enum when arg_typ = float_typ -> process_default [cases.float_default] (fun float_default -> { cases with float_default }) | `Sum _ | `Union _ when arg_typ = Js -> process_default [cases.int_default; cases.float_default; cases.string_default; cases.bool_default] (fun default -> { cases with int_default = default; float_default = default; string_default = default; bool_default = default }) | _ -> otherwise() end | Nary args_typ -> begin match variant_kind with | `Enum -> error location Non_constant_constructor_in_enum | `Sum _ -> let loc, args_field = get_string_attribute_default "js.arg" (location, "arg") attributes in check_label loc args_field; let get_args key i typ = js2ml typ (ojs "array_get" [ojs_get exp key; int i]) in case (mkval mlconstr (Some (Exp.tuple (List.mapi (get_args args_field) args_typ)))) | `Union _ -> case (mkval mlconstr (Some (js2ml (Tuple args_typ) exp))) (* treat it as a tuple of the constructor arguments *) end | Record args -> begin match variant_kind with | `Enum -> error location Non_constant_constructor_in_enum | `Sum _ -> case (mkval mlconstr (Some (Exp.record (List.map (fun (loc, mlname, jsname, typ) -> check_label loc jsname; mlname, get_arg jsname typ) args) None))) | `Union _ -> error location Record_constructor_in_union end in let { int_default; int_cases; float_default; float_cases; string_default; string_cases; bool_default; bool_cases; _ } = let cases = List.fold_left gen_cases empty_variant_cases constrs in (* check if there are any duplicate cases of number *) let _ = let { string_cases; float_cases; int_cases; bool_cases; int_default = _; float_default = _; bool_default = _; string_default = _ } = cases in let check_duplicates l = let compare_values x y = Stdlib.compare x.value y.value in let l = List.sort compare_values l in let rec has_dup = function | [] | [ _ ] -> () | x :: ((y :: _) as l) -> if compare_values x y = 0 then error loc (Duplicate_case_value (x.loc, y.loc)) else has_dup l in has_dup l in check_duplicates string_cases; check_duplicates bool_cases; check_duplicates (float_cases @ int_cases); in cases in (* if both `true` and `false` are present, there is no need to generate the default cases for bool values *) let bool_default, generate_fail_pattern_for_bool = if List.exists (fun {value; _} -> value) bool_cases && List.exists (fun {value; _} -> not value) bool_cases then None, false else bool_default, true in let gen_match ~fail_pattern e default other_cases = let other_cases = List.map (fun {case;_} -> case) other_cases in match default, other_cases with | None, [] -> None | Some default, [] when default.pc_lhs.ppat_desc = Ppat_any -> Some default.pc_rhs | Some default, _ -> let cases = List.rev (default :: other_cases) in Some (Exp.match_ e cases) | None, _ :: _ -> let cases = if fail_pattern then (Exp.case (Pat.any ()) assert_false) :: other_cases else other_cases in Some (Exp.match_ e (List.rev cases)) in let discriminator = match variant_kind with | `Enum -> exp | `Sum kind -> ojs_get exp kind | `Union No_discriminator -> error loc Union_without_discriminator | `Union (On_field kind) -> ojs_get exp kind in let number_match = let default_expr exprOpt = Option.map (fun expr -> Exp.case (Pat.any ()) expr) exprOpt in let get_int_match int_default = gen_match ~fail_pattern:true (js2ml int_typ discriminator) int_default int_cases in let get_float_match float_default = gen_match ~fail_pattern:true (js2ml float_typ discriminator) float_default float_cases in let int_match = get_int_match int_default in let float_match = get_float_match float_default in match int_match, float_match with | Some m, None | None, Some m -> Some m | None, None -> None | Some _, Some _ -> match int_default, float_default with | _, None -> get_float_match (default_expr int_match) | None, Some d -> let case = match get_int_match (default_expr (Some d.pc_rhs)) with | None -> d | Some int_match -> { d with pc_rhs = int_match } in get_float_match (Some case) | Some d1, Some d2 -> if d1 = d2 then get_float_match (default_expr int_match) else error loc Multiple_default_case in let string_match = gen_match ~fail_pattern:true (js2ml string_typ discriminator) string_default string_cases in let bool_match = gen_match ~fail_pattern:generate_fail_pattern_for_bool (js2ml bool_typ discriminator) bool_default bool_cases in match number_match, string_match, bool_match with | None, None, None -> assert false | Some number_match, None, None -> number_match | None, Some string_match, None -> string_match | None, None, Some bool_match -> bool_match | _ -> let case_number = Option.map (Exp.case (pat_str "number")) number_match in let case_string = Option.map (Exp.case (pat_str "string")) string_match in let case_bool = Option.map (Exp.case (pat_str "boolean")) bool_match in let case_default = match variant_kind, int_default, float_default, string_default, bool_default with | `Enum, _, _, _, _ | _, None, None, None, None -> Exp.case (Pat.any ()) assert_false | (`Sum _ | `Union _), _, _, _, _ -> let defaults = List.filter_map Fun.id [int_default; float_default; string_default; bool_default] in match defaults with | def :: rest when List.for_all ((=) def) rest -> def | _ -> assert false in let cases = List.fold_left (fun state -> function Some x -> x :: state | None -> state) [case_default] [case_bool; case_string; case_number] in Exp.match_ (ojs "type_of" [discriminator]) cases in let_exp_in exp f and ml2js ty exp = match ty with | Js -> exp | Name (s, tl) -> let s = if builtin_type s then "Ojs." ^ s else s in let args = List.map (ml2js_fun ~eta:true) tl in app (var (s ^ "_to_js")) (nolabel (args @ [exp])) false | Arrow {ty_args; ty_vararg = None; unit_arg; ty_res} -> let args = let f _i {lab; att=_; typ} = let s = fresh() in let typ = match lab with | Arg | Lab _ -> typ | Opt _ -> Name ("option", [typ]) in (s, gen_typ typ), (arg_label lab, js2ml typ (var s)) in List.mapi f ty_args in let formal_args, concrete_args = List.map fst args, List.map snd args in let res = ml2js_unit ty_res (app exp concrete_args unit_arg) in let body = if formal_args = [] then Ast_builder.Default.pexp_fun ~loc:Location.none Nolabel None (Pat.any ()) res else res in let f = List.fold_right (fun (s, _) -> fun_ (Nolabel, s, ojs_typ)) formal_args body in ojs "fun_to_js" [int (max 1 (List.length formal_args)); f] | Arrow {ty_args; ty_vararg = Some {lab=label_variadic; att=_; typ=ty_variadic}; unit_arg; ty_res} -> let arguments = fresh() in let n_args = List.length ty_args in let concrete_args = List.mapi (fun i {lab; att=_; typ} -> arg_label lab, js2ml typ (ojs "array_get" [var arguments; int i])) ty_args in let extra_arg = ojs "list_of_js_from" [ js2ml_fun ty_variadic; var arguments; int n_args ] in let extra_arg = match label_variadic with | Arg | Lab _ -> extra_arg | Opt _ -> Exp.construct (mknoloc (longident_parse "Some")) (Some extra_arg) in let concrete_args = concrete_args @ [arg_label label_variadic, extra_arg] in let res = app exp concrete_args unit_arg in let f = func [Nolabel, arguments, Typ.any ()] false (ml2js_unit ty_res res) in ojs "fun_to_js_args" [f] | Unit _ -> app (var "Ojs.unit_to_js") (nolabel [exp]) false | Variant {location; global_attrs; attributes; constrs} -> ml2js_of_variant ~variant:true location ~global_attrs attributes constrs exp | Tuple typs -> let typed_vars = List.mapi (fun i typ -> i, typ, fresh ()) typs in let pat = Pat.tuple (List.map (function (_, _, x) -> Pat.var (mknoloc x)) typed_vars) in Exp.let_ Nonrecursive [Vb.mk pat exp] begin let n = List.length typs in let a = fresh () in let new_array = ojs "array_make" [int n] in Exp.let_ Nonrecursive [Vb.mk (Pat.var (mknoloc a)) new_array] begin let f e (i, typ, x) = Exp.sequence (ojs "array_set" [var a; int i; ml2js typ (var x)]) e in List.fold_left f (var a) (List.rev typed_vars) end end | Typ_var _ -> app (var ("Obj.magic")) (nolabel ([exp])) false | Packaged_type { module_name; _ } -> app (var (module_name ^ ".t_to_js")) (nolabel [exp]) false and ml2js_discriminator ~global_attrs mlconstr attributes = match get_js_constr ~global_attrs mlconstr attributes with | `Int n -> ml2js int_typ (int_of_repr n) | `Float f -> ml2js float_typ (float_of_repr f) | `String s -> ml2js string_typ (str s) | `Bool b -> ml2js bool_typ (bool b) and ml2js_of_variant ~variant loc ~global_attrs attrs constrs exp = let variant_kind = get_variant_kind loc attrs in let check_label = match variant_kind with | `Sum kind -> (fun loc label -> if label = kind then error loc Sum_kind_args) | _ -> (fun _ _ -> ()) in let mkpat = if variant then fun x arg -> Pat.variant x arg else fun x arg -> Pat.construct (mknoloc (Longident.Lident x)) arg in let pair key typ value = Exp.tuple [str key; ml2js typ value] in let case {mlconstr; arg; attributes; location} = let mkobj args = let discriminator = ml2js_discriminator ~global_attrs mlconstr attributes in match variant_kind, args with | `Enum, [] -> discriminator | `Enum, _ :: _ -> error location Non_constant_constructor_in_enum | `Sum kind, _ -> ojs "obj" [Exp.array ((Exp.tuple [str kind; discriminator]) :: args)] | `Union _, [] -> ojs_null | `Union _, _ :: _ -> error location Record_constructor_in_union in match arg with | Constant -> Exp.case (mkpat mlconstr None) (mkobj []) | Unary arg_typ -> let x = fresh() in let value = match variant_kind with | `Enum when is_discriminator_type arg_typ && has_attribute "js.default" attributes -> ml2js arg_typ (var x) | `Enum | `Sum _ -> let loc, arg_field = get_string_attribute_default "js.arg" (location, "arg") attributes in check_label loc arg_field; mkobj [pair arg_field arg_typ (var x)] | `Union _ -> ml2js arg_typ (var x) in Exp.case (mkpat mlconstr (Some (Pat.var (mknoloc x)))) value | Nary args_typ -> begin match variant_kind with | `Enum | `Sum _ -> let loc, args_field = get_string_attribute_default "js.arg" (location, "arg") attributes in check_label loc args_field; let xis = List.mapi (fun i typ -> i, typ, fresh()) args_typ in let n_args = List.length xis in Exp.case (mkpat mlconstr (Some (Pat.tuple (List.map (fun (_, _, xi) -> Pat.var (mknoloc xi)) xis)))) (let args = fresh() in Exp.let_ Nonrecursive [Vb.mk (Pat.var (mknoloc args)) (ojs "array_make" [int n_args])] (List.fold_left (fun e (i, typi, xi) -> Exp.sequence (ojs "array_set" [var args; int i; ml2js typi (var xi)]) e) (mkobj [pair args_field Js (var args)]) xis)) | `Union _ -> (* treat it as a tuple of the constructor arguments *) let x = fresh() in Exp.case (mkpat mlconstr (Some (Pat.var (mknoloc x)))) (ml2js (Tuple args_typ) (var x)) end | Record args -> let x = fresh() in Exp.case (mkpat mlconstr (Some (Pat.var (mknoloc x)))) (mkobj (List.map (fun (loc, mlname, jsname, typ) -> check_label loc jsname; pair jsname typ (Exp.field (var x) mlname)) args)) in Exp.match_ exp (List.map case constrs) and js2ml_fun ?eta ty = mkfun ?eta ~typ:Js (js2ml ty) and ml2js_fun ?eta ty = mkfun ?eta ~typ:ty (ml2js ty) and prepare_args ty_args ty_vararg : (arg_label * label * _) list * [ `Push of expression | `Simple of expression ] = if ty_vararg = None && List.for_all (function | {lab = Opt {def = None; _}; _} -> false | {typ = Variant {location = _; global_attrs = _; attributes; constrs}; _} when has_attribute "js.enum" attributes -> is_simple_enum constrs | {lab = Arg | Lab _ | Opt {def = Some _; _}; _} -> true ) ty_args then let x,y = prepare_args_simple ty_args in x, `Simple y else let x, y = prepare_args_push ty_args ty_vararg in x, `Push y and prepare_args_simple ty_args = let f {lab; att=_; typ} = let s = fresh () in let e, typ = match lab with | Arg | Lab _ -> ml2js typ (var s), typ | Opt {def; _} -> begin match def with | None -> assert false | Some none -> ml2js typ (match_some_none ~none ~some:(fun v -> v) (var s)), Name ("option", [typ]) end in (arg_label lab, s, gen_typ typ), e in let formal_args, concrete = List.split (List.map f ty_args) in let concrete_args = Exp.array concrete in formal_args, concrete_args and prepare_args_push ty_args ty_vararg = let push arr typ x = match typ with | Variant {location = _; global_attrs; attributes; constrs} when (has_attribute "js.enum" attributes && not (is_simple_enum constrs)) -> let f {mlconstr; arg; attributes; location = _} = let gen_tuple typs = let xis = List.map (fun typ -> typ, fresh ()) typs in let cargs = match xis with | [] -> None | [_, x] -> Some (Pat.var (mknoloc x)) | _ :: _ :: _ -> Some (Pat.tuple (List.map (fun (_, xi) -> Pat.var (mknoloc xi)) xis)) in let args = ml2js_discriminator ~global_attrs mlconstr attributes :: List.map (fun (typi, xi) -> ml2js typi (var xi)) xis in let e = exp_ignore (ojs "call" [arr; str "push"; Exp.array args]) in Exp.case (Pat.variant mlconstr cargs) e in match arg with | Constant -> gen_tuple [] | Unary typ -> gen_tuple [typ] | Nary typs -> gen_tuple typs | Record _ -> assert false in let cases = List.map f constrs in Exp.match_ (Exp.constraint_ x (gen_typ typ)) cases | typ -> exp_ignore (ojs "call" [arr; str "push"; Exp.array [ml2js typ x]]) in let f {lab; att=_; typ} = let s = fresh () in let arg_typ = match lab with | Arg | Lab _ -> typ | Opt _ -> Name ("option", [typ]) in (arg_label lab, s, gen_typ arg_typ), fun arr -> let s = var s in match lab with | Arg | Lab _ -> push arr typ s | Opt {def; _} -> begin match def with | None -> match_some_none ~none:unit_expr ~some:(fun s -> push arr typ s) s | Some none -> push arr typ (match_some_none ~none ~some:(fun v -> v) s) end in let formal_args, concrete_args = List.split (List.map f ty_args) in let formal_args, concrete_args = match ty_vararg with | None -> formal_args, concrete_args | Some {lab; att=_; typ} -> let arg = fresh () in formal_args @ [arg_label lab, arg, gen_typ (Name ("list", [typ]))], concrete_args @ [fun arr -> let extra_args = list_iter (mkfun ~typ (fun x -> push arr typ x)) in match lab with | Arg | Lab _ -> extra_args (var arg) | Opt _ -> match_some_none ~none:unit_expr ~some:extra_args (var arg) ] in let body arr = List.fold_right (fun code -> Exp.sequence (code arr)) concrete_args arr in formal_args, let_exp_in (ojs "new_obj" [ojs_variable "Array"; Exp.array []]) body and ml2js_unit ty_res res = match ty_res with | Unit _ -> res | _ -> ml2js ty_res res and js2ml_unit ty_res res = match ty_res with | Unit _ -> exp_ignore res | _ -> js2ml ty_res res and gen_typ ?(packaged_type_as_type_var = false) = function | Name (s, tyl) -> Typ.constr (mknoloc (longident_parse s)) (List.map (gen_typ ~packaged_type_as_type_var) tyl) | Js -> ojs_typ | Unit _ -> Typ.constr (mknoloc (Lident "unit")) [] | Arrow {ty_args; ty_vararg; unit_arg; ty_res} -> let tl = match ty_vararg with | None -> ty_args | Some {lab; att; typ} -> ty_args @ [{lab; att; typ=Name ("list", [typ])}] in let tl = if unit_arg then tl @ [{lab=Arg;att=[];typ=Unit none}] else tl in List.fold_right (fun {lab; att=_; typ} t2 -> Typ.arrow (arg_label lab) (gen_typ ~packaged_type_as_type_var typ) t2) tl (gen_typ ~packaged_type_as_type_var ty_res) | Variant {location = _; global_attrs = _; attributes = _; constrs} -> let f {mlconstr; arg; attributes = _; location = _} = let mlconstr = mknoloc mlconstr in match arg with | Constant -> Rf.mk (Rtag (mlconstr, true, [])) | Unary typ -> Rf.mk (Rtag (mlconstr, false, [gen_typ ~packaged_type_as_type_var typ])) | Nary typs -> Rf.mk (Rtag (mlconstr, false, [gen_typ ~packaged_type_as_type_var (Tuple typs)])) | Record _ -> assert false in let rows = List.map f constrs in Typ.variant rows Closed None | Tuple typs -> Typ.tuple (List.map (gen_typ ~packaged_type_as_type_var) typs) | Typ_var label -> Typ.var label | Packaged_type { local_name; _ } -> if packaged_type_as_type_var then Typ.var local_name else Typ.constr (mknoloc (Lident local_name)) [] and mkfun ?typ ?eta f = let s = fresh () in let typ = match typ with | None -> Typ.any () | Some typ -> gen_typ typ in fun_ ?eta (Nolabel, s, typ) (f (var s)) let process_fields ctx ~global_attrs l = let loc = l.pld_name.loc in let mlname = l.pld_name.txt in let attrs = l.pld_attributes in let typ = l.pld_type in let jsname = match get_string_attribute "js" attrs with | None -> js_name ~global_attrs mlname | Some s -> s in loc, mknoloc (Lident mlname), (* OCaml label *) jsname, (* JS name *) parse_typ ctx ~global_attrs typ let global_object ~global_attrs = let rec traverse = function | [] -> ojs_global | hd :: tl -> begin match get_expr_attribute "js.scope" [hd] with | None -> traverse tl | Some {pexp_desc=Pexp_constant (Pconst_string (prop, _, _)); _} -> get_path (traverse tl) prop | Some {pexp_desc=Pexp_tuple path; _} -> let init = traverse tl in let folder state pexp = match pexp.pexp_desc with | Pexp_constant (Pconst_string (prop, _, _)) -> get_path state prop | _ -> pexp (* global object *) in List.fold_left folder init path | Some global_object -> global_object end in traverse global_attrs let rec gen_decls si = List.concat (List.map gen_decl si) and gen_funs ~global_attrs p = let name = p.ptype_name.txt in let decl_attrs = p.ptype_attributes in let global_attrs = global_attrs in let ctx_withloc = List.map (function | {ptyp_desc = Ptyp_any; ptyp_loc = loc; ptyp_attributes = _; ptyp_loc_stack = _}, (NoVariance, _) -> { loc = loc; txt = fresh () } | {ptyp_desc = Ptyp_var label; ptyp_loc = loc; ptyp_attributes = _; ptyp_loc_stack = _}, (NoVariance, _) -> { loc = loc; txt = label } | _ -> error p.ptype_loc Cannot_parse_type ) p.ptype_params in let poly ty = match ctx_withloc with | [] -> ty | l -> Typ.poly l ty in let ctx = List.map (fun lwl -> lwl.txt) ctx_withloc in let full_ctx = { empty_type_context with type_params = ctx } in let loc = p.ptype_loc in let exception Skip_mapping_generation in let local_type = Name (name, List.map (fun txt -> Name (local_type_of_type_var txt, [])) ctx) in let of_js, to_js, custom_funs = match p.ptype_kind with | _ when has_attribute "js.custom" decl_attrs -> begin match get_attribute "js.custom" decl_attrs with | None -> assert false | Some attribute -> match expr_of_payload attribute with | { pexp_desc = Pexp_record ( ( [ { txt = Lident "of_js"; loc = loc_of}, of_js; { txt = Lident "to_js"; loc = loc_to}, to_js ] | [ { txt = Lident "to_js"; loc = loc_to}, to_js; { txt = Lident "of_js"; loc = loc_of}, of_js ] ), None); _} -> let value_binding suffix loc (body: expression) (ty: core_type) = let name = { txt = Printf.sprintf "%s_%s" name suffix; loc} in Vb.mk ~loc ~value_constraint:(Pvc_constraint { locally_abstract_univars = []; typ = ty}) (Pat.var name) body in let ty = gen_typ (Name (name, List.map (fun x -> Typ_var x) ctx)) in let fold_types f base = let ty = List.fold_right (fun tv acc -> Typ.arrow Nolabel (f tv) acc) ctx base in poly ty in let of_js_ty = fold_types (fun tv -> Typ.arrow Nolabel ojs_typ (Typ.var tv)) (Typ.arrow Nolabel ojs_typ ty) in let to_js_ty = fold_types (fun tv -> Typ.arrow Nolabel (Typ.var tv) ojs_typ) (Typ.arrow Nolabel ty ojs_typ) in let vbs = [ value_binding "of_js" loc_of of_js of_js_ty; value_binding "to_js" loc_to to_js to_js_ty; ] in lazy (raise Skip_mapping_generation), lazy (raise Skip_mapping_generation), vbs | { pexp_loc; _ } -> error pexp_loc (Record_expected "{ to_js = ...; of_js = ... }") end | Ptype_abstract -> let ty, eta = match p.ptype_manifest with | None -> Js, true | Some ty -> parse_typ full_ctx ~global_attrs { ty with ptyp_attributes = decl_attrs @ ty.ptyp_attributes }, false in lazy (js2ml_fun ~eta ty), lazy (ml2js_fun ~eta ty), [] | Ptype_variant cstrs -> let prepare_constructor c = let mlconstr = c.pcd_name.txt in let arg = match c.pcd_args with | Pcstr_tuple args -> begin match args with | [] -> Constant | [x] -> Unary (parse_typ full_ctx ~global_attrs x) | _ :: _ :: _ -> Nary (List.map (parse_typ full_ctx ~global_attrs) args) end | Pcstr_record args -> let global_attrs = c.pcd_attributes @ global_attrs in Record (List.map (process_fields full_ctx ~global_attrs) args) in { mlconstr; arg; attributes = c.pcd_attributes; location = c.pcd_loc } in let params = List.map prepare_constructor cstrs in lazy (mkfun ~typ:Js (js2ml_of_variant ~variant:false loc ~global_attrs decl_attrs params)), lazy (mkfun ~typ:local_type (ml2js_of_variant ~variant:false loc ~global_attrs decl_attrs params)), [] | Ptype_record lbls -> let global_attrs = decl_attrs @ global_attrs in let lbls = List.map (process_fields full_ctx ~global_attrs) lbls in let of_js x (_loc, ml, js, ty) = ml, js2ml ty (ojs_get x js) in let to_js x (_loc, ml, js, ty) = Exp.tuple [str js; ml2js ty (Exp.field x ml)] in lazy (mkfun ~typ:Js (fun x -> Exp.record (List.map (of_js x) lbls) None)), lazy (mkfun ~typ:local_type (fun x -> ojs "obj" [Exp.array (List.map (to_js x) lbls)])), [] | _ -> error p.ptype_loc Cannot_parse_type in let force_opt x = try (Some (Lazy.force x)) with Error (_, Union_without_discriminator) | Skip_mapping_generation -> None in let of_js, to_js = force_opt of_js, force_opt to_js in let alpha_of_js typ = Arrow {ty_args = [{lab=Arg; att=[]; typ = Js}]; ty_vararg = None; unit_arg = false; ty_res = typ} in let alpha_to_js typ = Arrow {ty_args = [{lab=Arg; att=[]; typ}]; ty_vararg = None; unit_arg = false; ty_res = Js} in let push_typ f l = List.map (fun label -> f (Typ_var label)) ctx @ l in let push_fun suffix typ body = match body with | None -> None | Some body -> let params = List.concat [ List.map (fun label -> { pparam_loc = loc; pparam_desc = Pparam_newtype ({ label with txt = local_type_of_type_var label.txt})} ) ctx_withloc; List.map (fun label -> let name = (local_type_of_type_var label)^suffix in let label = Name (local_type_of_type_var label, []) in { pparam_loc = loc; pparam_desc = Pparam_val (Nolabel, None, (Pat.constraint_ (Pat.var (mknoloc name)) (gen_typ (typ label))))} ) ctx ] in match params with | [] -> Some body | params -> Some ( Ast_builder.Default.pexp_function ~loc params None (Pfunction_body body)) in let f (name, input_typs, ret_typ, code) = match code with | None -> None | Some code -> Some (Vb.mk ~loc:p.ptype_loc ~value_constraint:( Pvc_constraint { locally_abstract_univars = []; typ = (poly (gen_typ (Arrow { ty_args = (List.map (fun typ -> {lab=Arg; att=[]; typ}) input_typs); ty_vararg = None; unit_arg = false; ty_res = ret_typ })))}) (Pat.var (mknoloc name)) code) in let funs = choose f [ name ^ "_of_js", push_typ alpha_of_js [Js], Name (name, List.map (fun x -> Typ_var x) ctx), push_fun "_of_js" alpha_of_js of_js; name ^ "_to_js", push_typ alpha_to_js [Name (name, List.map (fun x -> Typ_var x) ctx)], Js, push_fun "_to_js" alpha_to_js to_js ] in funs @ custom_funs and gen_decl = function | Type (rec_flag, decls, global_attrs) -> let funs = List.concat (List.map (gen_funs ~global_attrs) decls) in let decls = List.map rewrite_typ_decl decls in [ Str.type_ rec_flag decls; Str.value rec_flag funs ] | Module (functor_parameters, s, decls) -> [ Str.module_ (gen_module functor_parameters s decls) ] | RecModule modules -> [ Str.rec_module (List.map (fun (module_type, functor_parameters, s, decls) -> gen_module ~module_type functor_parameters s decls) modules) ] | ModuleAlias (s, lid) -> [ Str.module_ (Mb.mk (mknoloc (Some s)) (Mod.ident lid)) ] | Val { decl = Ignore; _ } -> [] | Val { name = s; ty; decl; packages; loc; global_attrs } -> let global_object = global_object ~global_attrs in let d = gen_def ~global_object loc decl ty in [ def ~packages s (gen_typ ~packaged_type_as_type_var:true ty) d ] | Class decls -> let cast_funcs = List.concat (List.map gen_class_cast decls) in let classes = List.map (gen_classdecl cast_funcs) decls in [Str.class_ classes; Str.value Nonrecursive cast_funcs] | Implem str -> (Lazy.force mapper) # structure str | Open descr -> let descr = {descr with popen_expr = Mod.ident descr.popen_expr} in [ Str.open_ descr ] | Include descr -> [ Str.include_ descr ] and gen_module ?module_type functor_parameters s decls : module_binding = let structure = Mod.structure (gen_decls decls) in let functors = List.fold_left (fun acc param -> Mod.functor_ param acc ) structure (List.rev functor_parameters) in let body = match module_type with | None -> functors | Some mty -> Mod.constraint_ functors mty in Mb.mk (mknoloc (Some s)) body and gen_classdecl cast_funcs = function | Declaration { class_name; class_fields } -> let x = fresh() in let obj = Cl.structure (Cstr.mk (Pat.any()) (List.map (gen_class_field x) class_fields)) in (* generate "let _ = t_to_js in" to avoid unused decl warnings *) let ign = function | {pvb_pat = {ppat_desc = Ppat_var {txt; loc = _}; _}; _} -> Vb.mk (Pat.any ()) (var txt) | _ -> assert false in let obj = Cl.let_ Nonrecursive (List.map ign cast_funcs) obj in let obj = Cl.let_ Nonrecursive cast_funcs obj in Ci.mk (mknoloc class_name) (Cl.fun_ Nolabel None (Pat.constraint_ (Pat.var (mknoloc x)) ojs_typ) obj) | Constructor {class_name; js_class_name; class_arrow = {ty_args; ty_vararg; unit_arg; ty_res}} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let obj = ojs_new_obj_arr (ojs_variable js_class_name) concrete_args in let super_class = match ty_res with | Name (super_class, []) -> super_class | _ -> assert false in let e = Cl.apply (Cl.constr (mknoloc (longident_parse super_class)) []) [Nolabel, obj] in let e = if unit_arg then Cl.fun_ Nolabel None unit_pat e else e in let f e (label, x, _) = Cl.fun_ label None (Pat.var (mknoloc x)) e in Ci.mk (mknoloc class_name) (List.fold_left f e (List.rev formal_args)) and gen_class_field x = function | Method {method_name; method_typ; method_def; method_loc; method_attrs} -> let body = match method_def, method_typ with | Getter s, ty_res -> js2ml ty_res (ojs_get (var x) s) | Setter s, Arrow {ty_args = [{lab=Arg; att=_; typ}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} -> mkfun (fun arg -> ojs_set (var x) s (ml2js typ arg)) | MethodCall s, Arrow {ty_args; ty_vararg; unit_arg; ty_res} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res = ojs_call_arr (var x) (Some s) concrete_args in func formal_args unit_arg (js2ml_unit ty_res res) | MethodCall s, ty_res -> js2ml_unit ty_res (ojs "call" [var x; str s; Exp.array []]) | IndexGetter, Arrow {ty_args = [{lab=Arg; att=_; typ=ty_index}]; ty_vararg = None; unit_arg = false; ty_res } -> gen_index_get ty_index (var x) ty_res | IndexSetter, Arrow {ty_args = [{lab=Arg; att=_; typ=ty_index}; {lab=Arg; att=_; typ=ty_value}]; ty_vararg = None; unit_arg = false; ty_res = Unit _ } -> gen_index_set ty_index (var x) ty_value | ApplyAsFunction t, Arrow {ty_args; ty_vararg; unit_arg; ty_res} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res = match t with | Function -> ojs_apply_arr (var x) concrete_args | NewableFunction -> ojs_new_obj_arr (var x) concrete_args in func formal_args unit_arg (js2ml_unit ty_res res) | _ -> error method_loc Binding_type_mismatch in Cf.method_ ~attrs:method_attrs (mknoloc method_name) Public (Cf.concrete Fresh (Exp.constraint_ body (gen_typ method_typ))) | Inherit super -> let e = Cl.apply (Cl.constr super []) [Nolabel, var x] in Cf.inherit_ Fresh e None and gen_class_cast = function | Declaration { class_name; class_fields = _ } -> let class_typ = Typ.constr (mknoloc (longident_parse class_name)) [] in let to_js = let arg = fresh() in Vb.mk (Pat.var (mknoloc (class_name ^ "_to_js"))) (Ast_builder.Default.pexp_fun ~loc:Location.none Nolabel None (Pat.constraint_ (Pat.var (mknoloc arg)) class_typ) (Exp.constraint_ (Exp.send (var arg) (mknoloc "to_js")) ojs_typ)) in let of_js = let arg = fresh() in Vb.mk (Pat.var (mknoloc (class_name ^ "_of_js"))) (Ast_builder.Default.pexp_fun ~loc:Location.none Nolabel None (Pat.constraint_ (Pat.var (mknoloc arg)) ojs_typ) (Exp.constraint_ (Exp.apply (Exp.new_ (mknoloc (Longident.Lident class_name))) [Nolabel, var arg]) class_typ)) in [to_js; of_js] | Constructor {class_name = _; js_class_name = _; class_arrow = _} -> [] and gen_def ~global_object loc decl ty = match decl, ty with | Cast, Arrow {ty_args = [{lab=Arg; att=_; typ}]; ty_vararg = None; unit_arg = false; ty_res} -> mkfun ~typ (fun this -> js2ml ty_res (ml2js typ this)) | PropGet s, Arrow {ty_args = [{lab=Arg; att=_; typ}]; ty_vararg = None; unit_arg = false; ty_res} -> mkfun ~typ (fun this -> js2ml ty_res (ojs_get (ml2js typ this) s)) | PropGet s, Arrow {ty_args = []; ty_vararg = None; unit_arg = true; ty_res} -> fun_unit (gen_def ~global_object loc (Global s) ty_res) | Global s, ty_res -> begin match ty_res with | Arrow {ty_args; ty_vararg; unit_arg; ty_res} -> let this, s = select_path global_object s in let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res this = ojs_call_arr (ml2js Js this) s concrete_args in func formal_args unit_arg (js2ml_unit ty_res (res this)) | _ -> js2ml ty_res (get_path global_object s) end | PropSet s, Arrow {ty_args = [{lab=Arg; att=_; typ=(Name _ as ty_this)}; {lab=Arg; att=_; typ=ty_arg}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} -> let res this arg = ojs_set (ml2js ty_this this) s (ml2js ty_arg arg) in mkfun ~typ:ty_this (fun this -> mkfun ~typ:ty_arg (fun arg -> res this arg)) | PropSet s, Arrow {ty_args = [{lab = Arg; att = _; typ = ty_arg}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} -> mkfun ~typ:ty_arg (fun arg -> set_path ~loc:arg.pexp_loc global_object s (ml2js ty_arg arg)) | MethCall s, Arrow {ty_args = {lab=Arg; att=_; typ} :: ty_args; ty_vararg; unit_arg; ty_res} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res this = ojs_call_arr (ml2js typ this) (Some s) concrete_args in mkfun ~typ (fun this -> func formal_args unit_arg (js2ml_unit ty_res (res this))) | New name, Arrow {ty_args; ty_vararg; unit_arg; ty_res} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res = let constructor = match name with | None -> global_object | Some name -> get_path global_object name in ojs_new_obj_arr constructor concrete_args in func formal_args unit_arg (js2ml ty_res res) | Builder global_attrs, Arrow {ty_args; ty_vararg = None; unit_arg; ty_res} -> let gen_arg {lab; att; typ} = let s = fresh () in let arg_typ = match lab with | Arg | Lab _ -> typ | Opt _ -> Name ("option", [typ]) in (arg_label lab, s, gen_typ arg_typ), fun x -> let js = match get_string_attribute "js" att, lab with | Some s, _ -> s | None, Arg -> error loc Unlabelled_argument_in_builder | None, (Lab {ml; _} | Opt {ml; _}) -> js_name ~global_attrs ml in let code exp = ojs_set x js (ml2js typ exp) in (* special logic to avoid setting optional argument to 'undefined' *) match lab with | Arg | Lab _ -> code (var s) | Opt {def; _} -> begin match def with | None -> match_some_none (var s) ~none:unit_expr ~some:code | Some none -> code (match_some_none ~none ~some:(fun v -> v) (var s)) end in let args = List.map gen_arg ty_args in let formal_args = List.map fst args in let concrete_args = List.map snd args in let f x init code = Exp.sequence (code x) init in let init x = List.fold_left (f x) (js2ml_unit ty_res x) (List.rev concrete_args) in let body = let_exp_in (ojs "empty_obj" [unit_expr]) init in func formal_args unit_arg body | Apply t, Arrow {ty_args = {lab=Arg; att=_; typ} :: ty_args; ty_vararg; unit_arg; ty_res} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res this = match t with | Function -> ojs_apply_arr (ml2js typ this) concrete_args | NewableFunction -> ojs_new_obj_arr (ml2js typ this) concrete_args in mkfun ~typ (fun this -> func formal_args unit_arg (js2ml_unit ty_res (res this))) | Invoke, Arrow {ty_args; ty_vararg; unit_arg; ty_res} -> let formal_args, concrete_args = prepare_args ty_args ty_vararg in let res = ojs_apply_arr global_object concrete_args in func formal_args unit_arg (js2ml ty_res res) | IndexGet, Arrow {ty_args = [{lab=Arg; att=_; typ=(Name _ as ty_this)}; {lab=Arg; att=_; typ=ty_index}]; ty_vararg = None; unit_arg = false; ty_res} -> mkfun ~typ:ty_this (fun this -> gen_index_get ty_index (ml2js ty_this this) ty_res) | IndexSet, Arrow {ty_args = [{lab=Arg; att=_; typ=(Name _ as ty_this)}; {lab=Arg; att=_; typ=ty_index}; {lab=Arg; att=_; typ=ty_value}]; ty_vararg = None; unit_arg = false; ty_res = Unit _} -> mkfun ~typ:ty_this (fun this -> gen_index_set ty_index (ml2js ty_this this) ty_value) | Auto valdef, _ -> Ast_helper.Exp.attr (gen_def ~global_object loc valdef ty) (auto_deprecation_attribute loc valdef) | _ -> error loc Binding_type_mismatch and gen_index_get ty_index this ty_res = let res index = match ty_index with | Name ("int", []) -> ojs "array_get" [this; index] | _ -> ojs "get_prop" [this; ml2js ty_index index] in mkfun ~typ:ty_index (fun index -> js2ml ty_res (res index)) and gen_index_set ty_index this ty_value = let res index value = let value_js = ml2js ty_value value in match ty_index with | Name ("int", []) -> ojs "array_set" [this; index; value_js] | _ -> ojs "set_prop" [this; ml2js ty_index index; value_js] in mkfun ~typ:ty_index (fun index -> mkfun ~typ:ty_value (fun value -> res index value)) (** ppx mapper *) and str_of_sg ~global_attrs sg = let decls = parse_sig ~global_attrs sg in let attr = attr "js.dummy" (str "!! This code has been generated by gen_js_api !!") in register_loc attr; Str.attribute attr :: disable_warnings :: gen_decls decls and module_expr_rewriter ~loc ~attrs sg = let str = str_of_sg ~global_attrs:attrs sg in Mod.constraint_ (Mod.structure ~attrs:[ merlin_hide ] str) (Mty.signature ~loc ~attrs (clear_attr_mapper # signature sg)) and js_to_rewriter ~loc ty = let e' = with_default_loc {loc with loc_ghost = true } (fun () -> js2ml_fun (parse_typ empty_type_context ~global_attrs:[] ty)) in { e' with pexp_loc = loc } and js_of_rewriter ~loc ty = let e' = with_default_loc {loc with loc_ghost = true} (fun () -> ml2js_fun (parse_typ empty_type_context ~global_attrs:[] ty)) in { e' with pexp_loc = loc } and type_decl_rewriter ~loc rec_flag l = let itm = with_default_loc {loc with loc_ghost = true} (fun () -> let funs = List.concat (List.map (gen_funs ~global_attrs:[]) l) in [ disable_warnings; Str.value ~loc:loc rec_flag funs ] ) in itm and mapper = lazy (object inherit Ast_traverse.map as super method! module_expr mexp = let mexp = super # module_expr mexp in match mexp.pmod_desc with | Pmod_extension ({txt = "js"; _}, PSig sg) -> module_expr_rewriter ~loc:mexp.pmod_loc ~attrs:mexp.pmod_attributes sg | _ -> mexp method! structure_item str = let str = super # structure_item str in let global_attrs = [] in match str.pstr_desc with | Pstr_primitive vd when vd.pval_prim = [] -> begin match parse_valdecl ~global_attrs ~in_sig:false vd with | exception Exit -> str | d -> incl (gen_decls [d]) end | Pstr_type (rec_flag, decls) -> let js_decls = List.filter (fun d -> has_attribute "js" d.ptype_attributes) decls in begin match js_decls with | [] -> str | l -> incl ( {str with pstr_desc = Pstr_type (rec_flag, List.map (fun d -> if has_attribute "js" d.ptype_attributes then rewrite_typ_decl d else d) decls)} :: type_decl_rewriter ~loc:str.pstr_loc rec_flag l ) end | _ -> str method! expression e = let e = super # expression e in match e.pexp_desc with | Pexp_extension (attr, PTyp ty) when filter_extension "js.to" attr -> js_to_rewriter ~loc:e.pexp_loc ty | Pexp_extension (attr, PTyp ty) when filter_extension "js.of" attr -> js_of_rewriter ~loc:e.pexp_loc ty | _ -> e method! attribute a = ignore (filter_attr_name "js.dummy" a : bool); super # attribute a end) let is_js_attribute txt = txt = "js" || has_prefix ~prefix:"js." txt let check_loc_mapper = object inherit Ast_traverse.map method! attribute ({attr_name = {txt; loc}; _} as attr) = if is_js_attribute txt then begin if is_registered_loc loc || not !check_attribute || txt = "js.dummy" then () else error loc (Spurious_attribute txt) end; attr end (** Main *) let out = ref "" let specs = [ "-o", Arg.Set_string out, " Specify output .ml file (- for stdout)."; ] let usage = "gen_js_api [-o mymodule.ml] mymodule.mli" let standalone () = let files = ref [] in Arg.parse specs (fun s -> files := s :: !files) usage; let src = match !files with | [src] -> src | [] -> error Location.none No_input | _ -> error Location.none Multiple_inputs in if !out = "" then out := Filename.chop_extension src ^ ".ml"; let oc = if !out = "-" then stdout else open_out !out in let sg = Ocaml_common.Pparse.parse_interface ~tool_name:"gen_js_iface" src |> Selected_ast.Of_ocaml.copy_signature in let str = str_of_sg ~global_attrs:[] sg in ignore (check_loc_mapper # signature sg); let str = clear_attr_mapper # structure str in Format.fprintf (Format.formatter_of_out_channel oc) "%a@." Pprintast.structure str; if !out <> "-" then close_out oc let mapper = object inherit Ast_traverse.map as super method! structure str = check_loc_mapper # structure (super#structure str) end let mark_attributes_as_used = (* mark `js.***` attributes as used in mli. *) object inherit Ast_traverse.map as super method! attribute ({attr_name = {txt; _}; _} as attr) = if is_js_attribute txt then ignore (filter_attr_name txt attr : bool); super # attribute attr end ================================================ FILE: ppx-lib/gen_js_api_ppx.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) open Ppxlib val check_attribute : bool ref val mapper : Ast_traverse.map val module_expr_rewriter: loc:Location.t -> attrs:Ppxlib.Parsetree.attributes -> Ppxlib.Parsetree.signature -> Ppxlib.module_expr val js_of_rewriter: loc:Location.t -> core_type -> expression val js_to_rewriter: loc:Location.t -> core_type -> expression val type_decl_rewriter: loc:Location.t -> rec_flag -> type_declaration list -> structure val mark_attributes_as_used: Ast_traverse.map val standalone : unit -> unit ================================================ FILE: ppx-standalone/dune ================================================ (executables (names gen_js_api) (public_names gen_js_api) (package gen_js_api) (libraries compiler-libs.common ppxlib gen_js_api.lib)) (install (section libexec) (package gen_js_api) (files (gen_js_api.exe as gen_js_api))) ================================================ FILE: ppx-standalone/gen_js_api.ml ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) open Ppxlib let () = try Gen_js_api_ppx.standalone () with exn -> Format.eprintf "%a@." Location.report_exception exn; exit 2 ================================================ FILE: ppx-standalone/gen_js_api.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) (* Empty interface, to enable unused-declaration warnings. *) ================================================ FILE: ppx-test/binding.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) module M : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val cast: t -> string [@@js.cast] val prop_get_arg: t -> int [@@js.get "getter"] val prop_get: unit -> int [@@js.get "getter"] val global: t [@@js.global "global"] val global_arrow: int -> int [@@js.global "global"] val prop_set: t -> int -> unit [@@js.set "setter"] val prop_set_global: t -> unit [@@js.set "setter"] val method_call_global: t -> int [@@js.call "method"] val method_call_global_unit: t -> unit [@@js.call "method"] val method_call_unit: t -> unit -> int [@@js.call "method"] val method_call_args: t -> int -> int [@@js.call "method"] val method_call_unit_unit: t -> unit -> unit [@@js.call "method"] val method_call_args_unit: t -> int -> unit [@@js.call "method"] val new_thing: int -> t [@@js.new] val builder: ?x:int -> (int [@js "y"]) -> z:int -> t [@@js.builder] val index_get_int: t -> int -> string option [@@js.index_get] val index_get_string: t -> string -> string option [@@js.index_get] val index_get_generic: t -> Ojs.t -> string option [@@js.index_get] val index_set_int: t -> int -> string -> unit [@@js.index_set] val index_set_string: t -> string -> string -> unit [@@js.index_set] val index_set_generic: t -> Ojs.t -> string -> unit [@@js.index_set] end ================================================ FILE: ppx-test/binding_automatic.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) [@@@js.implem [@@@warning "-22"]] module M : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val prop_get_arg: t -> int val prop_get: unit -> int val set_prop: t -> int -> unit val set_global: int -> unit val new_thing_unit: unit -> t val new_thing_args: int -> t val method_call_global: t -> unit val method_call_unit: t -> unit -> int val method_call_args: t -> int -> int val method_call_unit_unit: t -> unit -> unit val method_call_args_unit: t -> int -> unit val global: t [@@@warning "-32"] val get: t -> int -> string option val set: t -> int -> string -> unit val get: t -> string -> string option val set: t -> string -> string -> unit [@@@warning "+32"] val get: t -> Ojs.t -> string option val set: t -> Ojs.t -> string -> unit end ================================================ FILE: ppx-test/binding_explicitly_automatic.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) [@@@js.implem [@@@warning "-22"]] module M : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val prop_get_arg: t -> int [@@js] val prop_get: unit -> int [@@js] val set_prop: t -> int -> unit [@@js] val set_global: int -> unit [@@js] val new_thing_unit: unit -> t [@@js] val new_thing_args: int -> t [@@js] val method_call_global: t -> unit [@@js] val method_call_unit: t -> unit -> int [@@js] val method_call_args: t -> int -> int [@@js] val method_call_unit_unit: t -> unit -> unit [@@js] val method_call_args_unit: t -> int -> unit [@@js] val global: t [@@js] [@@@warning "-32"] val get: t -> int -> string option [@@js] val set: t -> int -> string -> unit [@@js] val get: t -> string -> string option [@@js] val set: t -> string -> string -> unit [@@js] [@@@warning "+32"] val get: t -> Ojs.t -> string option [@@js] val set: t -> Ojs.t -> string -> unit [@@js] end ================================================ FILE: ppx-test/binding_manual.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) module M : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val prop_get_arg: t -> int [@@js.get "propGetArg"] val prop_get: unit -> int [@@js.get "propGet"] val set_prop: t -> int -> unit [@@js.set "prop"] val set_global: int -> unit [@@js.set "global"] val new_thing_unit: unit -> t [@@js.new "ThingUnit"] val new_thing_args: int -> t [@@js.new "ThingArgs"] val method_call_global: t -> unit [@@js.call "methodCallGlobal"] val method_call_unit: t -> unit -> int [@@js.call "methodCallUnit"] val method_call_args: t -> int -> int[@@js.call "methodCallArgs"] val method_call_unit_unit: t -> unit -> unit[@@js.call "methodCallUnitUnit"] val method_call_args_unit: t -> int -> unit[@@js.call "methodCallArgsUnit"] val global: t[@@js.global "global"] [@@@warning "-32"] val get: t -> int -> string option [@@js.index_get] val set: t -> int -> string -> unit [@@js.index_set] val get: t -> string -> string option [@@js.index_get] val set: t -> string -> string -> unit [@@js.index_set] [@@@warning "+32"] val get: t -> Ojs.t -> string option [@@js.index_get] val set: t -> Ojs.t -> string -> unit [@@js.index_set] end ================================================ FILE: ppx-test/dune ================================================ (rule (targets extension.ml.result) (deps extension.ml) (action (run ppx/main.exe --impl %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff expected/extension.ml extension.ml.result))) (rule (targets issues.ml.result) (deps issues.ml) (action (run ppx/main.exe --impl %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (enabled_if (>= %{ocaml_version} 4.09)) (action (diff expected/issues.ml issues.ml.result))) (rule (targets types.ml.result) (deps types.ml) (action (run ppx/main.exe --impl %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (enabled_if (>= %{ocaml_version} 4.09)) (action (diff expected/types.ml types.ml.result))) (rule (targets binding_automatic.ml) (deps binding_automatic.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff expected/binding_automatic.ml binding_automatic.ml))) (rule (targets binding_explicitly_automatic.ml) (deps binding_explicitly_automatic.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff binding_automatic.ml binding_explicitly_automatic.ml))) (rule (targets binding_manual.ml) (deps binding_manual.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (targets binding.ml) (deps binding.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff expected/binding.ml binding.ml))) (rule (targets scoped.ml) (deps scoped.mli) (action (run %{bin:gen_js_api} %{deps} -o %{targets}))) (rule (alias runtest) (action (diff expected/scoped.ml scoped.ml))) (rule (targets union_and_enum.ml) (deps union_and_enum.mli) (action (run %{bin:gen_js_api} %{deps} -o %{targets}))) (rule (alias runtest) (action (diff expected/union_and_enum.ml union_and_enum.ml))) (rule (targets issues_mli.ml) (deps issues_mli.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff expected/issues_mli.ml issues_mli.ml))) (rule (targets recursive_modules.ml) (deps recursive_modules.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff expected/recursive_modules.ml recursive_modules.ml))) (rule (targets first_class_modules.ml) (deps first_class_modules.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff expected/first_class_modules.ml first_class_modules.ml))) (library (name test_library) (libraries ojs) (preprocess (pps gen_js_api.ppx)) (modes byte) (modules binding_automatic binding_explicitly_automatic binding_manual binding extension first_class_modules issues_mli issues recursive_modules scoped types union_and_enum)) (rule (alias runtest) (package gen_js_api) (deps test_library.cma) (action (echo "Successfully compile test_library"))) (rule (targets modules.ml) (deps modules.mli) (action (run gen_js_api %{deps} -o %{targets}))) (rule (alias runtest) (package gen_js_api) (action (diff expected/modules.ml modules.ml))) ================================================ FILE: ppx-test/expected/binding.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module M = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let cast : t -> string = fun (x3 : t) -> Ojs.string_of_js (t_to_js x3) let prop_get_arg : t -> int = fun (x4 : t) -> Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x4) "getter") let prop_get : unit -> int = fun () -> Ojs.int_of_js (Ojs.get_prop_ascii Ojs.global "getter") let global : t = t_of_js (Ojs.get_prop_ascii Ojs.global "global") let global_arrow : int -> int = fun (x5 : int) -> Ojs.int_of_js (Ojs.call Ojs.global "global" [|(Ojs.int_to_js x5)|]) let prop_set : t -> int -> unit = fun (x6 : t) (x7 : int) -> Ojs.set_prop_ascii (t_to_js x6) "setter" (Ojs.int_to_js x7) let prop_set_global : t -> unit = fun (x8 : t) -> Ojs.set_prop_ascii Ojs.global "setter" (t_to_js x8) let method_call_global : t -> int = fun (x9 : t) -> Ojs.int_of_js (Ojs.call (t_to_js x9) "method" [||]) let method_call_global_unit : t -> unit = fun (x10 : t) -> ignore (Ojs.call (t_to_js x10) "method" [||]) let method_call_unit : t -> unit -> int = fun (x11 : t) () -> Ojs.int_of_js (Ojs.call (t_to_js x11) "method" [||]) let method_call_args : t -> int -> int = fun (x13 : t) (x12 : int) -> Ojs.int_of_js (Ojs.call (t_to_js x13) "method" [|(Ojs.int_to_js x12)|]) let method_call_unit_unit : t -> unit -> unit = fun (x14 : t) () -> ignore (Ojs.call (t_to_js x14) "method" [||]) let method_call_args_unit : t -> int -> unit = fun (x16 : t) (x15 : int) -> ignore (Ojs.call (t_to_js x16) "method" [|(Ojs.int_to_js x15)|]) let new_thing : int -> t = fun (x17 : int) -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Thing") [|(Ojs.int_to_js x17)|]) let builder : ?x:int -> int -> z:int -> t = fun ?x:(x18 : int option) (x19 : int) ~z:(x20 : int) -> let x21 = Ojs.empty_obj () in (match x18 with | Some x22 -> Ojs.set_prop_ascii x21 "x" (Ojs.int_to_js x22) | None -> ()); Ojs.set_prop_ascii x21 "y" (Ojs.int_to_js x19); Ojs.set_prop_ascii x21 "z" (Ojs.int_to_js x20); t_of_js x21 let index_get_int : t -> int -> string option = fun (x23 : t) (x24 : int) -> Ojs.option_of_js Ojs.string_of_js (Ojs.array_get (t_to_js x23) x24) let index_get_string : t -> string -> string option = fun (x26 : t) (x27 : string) -> Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop (t_to_js x26) (Ojs.string_to_js x27)) let index_get_generic : t -> Ojs.t -> string option = fun (x29 : t) (x30 : Ojs.t) -> Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop (t_to_js x29) x30) let index_set_int : t -> int -> string -> unit = fun (x32 : t) (x33 : int) (x34 : string) -> Ojs.array_set (t_to_js x32) x33 (Ojs.string_to_js x34) let index_set_string : t -> string -> string -> unit = fun (x35 : t) (x36 : string) (x37 : string) -> Ojs.set_prop (t_to_js x35) (Ojs.string_to_js x36) (Ojs.string_to_js x37) let index_set_generic : t -> Ojs.t -> string -> unit = fun (x38 : t) (x39 : Ojs.t) (x40 : string) -> Ojs.set_prop (t_to_js x38) x39 (Ojs.string_to_js x40) end ================================================ FILE: ppx-test/expected/binding_automatic.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] [@@@warning "-22"] module M = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let prop_get_arg : t -> int = ((fun (x3 : t) -> Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x3) "propGetArg")) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.get' attribute."]) let prop_get : unit -> int = ((fun () -> Ojs.int_of_js (Ojs.get_prop_ascii Ojs.global "propGet")) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.get' attribute."]) let set_prop : t -> int -> unit = ((fun (x4 : t) (x5 : int) -> Ojs.set_prop_ascii (t_to_js x4) "prop" (Ojs.int_to_js x5)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.set' attribute."]) let set_global : int -> unit = ((fun (x6 : int) -> Ojs.set_prop_ascii Ojs.global "global" (Ojs.int_to_js x6)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.set' attribute."]) let new_thing_unit : unit -> t = ((fun () -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "ThingUnit") [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.new' attribute."]) let new_thing_args : int -> t = ((fun (x7 : int) -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "ThingArgs") [|(Ojs.int_to_js x7)|])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.new' attribute."]) let method_call_global : t -> unit = ((fun (x8 : t) -> ignore (Ojs.call (t_to_js x8) "methodCallGlobal" [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_unit : t -> unit -> int = ((fun (x9 : t) () -> Ojs.int_of_js (Ojs.call (t_to_js x9) "methodCallUnit" [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_args : t -> int -> int = ((fun (x11 : t) (x10 : int) -> Ojs.int_of_js (Ojs.call (t_to_js x11) "methodCallArgs" [|(Ojs.int_to_js x10)|])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_unit_unit : t -> unit -> unit = ((fun (x12 : t) () -> ignore (Ojs.call (t_to_js x12) "methodCallUnitUnit" [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_args_unit : t -> int -> unit = ((fun (x14 : t) (x13 : int) -> ignore (Ojs.call (t_to_js x14) "methodCallArgsUnit" [|(Ojs.int_to_js x13)|])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let global : t = ((t_of_js (Ojs.get_prop_ascii Ojs.global "global")) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.global' attribute."]) let get : t -> int -> string option = ((fun (x15 : t) (x16 : int) -> Ojs.option_of_js Ojs.string_of_js (Ojs.array_get (t_to_js x15) x16)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.index_get' attribute."]) let set : t -> int -> string -> unit = ((fun (x18 : t) (x19 : int) (x20 : string) -> Ojs.array_set (t_to_js x18) x19 (Ojs.string_to_js x20)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.index_set' attribute."]) let get : t -> string -> string option = ((fun (x21 : t) (x22 : string) -> Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop (t_to_js x21) (Ojs.string_to_js x22))) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.index_get' attribute."]) let set : t -> string -> string -> unit = ((fun (x24 : t) (x25 : string) (x26 : string) -> Ojs.set_prop (t_to_js x24) (Ojs.string_to_js x25) (Ojs.string_to_js x26)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.index_set' attribute."]) let get : t -> Ojs.t -> string option = ((fun (x27 : t) (x28 : Ojs.t) -> Ojs.option_of_js Ojs.string_of_js (Ojs.get_prop (t_to_js x27) x28)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.index_get' attribute."]) let set : t -> Ojs.t -> string -> unit = ((fun (x30 : t) (x31 : Ojs.t) (x32 : string) -> Ojs.set_prop (t_to_js x30) x31 (Ojs.string_to_js x32)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.index_set' attribute."]) end ================================================ FILE: ppx-test/expected/extension.ml ================================================ let _ = Ojs.int_to_js let _ = fun (x2 : int -> int) -> Ojs.fun_to_js 1 (fun (x3 : Ojs.t) -> Ojs.int_to_js (x2 (Ojs.int_of_js x3))) ================================================ FILE: ppx-test/expected/first_class_modules.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module Console = struct let log : (module Ojs.T with type t = 'a) -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x1 : a) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log" [|(A.t_to_js x1)|]) let log2 : (module Ojs.T with type t = 'a) -> (module Ojs.T with type t = 'b) -> 'a -> 'b -> unit = fun (type a) -> fun (type b) -> fun ((module A) : (module Ojs.T with type t = a)) ((module B) : (module Ojs.T with type t = b)) (x2 : a) (x3 : b) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log" [|(A.t_to_js x2);(B.t_to_js x3)|]) let log3 : (module Ojs.T with type t = 'a) -> (module Ojs.T with type t = 'b) -> (module Ojs.T with type t = 'c) -> 'a -> 'b -> 'c -> unit = fun (type a) -> fun (type b) -> fun (type c) -> fun ((module A) : (module Ojs.T with type t = a)) ((module B) : (module Ojs.T with type t = b)) ((module C) : (module Ojs.T with type t = c)) (x4 : a) (x5 : b) (x6 : c) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log" [|(A.t_to_js x4);(B.t_to_js x5);(C.t_to_js x6)|]) end module Console2 = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x8 : Ojs.t) -> x8 and t_to_js : t -> Ojs.t = fun (x7 : Ojs.t) -> x7 let log : (module Ojs.T with type t = 'a) -> t -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x10 : t) (x9 : a) -> ignore (Ojs.call (t_to_js x10) "log" [|(A.t_to_js x9)|]) let log2 : (module Ojs.T with type t = 'a) -> (module Ojs.T with type t = 'b) -> t -> 'a -> 'b -> unit = fun (type a) -> fun (type b) -> fun ((module A) : (module Ojs.T with type t = a)) ((module B) : (module Ojs.T with type t = b)) (x13 : t) (x11 : a) (x12 : b) -> ignore (Ojs.call (t_to_js x13) "log" [|(A.t_to_js x11);(B.t_to_js x12)|]) let log3 : (module Ojs.T with type t = 'a) -> (module Ojs.T with type t = 'b) -> (module Ojs.T with type t = 'c) -> t -> 'a -> 'b -> 'c -> unit = fun (type a) -> fun (type b) -> fun (type c) -> fun ((module A) : (module Ojs.T with type t = a)) ((module B) : (module Ojs.T with type t = b)) ((module C) : (module Ojs.T with type t = c)) (x17 : t) (x14 : a) (x15 : b) (x16 : c) -> ignore (Ojs.call (t_to_js x17) "log" [|(A.t_to_js x14);(B.t_to_js x15);(C.t_to_js x16)|]) end module Console3 = struct module Log = struct let _1 : (module Ojs.T with type t = 'a) -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x18 : a) -> Ojs.unit_of_js (Ojs.apply (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "console") "log") [|(A.t_to_js x18)|]) let _2 : (module Ojs.T with type t = 'a) -> (module Ojs.T with type t = 'b) -> 'a -> 'b -> unit = fun (type a) -> fun (type b) -> fun ((module A) : (module Ojs.T with type t = a)) ((module B) : (module Ojs.T with type t = b)) (x19 : a) (x20 : b) -> Ojs.unit_of_js (Ojs.apply (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "console") "log") [|(A.t_to_js x19);(B.t_to_js x20)|]) let _3 : (module Ojs.T with type t = 'a) -> (module Ojs.T with type t = 'b) -> (module Ojs.T with type t = 'c) -> 'a -> 'b -> 'c -> unit = fun (type a) -> fun (type b) -> fun (type c) -> fun ((module A) : (module Ojs.T with type t = a)) ((module B) : (module Ojs.T with type t = b)) ((module C) : (module Ojs.T with type t = c)) (x21 : a) (x22 : b) (x23 : c) -> Ojs.unit_of_js (Ojs.apply (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "console") "log") [|(A.t_to_js x21);(B.t_to_js x22);(C.t_to_js x23)|]) end end module Array = struct type 'a t = Ojs.t let rec t_of_js : 'a . (Ojs.t -> 'a) -> Ojs.t -> 'a t = fun (type __a) (__a_of_js : Ojs.t -> __a) -> fun (x25 : Ojs.t) -> x25 and t_to_js : 'a . ('a -> Ojs.t) -> 'a t -> Ojs.t = fun (type __a) (__a_to_js : __a -> Ojs.t) -> fun (x24 : Ojs.t) -> x24 let create : (module Ojs.T with type t = 'a) -> 'a list -> 'a t = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x26 : a list) -> t_of_js A.t_of_js (Ojs.new_obj_arr (Ojs.get_prop_ascii Ojs.global "Array") (let x27 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in List.iter (fun (x28 : a) -> ignore (Ojs.call x27 "push" [|(A.t_to_js x28)|])) x26; x27)) let create' : (module Ojs.T with type t = 'a) -> 'a list -> 'a t = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x30 : a list) -> t_of_js A.t_of_js (Ojs.call (Ojs.get_prop_ascii Ojs.global "Array") "apply" [|Ojs.null;((let x31 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in List.iter (fun (x32 : a) -> ignore (Ojs.call x31 "push" [|(A.t_to_js x32)|])) x30; x31))|]) let push : (module Ojs.T with type t = 'a) -> 'a t -> 'a -> unit = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x35 : a t) (x34 : a) -> ignore (Ojs.call (t_to_js A.t_to_js x35) "push" [|(A.t_to_js x34)|]) let pop : (module Ojs.T with type t = 'a) -> 'a t -> 'a option = fun (type a) -> fun ((module A) : (module Ojs.T with type t = a)) (x37 : a t) -> Ojs.option_of_js A.t_of_js (Ojs.call (t_to_js A.t_to_js x37) "pop" [||]) end ================================================ FILE: ppx-test/expected/issues.ml ================================================ module Issue116 : sig type t end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 end)[@merlin.hide ]) module Issue117 : sig module T : sig val log : 'a -> unit val log2 : 'a -> 'b -> unit end end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module T = struct let log : 'a -> unit = fun (x3 : 'a) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "log" [|(Obj.magic x3)|]) let log2 : 'a -> 'b -> unit = fun (x4 : 'a) (x5 : 'b) -> ignore (Ojs.call (Ojs.get_prop_ascii Ojs.global "console") "jsLog2" [|(Obj.magic x4);(Obj.magic x5)|]) end end)[@merlin.hide ]) module Issue124 : sig type a and b = { a: a } type 'a dummy type 'a wrapped = | Wrapped of 'a type u = | Unknown of Ojs.t | T of t | WrappedT of t wrapped and t = [ `U of u ] dummy type ('a, 'b) base = [ `BaseA of 'a | `BaseB of 'b ] dummy and base1 = (int, string) base and base2 = (string, int) base end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type a = Ojs.t and b = { a: a } let rec a_of_js : Ojs.t -> a = fun (x7 : Ojs.t) -> x7 and a_to_js : a -> Ojs.t = fun (x6 : Ojs.t) -> x6 and b_of_js : Ojs.t -> b = fun js -> { a = (a_of_js js) } and b_to_js : b -> Ojs.t = fun { a } -> a_to_js a type 'a dummy = Ojs.t let rec dummy_of_js : 'a . (Ojs.t -> 'a) -> Ojs.t -> 'a dummy = fun (type __a) (__a_of_js : Ojs.t -> __a) -> fun (x9 : Ojs.t) -> x9 and dummy_to_js : 'a . ('a -> Ojs.t) -> 'a dummy -> Ojs.t = fun (type __a) (__a_to_js : __a -> Ojs.t) -> fun (x8 : Ojs.t) -> x8 type 'a wrapped = | Wrapped of 'a let rec wrapped_of_js : 'a . (Ojs.t -> 'a) -> Ojs.t -> 'a wrapped = let f a_of_js x = Wrapped (a_of_js x) in f and wrapped_to_js : 'a . ('a -> Ojs.t) -> 'a wrapped -> Ojs.t = let f a_to_js = function | Wrapped a -> a_to_js a in f type u = | Unknown of Ojs.t | T of t | WrappedT of t wrapped and t = [ `U of u ] dummy let rec u_of_js : Ojs.t -> u = fun (x15 : Ojs.t) -> let x16 = x15 in match Ojs.type_of (Ojs.get_prop_ascii x16 "type") with | "number" -> Unknown x16 | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x16 "type") with | "t" -> T (t_of_js x16) | "wrapped_t" -> WrappedT (wrapped_of_js t_of_js x16) | _ -> Unknown x16) | "boolean" -> Unknown x16 | _ -> Unknown x16 and u_to_js : u -> Ojs.t = fun (x10 : u) -> match x10 with | Unknown x11 -> x11 | T x12 -> t_to_js x12 | WrappedT x13 -> wrapped_to_js t_to_js x13 and t_of_js : Ojs.t -> t = Obj.magic and t_to_js : t -> Ojs.t = Obj.magic type ('a, 'b) base = [ `BaseA of 'a | `BaseB of 'b ] dummy and base1 = (int, string) base and base2 = (string, int) base let rec base_of_js : 'a 'b . (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) base = fun _ _ -> Obj.magic and base_to_js : 'a 'b . ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) base -> Ojs.t = fun _ _ -> Obj.magic and base1_of_js : Ojs.t -> base1 = fun (x21 : Ojs.t) -> base_of_js Ojs.int_of_js Ojs.string_of_js x21 and base1_to_js : base1 -> Ojs.t = fun (x18 : (int, string) base) -> base_to_js Ojs.int_to_js Ojs.string_to_js x18 and base2_of_js : Ojs.t -> base2 = fun (x27 : Ojs.t) -> base_of_js Ojs.string_of_js Ojs.int_of_js x27 and base2_to_js : base2 -> Ojs.t = fun (x24 : (string, int) base) -> base_to_js Ojs.string_to_js Ojs.int_to_js x24 end)[@merlin.hide ]) module Issue109 : sig type t = [ `S of string | `I of int ] end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type t = [ `S of string | `I of int ] let rec t_of_js : Ojs.t -> t = fun (x35 : Ojs.t) -> let x36 = x35 in match Ojs.type_of x36 with | "number" -> (match Ojs.int_of_js x36 with | x38 -> `I x38) | "string" -> (match Ojs.string_of_js x36 with | x37 -> `S x37) | _ -> assert false and t_to_js : t -> Ojs.t = fun (x32 : [ `S of string | `I of int ]) -> match x32 with | `S x33 -> Ojs.string_to_js x33 | `I x34 -> Ojs.int_to_js x34 end)[@merlin.hide ]) module Issue142 : sig type t = [ `Foo ] and u = t end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type t = [ `Foo ] and u = t let rec t_of_js : Ojs.t -> t = fun (x40 : Ojs.t) -> let x41 = x40 in match Ojs.int_of_js x41 with | 42 -> `Foo | _ -> assert false and t_to_js : t -> Ojs.t = fun (x39 : [ `Foo ]) -> match x39 with | `Foo -> Ojs.int_to_js 42 and u_of_js : Ojs.t -> u = fun (x43 : Ojs.t) -> t_of_js x43 and u_to_js : u -> Ojs.t = fun (x42 : t) -> t_to_js x42 end)[@merlin.hide ]) module Issue144 : sig type t val f : t -> args:int -> int end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x45 : Ojs.t) -> x45 and t_to_js : t -> Ojs.t = fun (x44 : Ojs.t) -> x44 let f : t -> args:int -> int = fun (x46 : t) ~args:(x47 : int) -> Ojs.int_of_js (Ojs.apply (Ojs.call (t_to_js x46) "f" [||]) [|(Ojs.int_to_js x47)|]) end)[@merlin.hide ]) module Issue146 : sig val f : ?arg:[ `Foo ] -> unit -> int end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] let f : ?arg:[ `Foo ] -> unit -> int = fun ?arg:(x48 : [ `Foo ] option) () -> Ojs.int_of_js (let x51 = Ojs.global in Ojs.call (Ojs.get_prop_ascii x51 "f") "apply" [|x51;((let x49 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in (match x48 with | Some x50 -> ignore (Ojs.call x49 "push" [|((match x50 with | `Foo -> Ojs.int_to_js 42))|]) | None -> ()); x49))|]) end)[@merlin.hide ]) module PR165 : sig module Markdown : sig type t end module ParameterInformation : sig type t val create : label:[ `String of string | `Tuple of (int * int) ] -> ?documentation:[ `String of string | `Markdown of Markdown.t ] -> unit -> t end end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module Markdown = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x53 : Ojs.t) -> x53 and t_to_js : t -> Ojs.t = fun (x52 : Ojs.t) -> x52 end module ParameterInformation = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x55 : Ojs.t) -> x55 and t_to_js : t -> Ojs.t = fun (x54 : Ojs.t) -> x54 let create : label:[ `String of string | `Tuple of (int * int) ] -> ?documentation:[ `String of string | `Markdown of Markdown.t ] -> unit -> t = fun ~label:(x56 : [ `String of string | `Tuple of (int * int) ]) ?documentation:(x57 : [ `String of string | `Markdown of Markdown.t ] option) () -> t_of_js (Ojs.new_obj_arr (Ojs.get_prop_ascii Ojs.global "ParameterInformation") (let x58 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in ignore (Ojs.call x58 "push" [|((match x56 with | `String x62 -> Ojs.string_to_js x62 | `Tuple x63 -> let (x64, x65) = x63 in let x66 = Ojs.array_make 2 in (Ojs.array_set x66 0 (Ojs.int_to_js x64); Ojs.array_set x66 1 (Ojs.int_to_js x65); x66)))|]); (match x57 with | Some x59 -> ignore (Ojs.call x58 "push" [|((match x59 with | `String x60 -> Ojs.string_to_js x60 | `Markdown x61 -> Markdown.t_to_js x61))|]) | None -> ()); x58)) end end)[@merlin.hide ]) ================================================ FILE: ppx-test/expected/issues_mli.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module Issue144 = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let f : t -> args:int -> int = fun (x3 : t) ~args:(x4 : int) -> Ojs.int_of_js (Ojs.apply (Ojs.call (t_to_js x3) "f" [||]) [|(Ojs.int_to_js x4)|]) end ================================================ FILE: ppx-test/expected/modules.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module Event = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 end module Foo = struct module E = Event let foo : E.t -> string -> unit = fun (x4 : E.t) (x3 : string) -> ignore (Ojs.call (E.t_to_js x4) "foo" [|(Ojs.string_to_js x3)|]) end module Bar = struct include Event let bar : t -> string -> unit = fun (x6 : t) (x5 : string) -> ignore (Ojs.call (t_to_js x6) "bar" [|(Ojs.string_to_js x5)|]) end ================================================ FILE: ppx-test/expected/recursive_modules.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] module rec Foo:sig type t = private Ojs.t val t_of_js : Ojs.t -> t val t_to_js : t -> Ojs.t val create : string -> t val describe : t -> string val to_bar : t -> Bar.t end = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let create : string -> t = fun (x3 : string) -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Foo") [|(Ojs.string_to_js x3)|]) let describe : t -> string = fun (x4 : t) -> Ojs.string_of_js (Ojs.call (t_to_js x4) "describe" [||]) let to_bar : t -> Bar.t = fun (x5 : t) -> Bar.t_of_js (Ojs.call (t_to_js x5) "toBar" [||]) end and Bar:sig type t = private Ojs.t val t_of_js : Ojs.t -> t val t_to_js : t -> Ojs.t val create : string -> t val describe : t -> string val to_foo : t -> Foo.t end = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x7 : Ojs.t) -> x7 and t_to_js : t -> Ojs.t = fun (x6 : Ojs.t) -> x6 let create : string -> t = fun (x8 : string) -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Bar") [|(Ojs.string_to_js x8)|]) let describe : t -> string = fun (x9 : t) -> Ojs.string_of_js (Ojs.call (t_to_js x9) "describe" [||]) let to_foo : t -> Foo.t = fun (x10 : t) -> Foo.t_of_js (Ojs.call (t_to_js x10) "toFoo" [||]) end ================================================ FILE: ppx-test/expected/scoped.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] [@@@warning "-22"] module M = struct type t = Ojs.t let rec t_of_js : Ojs.t -> t = fun (x2 : Ojs.t) -> x2 and t_to_js : t -> Ojs.t = fun (x1 : Ojs.t) -> x1 let prop_get_arg : t -> int = ((fun (x3 : t) -> Ojs.int_of_js (Ojs.get_prop_ascii (t_to_js x3) "propGetArg")) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.get' attribute."]) let prop_get : unit -> int = ((fun () -> Ojs.int_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "scope") "propGet")) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.get' attribute."]) let set_prop : t -> int -> unit = ((fun (x4 : t) (x5 : int) -> Ojs.set_prop_ascii (t_to_js x4) "prop" (Ojs.int_to_js x5)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.set' attribute."]) let set_global : int -> unit = ((fun (x6 : int) -> Ojs.set_prop_ascii (Ojs.get_prop_ascii Ojs.global "scope") "global" (Ojs.int_to_js x6)) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.set' attribute."]) let new_thing_unit : unit -> t = ((fun () -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "scope") "ThingUnit") [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.new' attribute."]) let new_thing_args : int -> t = ((fun (x7 : int) -> t_of_js (Ojs.new_obj (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "scope") "ThingArgs") [|(Ojs.int_to_js x7)|])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.new' attribute."]) let method_call_global : t -> unit = ((fun (x8 : t) -> ignore (Ojs.call (t_to_js x8) "methodCallGlobal" [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_unit : t -> unit -> int = ((fun (x9 : t) () -> Ojs.int_of_js (Ojs.call (t_to_js x9) "methodCallUnit" [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_args : t -> int -> int = ((fun (x11 : t) (x10 : int) -> Ojs.int_of_js (Ojs.call (t_to_js x11) "methodCallArgs" [|(Ojs.int_to_js x10)|])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_unit_unit : t -> unit -> unit = ((fun (x12 : t) () -> ignore (Ojs.call (t_to_js x12) "methodCallUnitUnit" [||])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let method_call_args_unit : t -> int -> unit = ((fun (x14 : t) (x13 : int) -> ignore (Ojs.call (t_to_js x14) "methodCallArgsUnit" [|(Ojs.int_to_js x13)|])) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.call' attribute."]) let global : t = ((t_of_js (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "scope") "global")) [@ocaml.ppwarning "Heuristic for automatic binding is deprecated; please add the '@js.global' attribute."]) let invoke : unit -> unit = fun () -> Ojs.unit_of_js (Ojs.apply (Ojs.get_prop_ascii Ojs.global "scope") [||]) end let d : unit -> unit = fun () -> ignore (Ojs.call (Ojs.get_prop_ascii (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "a") "b") "c") "d" [||]) ================================================ FILE: ppx-test/expected/types.ml ================================================ type 'a of_js = Ojs.t -> 'a type 'a to_js = 'a -> Ojs.t [@@@ocaml.text " JS-able types "] let _ : string of_js = Ojs.string_of_js let _ : string to_js = Ojs.string_to_js let _ : int of_js = Ojs.int_of_js let _ : int to_js = Ojs.int_to_js let _ : bool of_js = Ojs.bool_of_js let _ : bool to_js = Ojs.bool_to_js let _ : float of_js = Ojs.float_of_js let _ : float to_js = Ojs.float_to_js let _ : Ojs.t of_js = fun (x9 : Ojs.t) -> x9 let _ : Ojs.t to_js = fun (x10 : Ojs.t) -> x10 let _ : (string * int) of_js = fun (x11 : Ojs.t) -> let x12 = x11 in ((Ojs.string_of_js (Ojs.array_get x12 0)), (Ojs.int_of_js (Ojs.array_get x12 1))) let _ : (string * int) to_js = fun (x13 : (string * int)) -> let (x14, x15) = x13 in let x16 = Ojs.array_make 2 in Ojs.array_set x16 0 (Ojs.string_to_js x14); Ojs.array_set x16 1 (Ojs.int_to_js x15); x16 let _ : (string * int * bool) of_js = fun (x17 : Ojs.t) -> let x18 = x17 in ((Ojs.string_of_js (Ojs.array_get x18 0)), (Ojs.int_of_js (Ojs.array_get x18 1)), (Ojs.bool_of_js (Ojs.array_get x18 2))) let _ : (string * int * bool) to_js = fun (x19 : (string * int * bool)) -> let (x20, x21, x22) = x19 in let x23 = Ojs.array_make 3 in Ojs.array_set x23 0 (Ojs.string_to_js x20); Ojs.array_set x23 1 (Ojs.int_to_js x21); Ojs.array_set x23 2 (Ojs.bool_to_js x22); x23 let _ : (string -> int) of_js = fun (x24 : Ojs.t) (x25 : string) -> Ojs.int_of_js (Ojs.apply x24 [|(Ojs.string_to_js x25)|]) let _ : (string -> int) to_js = fun (x26 : string -> int) -> Ojs.fun_to_js 1 (fun (x27 : Ojs.t) -> Ojs.int_to_js (x26 (Ojs.string_of_js x27))) let _ : ((string -> int) -> bool -> unit) of_js = fun (x28 : Ojs.t) (x29 : string -> int) (x31 : bool) -> ignore (Ojs.apply x28 [|(Ojs.fun_to_js 1 (fun (x30 : Ojs.t) -> Ojs.int_to_js (x29 (Ojs.string_of_js x30))));(Ojs.bool_to_js x31)|]) let _ : ((string -> int) -> bool -> unit) to_js = fun (x32 : (string -> int) -> bool -> unit) -> Ojs.fun_to_js 2 (fun (x33 : Ojs.t) (x35 : Ojs.t) -> x32 (fun (x34 : string) -> Ojs.int_of_js (Ojs.apply x33 [|(Ojs.string_to_js x34)|])) (Ojs.bool_of_js x35)) let _ : string array of_js = fun (x36 : Ojs.t) -> Ojs.array_of_js Ojs.string_of_js x36 let _ : string array to_js = fun (x38 : string array) -> Ojs.array_to_js Ojs.string_to_js x38 let _ : string list of_js = fun (x40 : Ojs.t) -> Ojs.list_of_js Ojs.string_of_js x40 let _ : string list to_js = fun (x42 : string list) -> Ojs.list_to_js Ojs.string_to_js x42 let _ : string option of_js = fun (x44 : Ojs.t) -> Ojs.option_of_js Ojs.string_of_js x44 let _ : string option to_js = fun (x46 : string option) -> Ojs.option_to_js Ojs.string_to_js x46 let _ : (_ -> _) of_js = fun (x48 : Ojs.t) (x49 : 'a) -> Obj.magic (Ojs.apply x48 [|(Obj.magic x49)|]) let _ : (_ -> _) to_js = fun (x50 : 'a -> 'b) -> Ojs.fun_to_js 1 (fun (x51 : Ojs.t) -> Obj.magic (x50 (Obj.magic x51))) let _ : [ `foo | `bar | `Baz | `I of int | `S of string ] of_js = fun (x52 : Ojs.t) -> let x53 = x52 in match Ojs.type_of x53 with | "number" -> (match Ojs.int_of_js x53 with | 42 -> `bar | x54 -> `I x54) | "string" -> (match Ojs.string_of_js x53 with | "foo" -> `foo | "Baz" -> `Baz | x55 -> `S x55) | _ -> assert false let _ : [ `foo | `bar | `Baz | `I of int | `S of string ] to_js = fun (x56 : [ `foo | `bar | `Baz | `I of int | `S of string ]) -> match x56 with | `foo -> Ojs.string_to_js "foo" | `bar -> Ojs.int_to_js 42 | `Baz -> Ojs.string_to_js "Baz" | `I x57 -> Ojs.int_to_js x57 | `S x58 -> Ojs.string_to_js x58 [@@@ocaml.text " Label & Options Value "] let _ : (label:int -> ?opt:int -> unit -> unit) of_js = fun (x59 : Ojs.t) ~label:(x60 : int) ?opt:(x61 : int option) () -> ignore (Ojs.call x59 "apply" [|Ojs.null;((let x62 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in ignore (Ojs.call x62 "push" [|(Ojs.int_to_js x60)|]); (match x61 with | Some x63 -> ignore (Ojs.call x62 "push" [|(Ojs.int_to_js x63)|]) | None -> ()); x62))|]) let _ : (label:int -> ?opt:int -> unit -> unit) to_js = fun (x64 : label:int -> ?opt:int -> unit -> unit) -> Ojs.fun_to_js 2 (fun (x65 : Ojs.t) (x66 : Ojs.t) -> x64 ~label:(Ojs.int_of_js x65) ?opt:(Ojs.option_of_js Ojs.int_of_js x66) ()) let _ : (label:int -> ?opt:int -> unit -> unit) of_js = fun (x68 : Ojs.t) ~label:(x69 : int) ?opt:(x70 : int option) () -> ignore (Ojs.call x68 "apply" [|Ojs.null;((let x71 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in ignore (Ojs.call x71 "push" [|(Ojs.int_to_js x69)|]); (match x70 with | Some x72 -> ignore (Ojs.call x71 "push" [|(Ojs.int_to_js x72)|]) | None -> ()); x71))|]) let _ : (label:int -> ?opt:int -> unit -> unit) to_js = fun (x73 : label:int -> ?opt:int -> unit -> unit) -> Ojs.fun_to_js 2 (fun (x74 : Ojs.t) (x75 : Ojs.t) -> x73 ~label:(Ojs.int_of_js x74) ?opt:(Ojs.option_of_js Ojs.int_of_js x75) ()) [@@@ocaml.text " Functions "] module B : sig val default0 : ?x:int -> unit -> unit val default1 : ?x:int -> unit -> unit val builder0 : unit -> Ojs.t val builder1 : x:int -> Ojs.t val builder2 : ?x:int -> ?y:string -> unit -> Ojs.t val builder3 : x:int -> y:string -> unit -> Ojs.t val builder4 : x:int -> y:string -> z:unit -> Ojs.t val builder5 : ?x:int -> ?y:string -> unit -> Ojs.t val builder6 : ?x:int -> ?y:string -> ?z:int -> unit -> Ojs.t val sep : string -> string list -> string end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] let default0 : ?x:int -> unit -> unit = fun ?x:(x77 : int option) () -> ignore (let x80 = Ojs.global in Ojs.call (Ojs.get_prop_ascii x80 "default0") "apply" [|x80;((let x78 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in (match x77 with | Some x79 -> ignore (Ojs.call x78 "push" [|(Ojs.int_to_js x79)|]) | None -> ()); x78))|]) let default1 : ?x:int -> unit -> unit = fun ?x:(x81 : int option) () -> ignore (Ojs.call Ojs.global "default1" [|(Ojs.int_to_js (match x81 with | Some x82 -> x82 | None -> 42))|]) let builder0 : unit -> Ojs.t = fun () -> let x83 = Ojs.empty_obj () in x83 let builder1 : x:int -> Ojs.t = fun ~x:(x84 : int) -> let x85 = Ojs.empty_obj () in Ojs.set_prop_ascii x85 "x" (Ojs.int_to_js x84); x85 let builder2 : ?x:int -> ?y:string -> unit -> Ojs.t = fun ?x:(x86 : int option) ?y:(x87 : string option) () -> let x88 = Ojs.empty_obj () in (match x86 with | Some x90 -> Ojs.set_prop_ascii x88 "x" (Ojs.int_to_js x90) | None -> ()); (match x87 with | Some x89 -> Ojs.set_prop_ascii x88 "y" (Ojs.string_to_js x89) | None -> ()); x88 let builder3 : x:int -> y:string -> unit -> Ojs.t = fun ~x:(x91 : int) ~y:(x92 : string) () -> let x93 = Ojs.empty_obj () in Ojs.set_prop_ascii x93 "x" (Ojs.int_to_js x91); Ojs.set_prop_ascii x93 "y" (Ojs.string_to_js x92); x93 let builder4 : x:int -> y:string -> z:unit -> Ojs.t = fun ~x:(x94 : int) ~y:(x95 : string) ~z:(x96 : unit) -> let x97 = Ojs.empty_obj () in Ojs.set_prop_ascii x97 "x" (Ojs.int_to_js x94); Ojs.set_prop_ascii x97 "y" (Ojs.string_to_js x95); Ojs.set_prop_ascii x97 "z" (Ojs.unit_to_js x96); x97 let builder5 : ?x:int -> ?y:string -> unit -> Ojs.t = fun ?x:(x98 : int option) ?y:(x99 : string option) () -> let x100 = Ojs.empty_obj () in (match x98 with | Some x102 -> Ojs.set_prop_ascii x100 "x" (Ojs.int_to_js x102) | None -> ()); (match x99 with | Some x101 -> Ojs.set_prop_ascii x100 "y" (Ojs.string_to_js x101) | None -> ()); x100 let builder6 : ?x:int -> ?y:string -> ?z:int -> unit -> Ojs.t = fun ?x:(x103 : int option) ?y:(x104 : string option) ?z:(x105 : int option) () -> let x106 = Ojs.empty_obj () in Ojs.set_prop_ascii x106 "x" (Ojs.int_to_js (match x103 with | Some x109 -> x109 | None -> 42)); Ojs.set_prop_ascii x106 "y" (Ojs.string_to_js (match x104 with | Some x108 -> x108 | None -> "42")); (match x105 with | Some x107 -> Ojs.set_prop_ascii x106 "z" (Ojs.int_to_js x107) | None -> ()); x106 let sep : string -> string list -> string = fun (x110 : string) (x111 : string list) -> Ojs.string_of_js (let x114 = Ojs.global in Ojs.call (Ojs.get_prop_ascii x114 "sep") "apply" [|x114;((let x112 = Ojs.new_obj (Ojs.get_prop_ascii Ojs.global "Array") [||] in ignore (Ojs.call x112 "push" [|(Ojs.string_to_js x110)|]); List.iter (fun (x113 : string) -> ignore (Ojs.call x112 "push" [|(Ojs.string_to_js x113)|])) x111; x112))|]) end)[@merlin.hide ]) module T : sig type js = private Ojs.t type abstract type alias = js type private_alias = private alias type record = { x: js ; y: js } type mutable_record = { mutable x: js ; y: js } type record_relabel = { x: int ; y: int } type ('a, 'b) parametrized = { x: 'a ; y: 'b } type 'a abs = ('a -> int) -> unit type specialized = (int, int) parametrized type enum = | Foo | Bar | Baz | Qux type status = | OK | KO | OO | OtherS of string | OtherI of int type poly = [ `foo | `bar | `baz | `Qux | `I of int | `S of string ] type sum = | A | B of int | C of int * string | D of { age: int ; name: string } | Unknown of Ojs.t type t = | A | B of int | C of int * string | D of { age: int ; name: string } | E of int | Unknown of Ojs.t type union = | A | B of int | C of int | D of Ojs.t type poly_union = [ `A | `B of int | `C of int | `D of Ojs.t ] type discr_union = | A | B of int | C of int | D of Ojs.t type discr_poly_union = [ `A | `B of int | `C of int | `D of Ojs.t ] type discr_union_value = | A | B of int | C of int | D of Ojs.t module NestedScope0 : sig val f : string -> unit end module NestedScope1 : sig val f : string -> unit end module NestedScope2 : sig val f : string -> unit end end = ((struct [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type js = Ojs.t let rec js_of_js : Ojs.t -> js = fun (x116 : Ojs.t) -> x116 and js_to_js : js -> Ojs.t = fun (x115 : Ojs.t) -> x115 type abstract = Ojs.t let rec abstract_of_js : Ojs.t -> abstract = fun (x118 : Ojs.t) -> x118 and abstract_to_js : abstract -> Ojs.t = fun (x117 : Ojs.t) -> x117 type alias = js let rec alias_of_js : Ojs.t -> alias = fun (x120 : Ojs.t) -> js_of_js x120 and alias_to_js : alias -> Ojs.t = fun (x119 : js) -> js_to_js x119 type private_alias = alias let rec private_alias_of_js : Ojs.t -> private_alias = fun (x122 : Ojs.t) -> alias_of_js x122 and private_alias_to_js : private_alias -> Ojs.t = fun (x121 : alias) -> alias_to_js x121 type record = { x: js ; y: js } let rec record_of_js : Ojs.t -> record = fun (x124 : Ojs.t) -> { x = (js_of_js (Ojs.get_prop_ascii x124 "x")); y = (js_of_js (Ojs.get_prop_ascii x124 "y")) } and record_to_js : record -> Ojs.t = fun (x123 : record) -> Ojs.obj [|("x", (js_to_js x123.x));("y", (js_to_js x123.y))|] type mutable_record = { mutable x: js ; y: js } let rec mutable_record_of_js : Ojs.t -> mutable_record = fun (x126 : Ojs.t) -> { x = (js_of_js (Ojs.get_prop_ascii x126 "x")); y = (js_of_js (Ojs.get_prop_ascii x126 "y")) } and mutable_record_to_js : mutable_record -> Ojs.t = fun (x125 : mutable_record) -> Ojs.obj [|("x", (js_to_js x125.x));("y", (js_to_js x125.y))|] type record_relabel = { x: int ; y: int } let rec record_relabel_of_js : Ojs.t -> record_relabel = fun (x128 : Ojs.t) -> { x = (Ojs.int_of_js (Ojs.get_prop_ascii x128 "x")); y = (Ojs.int_of_js (Ojs.get_prop_ascii x128 "Y")) } and record_relabel_to_js : record_relabel -> Ojs.t = fun (x127 : record_relabel) -> Ojs.obj [|("x", (Ojs.int_to_js x127.x));("Y", (Ojs.int_to_js x127.y))|] type ('a, 'b) parametrized = { x: 'a ; y: 'b } let rec parametrized_of_js : 'a 'b . (Ojs.t -> 'a) -> (Ojs.t -> 'b) -> Ojs.t -> ('a, 'b) parametrized = fun (type __a) (type __b) (__a_of_js : Ojs.t -> __a) (__b_of_js : Ojs.t -> __b) -> fun (x130 : Ojs.t) -> { x = (__a_of_js (Ojs.get_prop_ascii x130 "x")); y = (__b_of_js (Ojs.get_prop_ascii x130 "y")) } and parametrized_to_js : 'a 'b . ('a -> Ojs.t) -> ('b -> Ojs.t) -> ('a, 'b) parametrized -> Ojs.t = fun (type __a) (type __b) (__a_to_js : __a -> Ojs.t) (__b_to_js : __b -> Ojs.t) -> fun (x129 : (__a, __b) parametrized) -> Ojs.obj [|("x", (__a_to_js x129.x));("y", (__b_to_js x129.y))|] type 'a abs = ('a -> int) -> unit let rec abs_of_js : 'a . (Ojs.t -> 'a) -> Ojs.t -> 'a abs = fun (type __a) (__a_of_js : Ojs.t -> __a) -> fun (x134 : Ojs.t) (x135 : __a -> int) -> ignore (Ojs.apply x134 [|(Ojs.fun_to_js 1 (fun (x136 : Ojs.t) -> Ojs.int_to_js (x135 (__a_of_js x136))))|]) and abs_to_js : 'a . ('a -> Ojs.t) -> 'a abs -> Ojs.t = fun (type __a) (__a_to_js : __a -> Ojs.t) -> fun (x131 : (__a -> int) -> unit) -> Ojs.fun_to_js 1 (fun (x132 : Ojs.t) -> x131 (fun (x133 : __a) -> Ojs.int_of_js (Ojs.apply x132 [|(__a_to_js x133)|]))) type specialized = (int, int) parametrized let rec specialized_of_js : Ojs.t -> specialized = fun (x140 : Ojs.t) -> parametrized_of_js Ojs.int_of_js Ojs.int_of_js x140 and specialized_to_js : specialized -> Ojs.t = fun (x137 : (int, int) parametrized) -> parametrized_to_js Ojs.int_to_js Ojs.int_to_js x137 type enum = | Foo | Bar | Baz | Qux let rec enum_of_js : Ojs.t -> enum = fun (x144 : Ojs.t) -> let x145 = x144 in match Ojs.type_of x145 with | "number" -> (match Ojs.float_of_js x145 with | 4.2 -> Baz | _ -> (match Ojs.int_of_js x145 with | 42 -> Bar | _ -> assert false)) | "string" -> (match Ojs.string_of_js x145 with | "foo" -> Foo | "Qux" -> Qux | _ -> assert false) | _ -> assert false and enum_to_js : enum -> Ojs.t = fun (x143 : enum) -> match x143 with | Foo -> Ojs.string_to_js "foo" | Bar -> Ojs.int_to_js 42 | Baz -> Ojs.float_to_js 4.2 | Qux -> Ojs.string_to_js "Qux" type status = | OK | KO | OO | OtherS of string | OtherI of int let rec status_of_js : Ojs.t -> status = fun (x149 : Ojs.t) -> let x150 = x149 in match Ojs.type_of x150 with | "number" -> (match Ojs.float_of_js x150 with | 1.5 -> OO | _ -> (match Ojs.int_of_js x150 with | 1 -> OK | 2 -> KO | x152 -> OtherI x152)) | "string" -> (match Ojs.string_of_js x150 with | x151 -> OtherS x151) | _ -> assert false and status_to_js : status -> Ojs.t = fun (x146 : status) -> match x146 with | OK -> Ojs.int_to_js 1 | KO -> Ojs.int_to_js 2 | OO -> Ojs.float_to_js 1.5 | OtherS x147 -> Ojs.string_to_js x147 | OtherI x148 -> Ojs.int_to_js x148 type poly = [ `foo | `bar | `baz | `Qux | `I of int | `S of string ] let rec poly_of_js : Ojs.t -> poly = fun (x156 : Ojs.t) -> let x157 = x156 in match Ojs.type_of x157 with | "number" -> (match Ojs.float_of_js x157 with | 4.2 -> `baz | _ -> (match Ojs.int_of_js x157 with | 42 -> `bar | x158 -> `I x158)) | "string" -> (match Ojs.string_of_js x157 with | "foo" -> `foo | "Qux" -> `Qux | x159 -> `S x159) | _ -> assert false and poly_to_js : poly -> Ojs.t = fun (x153 : [ `foo | `bar | `baz | `Qux | `I of int | `S of string ]) -> match x153 with | `foo -> Ojs.string_to_js "foo" | `bar -> Ojs.int_to_js 42 | `baz -> Ojs.float_to_js 4.2 | `Qux -> Ojs.string_to_js "Qux" | `I x154 -> Ojs.int_to_js x154 | `S x155 -> Ojs.string_to_js x155 type sum = | A | B of int | C of int * string | D of { age: int ; name: string } | Unknown of Ojs.t let rec sum_of_js : Ojs.t -> sum = fun (x167 : Ojs.t) -> let x168 = x167 in match Ojs.type_of (Ojs.get_prop_ascii x168 "kind") with | "number" -> Unknown x168 | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x168 "kind") with | "A" -> A | "B" -> B (Ojs.int_of_js (Ojs.get_prop_ascii x168 "arg")) | "C" -> C ((Ojs.int_of_js (Ojs.array_get (Ojs.get_prop_ascii x168 "arg") 0)), (Ojs.string_of_js (Ojs.array_get (Ojs.get_prop_ascii x168 "arg") 1))) | "D" -> D { age = (Ojs.int_of_js (Ojs.get_prop_ascii x168 "age")); name = (Ojs.string_of_js (Ojs.get_prop_ascii x168 "name")) } | _ -> Unknown x168) | "boolean" -> Unknown x168 | _ -> Unknown x168 and sum_to_js : sum -> Ojs.t = fun (x160 : sum) -> match x160 with | A -> Ojs.obj [|("kind", (Ojs.string_to_js "A"))|] | B x161 -> Ojs.obj [|("kind", (Ojs.string_to_js "B"));("arg", (Ojs.int_to_js x161))|] | C (x162, x163) -> let x164 = Ojs.array_make 2 in (Ojs.array_set x164 1 (Ojs.string_to_js x163); Ojs.array_set x164 0 (Ojs.int_to_js x162); Ojs.obj [|("kind", (Ojs.string_to_js "C"));("arg", x164)|]) | D x165 -> Ojs.obj [|("kind", (Ojs.string_to_js "D"));("age", (Ojs.int_to_js x165.age)); ("name", (Ojs.string_to_js x165.name))|] | Unknown x166 -> Ojs.obj [|("kind", (Ojs.string_to_js "Unknown"));("arg", x166)|] type t = | A | B of int | C of int * string | D of { age: int ; name: string } | E of int | Unknown of Ojs.t let rec t_of_js : Ojs.t -> t = fun (x177 : Ojs.t) -> let x178 = x177 in match Ojs.type_of (Ojs.get_prop_ascii x178 "kind") with | "number" -> Unknown x178 | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x178 "kind") with | "A" -> A | "B" -> B (Ojs.int_of_js (Ojs.get_prop_ascii x178 "bArg")) | "C" -> C ((Ojs.int_of_js (Ojs.array_get (Ojs.get_prop_ascii x178 "cArg") 0)), (Ojs.string_of_js (Ojs.array_get (Ojs.get_prop_ascii x178 "cArg") 1))) | "D" -> D { age = (Ojs.int_of_js (Ojs.get_prop_ascii x178 "X")); name = (Ojs.string_of_js (Ojs.get_prop_ascii x178 "Y")) } | "F" -> E (Ojs.int_of_js (Ojs.get_prop_ascii x178 "fArg")) | _ -> Unknown x178) | "boolean" -> Unknown x178 | _ -> Unknown x178 and t_to_js : t -> Ojs.t = fun (x169 : t) -> match x169 with | A -> Ojs.obj [|("kind", (Ojs.string_to_js "A"))|] | B x170 -> Ojs.obj [|("kind", (Ojs.string_to_js "B"));("bArg", (Ojs.int_to_js x170))|] | C (x171, x172) -> let x173 = Ojs.array_make 2 in (Ojs.array_set x173 1 (Ojs.string_to_js x172); Ojs.array_set x173 0 (Ojs.int_to_js x171); Ojs.obj [|("kind", (Ojs.string_to_js "C"));("cArg", x173)|]) | D x174 -> Ojs.obj [|("kind", (Ojs.string_to_js "D"));("X", (Ojs.int_to_js x174.age)); ("Y", (Ojs.string_to_js x174.name))|] | E x175 -> Ojs.obj [|("kind", (Ojs.string_to_js "F"));("fArg", (Ojs.int_to_js x175))|] | Unknown x176 -> Ojs.obj [|("kind", (Ojs.string_to_js "Unknown"));("arg", x176)|] type union = | A | B of int | C of int | D of Ojs.t let rec union_to_js : union -> Ojs.t = fun (x179 : union) -> match x179 with | A -> Ojs.null | B x180 -> Ojs.int_to_js x180 | C x181 -> Ojs.int_to_js x181 | D x182 -> x182 type poly_union = [ `A | `B of int | `C of int | `D of Ojs.t ] let rec poly_union_to_js : poly_union -> Ojs.t = fun (x185 : [ `A | `B of int | `C of int | `D of Ojs.t ]) -> match x185 with | `A -> Ojs.null | `B x186 -> Ojs.int_to_js x186 | `C x187 -> Ojs.int_to_js x187 | `D x188 -> x188 type discr_union = | A | B of int | C of int | D of Ojs.t let rec discr_union_of_js : Ojs.t -> discr_union = fun (x195 : Ojs.t) -> let x196 = x195 in match Ojs.type_of (Ojs.get_prop_ascii x196 "discr") with | "number" -> D x196 | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x196 "discr") with | "A" -> A | "B" -> B (Ojs.int_of_js x196) | "C" -> C (Ojs.int_of_js x196) | _ -> D x196) | "boolean" -> D x196 | _ -> D x196 and discr_union_to_js : discr_union -> Ojs.t = fun (x191 : discr_union) -> match x191 with | A -> Ojs.null | B x192 -> Ojs.int_to_js x192 | C x193 -> Ojs.int_to_js x193 | D x194 -> x194 type discr_poly_union = [ `A | `B of int | `C of int | `D of Ojs.t ] let rec discr_poly_union_of_js : Ojs.t -> discr_poly_union = fun (x201 : Ojs.t) -> let x202 = x201 in match Ojs.type_of (Ojs.get_prop_ascii x202 "discr") with | "number" -> `D x202 | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x202 "discr") with | "A" -> `A | "B" -> `B (Ojs.int_of_js x202) | "C" -> `C (Ojs.int_of_js x202) | _ -> `D x202) | "boolean" -> `D x202 | _ -> `D x202 and discr_poly_union_to_js : discr_poly_union -> Ojs.t = fun (x197 : [ `A | `B of int | `C of int | `D of Ojs.t ]) -> match x197 with | `A -> Ojs.null | `B x198 -> Ojs.int_to_js x198 | `C x199 -> Ojs.int_to_js x199 | `D x200 -> x200 type discr_union_value = | A | B of int | C of int | D of Ojs.t let rec discr_union_value_of_js : Ojs.t -> discr_union_value = fun (x207 : Ojs.t) -> let x208 = x207 in match Ojs.type_of (Ojs.get_prop_ascii x208 "discr") with | "number" -> (match Ojs.int_of_js (Ojs.get_prop_ascii x208 "discr") with | 0 -> A | _ -> D x208) | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x208 "discr") with | "42" -> B (Ojs.int_of_js x208) | "C" -> C (Ojs.int_of_js x208) | _ -> D x208) | "boolean" -> D x208 | _ -> D x208 and discr_union_value_to_js : discr_union_value -> Ojs.t = fun (x203 : discr_union_value) -> match x203 with | A -> Ojs.null | B x204 -> Ojs.int_to_js x204 | C x205 -> Ojs.int_to_js x205 | D x206 -> x206 module NestedScope0 = struct let f : string -> unit = fun (x209 : string) -> ignore (Ojs.call (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "outer") "inner") "f" [|(Ojs.string_to_js x209)|]) end module NestedScope1 = struct let f : string -> unit = fun (x210 : string) -> ignore (Ojs.call (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "outer") "inner") "f" [|(Ojs.string_to_js x210)|]) end module NestedScope2 = struct let f : string -> unit = fun (x211 : string) -> ignore (Ojs.call (Ojs.get_prop_ascii (Ojs.get_prop_ascii Ojs.global "outer") "inner") "f" [|(Ojs.string_to_js x211)|]) end end)[@merlin.hide ]) [@@ocaml.doc " Types Declarations "] ================================================ FILE: ppx-test/expected/union_and_enum.ml ================================================ [@@@js.dummy "!! This code has been generated by gen_js_api !!"] [@@@ocaml.warning "-7-32-39"] type enum_int = | Enum_int_0 | Enum_int_1 | Enum_int_other of int let rec enum_int_of_js : Ojs.t -> enum_int = fun (x3 : Ojs.t) -> let x4 = x3 in match Ojs.int_of_js x4 with | 0 -> Enum_int_0 | 1 -> Enum_int_1 | x5 -> Enum_int_other x5 and enum_int_to_js : enum_int -> Ojs.t = fun (x1 : enum_int) -> match x1 with | Enum_int_0 -> Ojs.int_to_js 0 | Enum_int_1 -> Ojs.int_to_js 1 | Enum_int_other x2 -> Ojs.int_to_js x2 type enum_float = | Enum_float_0_1 | Enum_float_1_1 | Enum_float_other of float let rec enum_float_of_js : Ojs.t -> enum_float = fun (x8 : Ojs.t) -> let x9 = x8 in match Ojs.float_of_js x9 with | 0.1 -> Enum_float_0_1 | 1.1 -> Enum_float_1_1 | x10 -> Enum_float_other x10 and enum_float_to_js : enum_float -> Ojs.t = fun (x6 : enum_float) -> match x6 with | Enum_float_0_1 -> Ojs.float_to_js 0.1 | Enum_float_1_1 -> Ojs.float_to_js 1.1 | Enum_float_other x7 -> Ojs.float_to_js x7 type enum_number_1 = | Enum_number_0 | Enum_number_1 | Enum_number_0_1 | Enum_number_1_1 | Enum_number_other of int let rec enum_number_1_of_js : Ojs.t -> enum_number_1 = fun (x13 : Ojs.t) -> let x14 = x13 in match Ojs.float_of_js x14 with | 0.1 -> Enum_number_0_1 | 1.1 -> Enum_number_1_1 | _ -> (match Ojs.int_of_js x14 with | 0 -> Enum_number_0 | 1 -> Enum_number_1 | x15 -> Enum_number_other x15) and enum_number_1_to_js : enum_number_1 -> Ojs.t = fun (x11 : enum_number_1) -> match x11 with | Enum_number_0 -> Ojs.int_to_js 0 | Enum_number_1 -> Ojs.int_to_js 1 | Enum_number_0_1 -> Ojs.float_to_js 0.1 | Enum_number_1_1 -> Ojs.float_to_js 1.1 | Enum_number_other x12 -> Ojs.int_to_js x12 type enum_number_2 = | Enum_number_0 | Enum_number_1 | Enum_number_0_1 | Enum_number_1_1 | Enum_number_other of float let rec enum_number_2_of_js : Ojs.t -> enum_number_2 = fun (x18 : Ojs.t) -> let x19 = x18 in match Ojs.float_of_js x19 with | 0.1 -> Enum_number_0_1 | 1.1 -> Enum_number_1_1 | x20 -> (match Ojs.int_of_js x19 with | 0 -> Enum_number_0 | 1 -> Enum_number_1 | _ -> Enum_number_other x20) and enum_number_2_to_js : enum_number_2 -> Ojs.t = fun (x16 : enum_number_2) -> match x16 with | Enum_number_0 -> Ojs.int_to_js 0 | Enum_number_1 -> Ojs.int_to_js 1 | Enum_number_0_1 -> Ojs.float_to_js 0.1 | Enum_number_1_1 -> Ojs.float_to_js 1.1 | Enum_number_other x17 -> Ojs.float_to_js x17 type enum_string = | Enum_string_foo | Enum_string_bar | Enum_string_other of string let rec enum_string_of_js : Ojs.t -> enum_string = fun (x23 : Ojs.t) -> let x24 = x23 in match Ojs.string_of_js x24 with | "foo" -> Enum_string_foo | "bar" -> Enum_string_bar | x25 -> Enum_string_other x25 and enum_string_to_js : enum_string -> Ojs.t = fun (x21 : enum_string) -> match x21 with | Enum_string_foo -> Ojs.string_to_js "foo" | Enum_string_bar -> Ojs.string_to_js "bar" | Enum_string_other x22 -> Ojs.string_to_js x22 type enum_bool = | Enum_bool_true | Enum_bool_false let rec enum_bool_of_js : Ojs.t -> enum_bool = fun (x27 : Ojs.t) -> let x28 = x27 in match Ojs.bool_of_js x28 with | true -> Enum_bool_true | false -> Enum_bool_false and enum_bool_to_js : enum_bool -> Ojs.t = fun (x26 : enum_bool) -> match x26 with | Enum_bool_true -> Ojs.bool_to_js true | Enum_bool_false -> Ojs.bool_to_js false type enum_bool_partial = | Enum_bool_true let rec enum_bool_partial_of_js : Ojs.t -> enum_bool_partial = fun (x30 : Ojs.t) -> let x31 = x30 in match Ojs.bool_of_js x31 with | true -> Enum_bool_true | _ -> assert false and enum_bool_partial_to_js : enum_bool_partial -> Ojs.t = fun (x29 : enum_bool_partial) -> match x29 with | Enum_bool_true -> Ojs.bool_to_js true type enum_bool_partial2 = | Enum_bool_true | Enum_bool_other of bool let rec enum_bool_partial2_of_js : Ojs.t -> enum_bool_partial2 = fun (x34 : Ojs.t) -> let x35 = x34 in match Ojs.bool_of_js x35 with | true -> Enum_bool_true | x36 -> Enum_bool_other x36 and enum_bool_partial2_to_js : enum_bool_partial2 -> Ojs.t = fun (x32 : enum_bool_partial2) -> match x32 with | Enum_bool_true -> Ojs.bool_to_js true | Enum_bool_other x33 -> Ojs.bool_to_js x33 type enum_mixed = | Enum_int_0 | Enum_int_1 | Enum_float_0_1 | Enum_float_1_1 | Enum_number_other of int | Enum_string_foo | Enum_string_bar | Enum_string_other of string | Enum_bool_true | Enum_bool_false let rec enum_mixed_of_js : Ojs.t -> enum_mixed = fun (x40 : Ojs.t) -> let x41 = x40 in match Ojs.type_of x41 with | "number" -> (match Ojs.float_of_js x41 with | 0.1 -> Enum_float_0_1 | 1.1 -> Enum_float_1_1 | _ -> (match Ojs.int_of_js x41 with | 0 -> Enum_int_0 | 1 -> Enum_int_1 | x42 -> Enum_number_other x42)) | "string" -> (match Ojs.string_of_js x41 with | "foo" -> Enum_string_foo | "bar" -> Enum_string_bar | x43 -> Enum_string_other x43) | "boolean" -> (match Ojs.bool_of_js x41 with | true -> Enum_bool_true | false -> Enum_bool_false) | _ -> assert false and enum_mixed_to_js : enum_mixed -> Ojs.t = fun (x37 : enum_mixed) -> match x37 with | Enum_int_0 -> Ojs.int_to_js 0 | Enum_int_1 -> Ojs.int_to_js 1 | Enum_float_0_1 -> Ojs.float_to_js 0.1 | Enum_float_1_1 -> Ojs.float_to_js 1.1 | Enum_number_other x38 -> Ojs.int_to_js x38 | Enum_string_foo -> Ojs.string_to_js "foo" | Enum_string_bar -> Ojs.string_to_js "bar" | Enum_string_other x39 -> Ojs.string_to_js x39 | Enum_bool_true -> Ojs.bool_to_js true | Enum_bool_false -> Ojs.bool_to_js false type enum_mixed_partial_bool = | Enum_int_0 | Enum_int_1 | Enum_float_0_1 | Enum_float_1_1 | Enum_number_other of float | Enum_string_foo | Enum_string_bar | Enum_string_other of string | Enum_bool_true let rec enum_mixed_partial_bool_of_js : Ojs.t -> enum_mixed_partial_bool = fun (x47 : Ojs.t) -> let x48 = x47 in match Ojs.type_of x48 with | "number" -> (match Ojs.float_of_js x48 with | 0.1 -> Enum_float_0_1 | 1.1 -> Enum_float_1_1 | x49 -> (match Ojs.int_of_js x48 with | 0 -> Enum_int_0 | 1 -> Enum_int_1 | _ -> Enum_number_other x49)) | "string" -> (match Ojs.string_of_js x48 with | "foo" -> Enum_string_foo | "bar" -> Enum_string_bar | x50 -> Enum_string_other x50) | "boolean" -> (match Ojs.bool_of_js x48 with | true -> Enum_bool_true | _ -> assert false) | _ -> assert false and enum_mixed_partial_bool_to_js : enum_mixed_partial_bool -> Ojs.t = fun (x44 : enum_mixed_partial_bool) -> match x44 with | Enum_int_0 -> Ojs.int_to_js 0 | Enum_int_1 -> Ojs.int_to_js 1 | Enum_float_0_1 -> Ojs.float_to_js 0.1 | Enum_float_1_1 -> Ojs.float_to_js 1.1 | Enum_number_other x45 -> Ojs.float_to_js x45 | Enum_string_foo -> Ojs.string_to_js "foo" | Enum_string_bar -> Ojs.string_to_js "bar" | Enum_string_other x46 -> Ojs.string_to_js x46 | Enum_bool_true -> Ojs.bool_to_js true type enum_mixed_partial_bool2 = | Enum_int_0 | Enum_int_1 | Enum_float_0_1 | Enum_float_1_1 | Enum_number_other of float | Enum_string_foo | Enum_string_bar | Enum_string_other of string | Enum_bool_true | Enum_bool_other of bool let rec enum_mixed_partial_bool2_of_js : Ojs.t -> enum_mixed_partial_bool2 = fun (x55 : Ojs.t) -> let x56 = x55 in match Ojs.type_of x56 with | "number" -> (match Ojs.float_of_js x56 with | 0.1 -> Enum_float_0_1 | 1.1 -> Enum_float_1_1 | x57 -> (match Ojs.int_of_js x56 with | 0 -> Enum_int_0 | 1 -> Enum_int_1 | _ -> Enum_number_other x57)) | "string" -> (match Ojs.string_of_js x56 with | "foo" -> Enum_string_foo | "bar" -> Enum_string_bar | x58 -> Enum_string_other x58) | "boolean" -> (match Ojs.bool_of_js x56 with | true -> Enum_bool_true | x59 -> Enum_bool_other x59) | _ -> assert false and enum_mixed_partial_bool2_to_js : enum_mixed_partial_bool2 -> Ojs.t = fun (x51 : enum_mixed_partial_bool2) -> match x51 with | Enum_int_0 -> Ojs.int_to_js 0 | Enum_int_1 -> Ojs.int_to_js 1 | Enum_float_0_1 -> Ojs.float_to_js 0.1 | Enum_float_1_1 -> Ojs.float_to_js 1.1 | Enum_number_other x52 -> Ojs.float_to_js x52 | Enum_string_foo -> Ojs.string_to_js "foo" | Enum_string_bar -> Ojs.string_to_js "bar" | Enum_string_other x53 -> Ojs.string_to_js x53 | Enum_bool_true -> Ojs.bool_to_js true | Enum_bool_other x54 -> Ojs.bool_to_js x54 type dummy1 = Ojs.t let rec dummy1_of_js : Ojs.t -> dummy1 = fun (x61 : Ojs.t) -> x61 and dummy1_to_js : dummy1 -> Ojs.t = fun (x60 : Ojs.t) -> x60 type dummy2 = Ojs.t let rec dummy2_of_js : Ojs.t -> dummy2 = fun (x63 : Ojs.t) -> x63 and dummy2_to_js : dummy2 -> Ojs.t = fun (x62 : Ojs.t) -> x62 type dummy3 = Ojs.t let rec dummy3_of_js : Ojs.t -> dummy3 = fun (x65 : Ojs.t) -> x65 and dummy3_to_js : dummy3 -> Ojs.t = fun (x64 : Ojs.t) -> x64 type dummy4 = Ojs.t let rec dummy4_of_js : Ojs.t -> dummy4 = fun (x67 : Ojs.t) -> x67 and dummy4_to_js : dummy4 -> Ojs.t = fun (x66 : Ojs.t) -> x66 type dummy5 = Ojs.t let rec dummy5_of_js : Ojs.t -> dummy5 = fun (x69 : Ojs.t) -> x69 and dummy5_to_js : dummy5 -> Ojs.t = fun (x68 : Ojs.t) -> x68 type dummy6 = Ojs.t let rec dummy6_of_js : Ojs.t -> dummy6 = fun (x71 : Ojs.t) -> x71 and dummy6_to_js : dummy6 -> Ojs.t = fun (x70 : Ojs.t) -> x70 type union_int = | Union_int_0 of dummy1 | Union_int_1 of dummy2 | Unknown of Ojs.t let rec union_int_of_js : Ojs.t -> union_int = fun (x76 : Ojs.t) -> let x77 = x76 in match Ojs.type_of (Ojs.get_prop_ascii x77 "tag") with | "number" -> (match Ojs.int_of_js (Ojs.get_prop_ascii x77 "tag") with | 0 -> Union_int_0 (dummy1_of_js x77) | 1 -> Union_int_1 (dummy2_of_js x77) | _ -> Unknown x77) | "string" -> Unknown x77 | "boolean" -> Unknown x77 | _ -> Unknown x77 and union_int_to_js : union_int -> Ojs.t = fun (x72 : union_int) -> match x72 with | Union_int_0 x73 -> dummy1_to_js x73 | Union_int_1 x74 -> dummy2_to_js x74 | Unknown x75 -> x75 type union_float = | Union_float_0_1 of dummy1 | Union_float_1_1 of dummy2 | Unknown of Ojs.t let rec union_float_of_js : Ojs.t -> union_float = fun (x82 : Ojs.t) -> let x83 = x82 in match Ojs.type_of (Ojs.get_prop_ascii x83 "tag") with | "number" -> (match Ojs.float_of_js (Ojs.get_prop_ascii x83 "tag") with | 0.1 -> Union_float_0_1 (dummy1_of_js x83) | 1.1 -> Union_float_1_1 (dummy2_of_js x83) | _ -> Unknown x83) | "string" -> Unknown x83 | "boolean" -> Unknown x83 | _ -> Unknown x83 and union_float_to_js : union_float -> Ojs.t = fun (x78 : union_float) -> match x78 with | Union_float_0_1 x79 -> dummy1_to_js x79 | Union_float_1_1 x80 -> dummy2_to_js x80 | Unknown x81 -> x81 type union_string = | Union_string_foo of dummy3 | Union_string_bar of dummy4 | Unknown of Ojs.t let rec union_string_of_js : Ojs.t -> union_string = fun (x88 : Ojs.t) -> let x89 = x88 in match Ojs.type_of (Ojs.get_prop_ascii x89 "tag") with | "number" -> Unknown x89 | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x89 "tag") with | "foo" -> Union_string_foo (dummy3_of_js x89) | "bar" -> Union_string_bar (dummy4_of_js x89) | _ -> Unknown x89) | "boolean" -> Unknown x89 | _ -> Unknown x89 and union_string_to_js : union_string -> Ojs.t = fun (x84 : union_string) -> match x84 with | Union_string_foo x85 -> dummy3_to_js x85 | Union_string_bar x86 -> dummy4_to_js x86 | Unknown x87 -> x87 type union_bool = | Union_bool_true of dummy5 | Union_bool_false of dummy6 let rec union_bool_of_js : Ojs.t -> union_bool = fun (x93 : Ojs.t) -> let x94 = x93 in match Ojs.bool_of_js (Ojs.get_prop_ascii x94 "tag") with | true -> Union_bool_true (dummy5_of_js x94) | false -> Union_bool_false (dummy6_of_js x94) and union_bool_to_js : union_bool -> Ojs.t = fun (x90 : union_bool) -> match x90 with | Union_bool_true x91 -> dummy5_to_js x91 | Union_bool_false x92 -> dummy6_to_js x92 type union_bool_partial = | Union_bool_true of dummy5 let rec union_bool_partial_of_js : Ojs.t -> union_bool_partial = fun (x97 : Ojs.t) -> let x98 = x97 in match Ojs.bool_of_js (Ojs.get_prop_ascii x98 "tag") with | true -> Union_bool_true (dummy5_of_js x98) | _ -> assert false and union_bool_partial_to_js : union_bool_partial -> Ojs.t = fun (x95 : union_bool_partial) -> match x95 with | Union_bool_true x96 -> dummy5_to_js x96 type union_bool_partial2 = | Union_bool_true of dummy5 | Unknown of Ojs.t let rec union_bool_partial2_of_js : Ojs.t -> union_bool_partial2 = fun (x102 : Ojs.t) -> let x103 = x102 in match Ojs.type_of (Ojs.get_prop_ascii x103 "tag") with | "number" -> Unknown x103 | "string" -> Unknown x103 | "boolean" -> (match Ojs.bool_of_js (Ojs.get_prop_ascii x103 "tag") with | true -> Union_bool_true (dummy5_of_js x103) | _ -> Unknown x103) | _ -> Unknown x103 and union_bool_partial2_to_js : union_bool_partial2 -> Ojs.t = fun (x99 : union_bool_partial2) -> match x99 with | Union_bool_true x100 -> dummy5_to_js x100 | Unknown x101 -> x101 type union_mixed = | Union_int_0 of dummy1 | Union_int_1 of dummy2 | Union_float_0_1 of dummy1 | Union_float_1_1 of dummy2 | Union_string_foo of dummy3 | Union_string_bar of dummy4 | Union_bool_true of dummy5 | Union_bool_false of dummy6 | Unknown of Ojs.t let rec union_mixed_of_js : Ojs.t -> union_mixed = fun (x114 : Ojs.t) -> let x115 = x114 in match Ojs.type_of (Ojs.get_prop_ascii x115 "tag") with | "number" -> (match Ojs.float_of_js (Ojs.get_prop_ascii x115 "tag") with | 0.1 -> Union_float_0_1 (dummy1_of_js x115) | 1.1 -> Union_float_1_1 (dummy2_of_js x115) | _ -> (match Ojs.int_of_js (Ojs.get_prop_ascii x115 "tag") with | 0 -> Union_int_0 (dummy1_of_js x115) | 1 -> Union_int_1 (dummy2_of_js x115) | _ -> Unknown x115)) | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x115 "tag") with | "foo" -> Union_string_foo (dummy3_of_js x115) | "bar" -> Union_string_bar (dummy4_of_js x115) | _ -> Unknown x115) | "boolean" -> (match Ojs.bool_of_js (Ojs.get_prop_ascii x115 "tag") with | true -> Union_bool_true (dummy5_of_js x115) | false -> Union_bool_false (dummy6_of_js x115)) | _ -> Unknown x115 and union_mixed_to_js : union_mixed -> Ojs.t = fun (x104 : union_mixed) -> match x104 with | Union_int_0 x105 -> dummy1_to_js x105 | Union_int_1 x106 -> dummy2_to_js x106 | Union_float_0_1 x107 -> dummy1_to_js x107 | Union_float_1_1 x108 -> dummy2_to_js x108 | Union_string_foo x109 -> dummy3_to_js x109 | Union_string_bar x110 -> dummy4_to_js x110 | Union_bool_true x111 -> dummy5_to_js x111 | Union_bool_false x112 -> dummy6_to_js x112 | Unknown x113 -> x113 type union_mixed_partial_bool = | Union_int_0 of dummy1 | Union_int_1 of dummy2 | Union_float_0_1 of dummy1 | Union_float_1_1 of dummy2 | Union_string_foo of dummy3 | Union_string_bar of dummy4 | Union_bool_true of dummy5 | Unknown of Ojs.t let rec union_mixed_partial_bool_of_js : Ojs.t -> union_mixed_partial_bool = fun (x125 : Ojs.t) -> let x126 = x125 in match Ojs.type_of (Ojs.get_prop_ascii x126 "tag") with | "number" -> (match Ojs.float_of_js (Ojs.get_prop_ascii x126 "tag") with | 0.1 -> Union_float_0_1 (dummy1_of_js x126) | 1.1 -> Union_float_1_1 (dummy2_of_js x126) | _ -> (match Ojs.int_of_js (Ojs.get_prop_ascii x126 "tag") with | 0 -> Union_int_0 (dummy1_of_js x126) | 1 -> Union_int_1 (dummy2_of_js x126) | _ -> Unknown x126)) | "string" -> (match Ojs.string_of_js (Ojs.get_prop_ascii x126 "tag") with | "foo" -> Union_string_foo (dummy3_of_js x126) | "bar" -> Union_string_bar (dummy4_of_js x126) | _ -> Unknown x126) | "boolean" -> (match Ojs.bool_of_js (Ojs.get_prop_ascii x126 "tag") with | true -> Union_bool_true (dummy5_of_js x126) | _ -> Unknown x126) | _ -> Unknown x126 and union_mixed_partial_bool_to_js : union_mixed_partial_bool -> Ojs.t = fun (x116 : union_mixed_partial_bool) -> match x116 with | Union_int_0 x117 -> dummy1_to_js x117 | Union_int_1 x118 -> dummy2_to_js x118 | Union_float_0_1 x119 -> dummy1_to_js x119 | Union_float_1_1 x120 -> dummy2_to_js x120 | Union_string_foo x121 -> dummy3_to_js x121 | Union_string_bar x122 -> dummy4_to_js x122 | Union_bool_true x123 -> dummy5_to_js x123 | Unknown x124 -> x124 ================================================ FILE: ppx-test/extension.ml ================================================ let _ = [%js.of: int] let _ = [%js.of: int -> int] ================================================ FILE: ppx-test/first_class_modules.mli ================================================ module[@js.scope "console"] Console: sig val log: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.global "log"] val log2: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> 'a -> 'b -> unit [@@js.global "log"] val log3: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> (module[@js] Ojs.T with type t = 'c) -> 'a -> 'b -> 'c -> unit [@@js.global "log"] end module Console2: sig type t val log: (module[@js] Ojs.T with type t = 'a) -> t -> 'a -> unit [@@js.call "log"] val log2: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> t -> 'a -> 'b -> unit [@@js.call "log"] val log3: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> (module[@js] Ojs.T with type t = 'c) -> t -> 'a -> 'b -> 'c -> unit [@@js.call "log"] end module[@js.scope "console"] Console3: sig module [@js.scope "log"] Log: sig val _1: (module[@js] Ojs.T with type t = 'a) -> 'a -> unit [@@js.invoke] val _2: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> 'a -> 'b -> unit [@@js.invoke] val _3: (module[@js] Ojs.T with type t = 'a) -> (module[@js] Ojs.T with type t = 'b) -> (module[@js] Ojs.T with type t = 'c) -> 'a -> 'b -> 'c -> unit [@@js.invoke] end end module[@js.scope "Array"] Array: sig type 'a t val t_to_js: ('a -> Ojs.t) -> 'a t -> Ojs.t val t_of_js: (Ojs.t -> 'a) -> Ojs.t -> 'a t val create: (module[@js] Ojs.T with type t = 'a) -> ('a list [@js.variadic]) -> 'a t [@@js.create] val create': (module[@js] Ojs.T with type t = 'a) -> ('a list [@js.variadic]) -> 'a t [@@js.invoke] val push: (module[@js] Ojs.T with type t = 'a) -> 'a t -> 'a -> unit [@@js.call] val pop: (module[@js] Ojs.T with type t = 'a) -> 'a t -> 'a option [@@js.call] end ================================================ FILE: ppx-test/issues.ml ================================================ module Issue116 = [%js: type t] module Issue117 = [%js: module T: sig val log: 'a -> unit [@@js.global] val log2: 'a -> 'b -> unit [@@js.global "jsLog2"] end [@js.scope "console"] ] module Issue124 = [%js: type a and b = { a : a } [@@js.custom { to_js = (fun { a } -> [%js.of: a] a); of_js = (fun js -> { a = [%js.to: a] js}) }] type 'a dummy type 'a wrapped = | Wrapped of 'a [@@js.custom { to_js = ( let f a_to_js = function Wrapped a -> a_to_js a in f); of_js = ( let f a_of_js x = Wrapped (a_of_js x) in f ) }] type u = | Unknown of Ojs.t [@js.default] | T of t [@js "t"] | WrappedT of t wrapped [@js "wrapped_t"] [@@js.union on_field "type"] and t = [`U of u] dummy [@@js.custom { to_js = Obj.magic; of_js = Obj.magic }] type ('a, 'b) base = [ `BaseA of 'a | `BaseB of 'b ] dummy [@@js.custom { to_js = (fun _ _ -> Obj.magic); of_js = (fun _ _ -> Obj.magic) }] and base1 = (int, string) base and base2 = (string, int) base ] module Issue109 = [%js: type t = [ `S of string [@js.default] | `I of int [@js.default] ] [@@js.enum] ] module Issue142 = [%js: type t = [`Foo [@js 42]] [@js.enum] and u = t ] module Issue144 = [%js: type t val f: t -> (args:int -> int [@js.dummy]) [@@js.call "f"] ] module Issue146 = [%js: val f: ?arg:([`Foo [@js 42]] [@js.enum]) -> unit -> int [@@js.global "f"] ] module PR165 = [%js: module Markdown : sig type t end module [@js.scope "ParameterInformation"] ParameterInformation : sig type t val create: label:([`String of string | `Tuple of (int * int)] [@js.union]) -> ?documentation:([`String of string | `Markdown of Markdown.t] [@js.union]) -> unit -> t [@@js.create] end ] ================================================ FILE: ppx-test/issues_mli.mli ================================================ module Issue144: sig type t val f: t -> (args:int -> int [@js.dummy]) [@@js.call "f"] end ================================================ FILE: ppx-test/modules.mli ================================================ module Event: sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t end module Foo: sig module E = Event val foo: E.t -> string -> unit [@@js.call] end module Bar: sig include (module type of Event) val bar: t -> string -> unit [@@js.call] end ================================================ FILE: ppx-test/ppx/dune ================================================ (executable (name main) (libraries ppxlib gen_js_api_ppx_driver)) ================================================ FILE: ppx-test/ppx/main.ml ================================================ (* To run as a standalone binary, run the registered drivers *) let () = Ppxlib.Driver.standalone () ================================================ FILE: ppx-test/recursive_modules.mli ================================================ module [@js.scope "Foo"] rec Foo : sig type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val create: string -> t [@@js.create] val describe: t -> string [@@js.call "describe"] val to_bar: t -> Bar.t [@@js.call "toBar"] end and [@js.scope "Bar"] Bar : sig type t = private Ojs.t val t_of_js: Ojs.t -> t val t_to_js: t -> Ojs.t val create: string -> t [@@js.create] val describe: t -> string [@@js.call "describe"] val to_foo: t -> Foo.t [@@js.call "toFoo"] end ================================================ FILE: ppx-test/scoped.mli ================================================ (* The gen_js_api is released under the terms of an MIT-like license. *) (* See the attached LICENSE file. *) (* Copyright 2015 by LexiFi. *) [@@@js.implem [@@@warning "-22"]] module M : sig type t = private Ojs.t val t_to_js: t -> Ojs.t val t_of_js: Ojs.t -> t val prop_get_arg: t -> int val prop_get: unit -> int val set_prop: t -> int -> unit val set_global: int -> unit val new_thing_unit: unit -> t val new_thing_args: int -> t val method_call_global: t -> unit val method_call_unit: t -> unit -> int val method_call_args: t -> int -> int val method_call_unit_unit: t -> unit -> unit val method_call_args_unit: t -> int -> unit val global: t val invoke: unit -> unit [@@js.invoke] end[@js.scope "scope"] val d: unit -> unit [@@js.scope ("a", "b", "c")] [@@js.global] ================================================ FILE: ppx-test/types.ml ================================================ type 'a of_js = Ojs.t -> 'a type 'a to_js = 'a -> Ojs.t (** JS-able types *) let _ : string of_js = [%js.to: string] let _ : string to_js = [%js.of: string] let _ : int of_js = [%js.to: int] let _ : int to_js = [%js.of: int] let _ : bool of_js = [%js.to: bool] let _ : bool to_js = [%js.of: bool] let _ : float of_js = [%js.to: float] let _ : float to_js = [%js.of: float] let _ : Ojs.t of_js = [%js.to: Ojs.t] let _ : Ojs.t to_js = [%js.of: Ojs.t] let _ : (string * int) of_js = [%js.to: string * int] let _ : (string * int) to_js = [%js.of: string * int] let _ : (string * int * bool) of_js = [%js.to: string * int * bool] let _ : (string * int * bool) to_js = [%js.of: string * int * bool] let _ : (string -> int) of_js = [%js.to: string -> int] let _ : (string -> int) to_js = [%js.of: string -> int] let _ : ((string -> int) -> bool -> unit) of_js = [%js.to: (string -> int) -> bool -> unit] let _ : ((string -> int) -> bool -> unit) to_js = [%js.of: (string -> int) -> bool -> unit] let _ : (string array) of_js = [%js.to: string array] let _ : (string array) to_js = [%js.of: string array] let _ : (string list) of_js = [%js.to: string list] let _ : (string list) to_js = [%js.of: string list] let _ : (string option) of_js = [%js.to: string option] let _ : (string option) to_js = [%js.of: string option] let _ : (_ -> _) of_js = [%js.to: 'a -> 'b] let _ : (_ -> _) to_js = [%js.of: 'a -> 'b] let _ : [`foo | `bar | `Baz | `I of int | `S of string ] of_js = [%js.to: [`foo | `bar [@js 42] | `Baz | `I of int [@js.default] | `S of string[@js.default] ] [@js.enum]] let _ : [`foo | `bar | `Baz | `I of int | `S of string ] to_js = [%js.of: [`foo | `bar [@js 42] | `Baz | `I of int [@js.default] | `S of string[@js.default] ] [@js.enum]] (** Label & Options Value *) let _ : (label:int -> ?opt:int -> unit -> unit) of_js = [%js.to: label:int -> ?opt:int -> unit -> unit] let _ : (label:int -> ?opt:int -> unit -> unit) to_js = [%js.of: label:int -> ?opt:int -> unit -> unit] let _ : (label:int -> ?opt:int -> unit -> unit) of_js = [%js.to: label:int -> ?opt:int -> unit -> unit] (* js.default is ignored *) let _ : (label:int -> ?opt:int -> unit -> unit) to_js = [%js.of: label:int -> ?opt:int -> unit -> unit] (* js.default is ignored *) (** Functions *) module B = [%js: val default0: ?x:int -> unit -> unit [@@js.global] val default1: ?x:(int[@js.default 42]) -> unit -> unit [@@js.global] val builder0: unit -> Ojs.t [@@js.builder] val builder1: x:int -> Ojs.t [@@js.builder] val builder2: ?x:int -> ?y:string -> unit -> Ojs.t [@@js.builder] val builder3: x:int -> y:string -> unit -> Ojs.t [@@js.builder] val builder4: x:int -> y:string -> z:unit -> Ojs.t [@@js.builder] val builder5: ?x:int -> ?y:string -> unit -> Ojs.t [@@js.builder] val builder6: ?x:(int [@js.default 42]) -> ?y:(string [@js.default "42"]) -> ?z:int -> unit -> Ojs.t [@@js.builder] val sep: string -> (string list [@js.variadic]) -> string [@@js.global] ] (** Types Declarations *) module T = [%js: type js = private Ojs.t type abstract type alias = js type private_alias = private alias type record = { x: js; y: js } type mutable_record = { mutable x: js; y: js } type record_relabel = { x : int; y : int [@js "Y"]} type ('a, 'b) parametrized = { x : 'a; y : 'b } type 'a abs = ('a -> int) -> unit type specialized = (int, int) parametrized type enum = | Foo [@js "foo"] | Bar [@js 42] | Baz [@js 4.2] | Qux [@@js.enum] type status = | OK [@js 1] | KO [@js 2] | OO [@js 1.5] | OtherS of string [@js.default] | OtherI of int [@js.default] [@@js.enum] type poly = [`foo | `bar [@js 42] | `baz [@js 4.2] | `Qux | `I of int [@js.default] | `S of string[@js.default]] [@js.enum] type sum = | A | B of int | C of int * string | D of {age: int; name: string} | Unknown of Ojs.t [@js.default] [@@js.sum] type t = | A [@js "A"] | B of int [@js.arg "bArg"] | C of int * string [@js.arg "cArg"] | D of {age: int [@js "X"]; name: string [@js "Y"]} | E of int [@js "F"][@js.arg "fArg"] | Unknown of Ojs.t [@js.default] [@@js.sum "kind"] type union = A | B of int | C of int | D of Ojs.t [@js.default] [@@js.union] type poly_union = [`A | `B of int | `C of int | `D of Ojs.t [@js.default]] [@js.union] type discr_union = A | B of int | C of int | D of Ojs.t [@js.default] [@@js.union on_field "discr"] type discr_poly_union = [`A | `B of int | `C of int | `D of Ojs.t [@js.default]] [@js.union on_field "discr"] type discr_union_value = A [@js 0] | B of int [@js "42"] | C of int | D of Ojs.t [@js.default] [@@js.union on_field "discr"] module NestedScope0 : sig val f: string -> unit [@@js.global "outer.inner.f"] end module [@js.scope ("outer", "inner")] NestedScope1 : sig val f: string -> unit [@@js.global] end module NestedScope2 : sig val f: string -> unit [@@js.global] end [@js.scope "inner"] [@js.scope "outer"] ] ================================================ FILE: ppx-test/union_and_enum.mli ================================================ type enum_int = | Enum_int_0 [@js 0] | Enum_int_1 [@js 1] | Enum_int_other of int [@js.default] [@@js.enum] type enum_float = | Enum_float_0_1 [@js 0.1] | Enum_float_1_1 [@js 1.1] | Enum_float_other of float [@js.default] [@@js.enum] (* float cases should be matched first *) type enum_number_1 = | Enum_number_0 [@js 0] | Enum_number_1 [@js 1] | Enum_number_0_1 [@js 0.1] | Enum_number_1_1 [@js 1.1] | Enum_number_other of int [@js.default] [@@js.enum] (* float cases should be matched first even if the default case is float *) type enum_number_2 = | Enum_number_0 [@js 0] | Enum_number_1 [@js 1] | Enum_number_0_1 [@js 0.1] | Enum_number_1_1 [@js 1.1] | Enum_number_other of float [@js.default] [@@js.enum] type enum_string = | Enum_string_foo [@js "foo"] | Enum_string_bar [@js "bar"] | Enum_string_other of string [@js.default] [@@js.enum] (* if both true and false are expected, the boolean part of `_of_js` should not have the default case *) type enum_bool = | Enum_bool_true [@js true] | Enum_bool_false [@js false] [@@js.enum] (* otherwise, an unknown boolean value should trigger `assert false` *) type enum_bool_partial = | Enum_bool_true [@js true] [@@js.enum] (* or it should be mapped to the case with `js.default` *) type enum_bool_partial2 = | Enum_bool_true [@js true] | Enum_bool_other of bool [@js.default] [@@js.enum] (* if both true and false are expected, the boolean part of `_of_js` should not have the default case *) type enum_mixed = | Enum_int_0 [@js 0] | Enum_int_1 [@js 1] | Enum_float_0_1 [@js 0.1] | Enum_float_1_1 [@js 1.1] | Enum_number_other of int [@js.default] | Enum_string_foo [@js "foo"] | Enum_string_bar [@js "bar"] | Enum_string_other of string [@js.default] | Enum_bool_true [@js true] | Enum_bool_false [@js false] [@@js.enum] (* otherwise, an unknown boolean value should trigger `assert false` *) type enum_mixed_partial_bool = | Enum_int_0 [@js 0] | Enum_int_1 [@js 1] | Enum_float_0_1 [@js 0.1] | Enum_float_1_1 [@js 1.1] | Enum_number_other of float [@js.default] | Enum_string_foo [@js "foo"] | Enum_string_bar [@js "bar"] | Enum_string_other of string [@js.default] | Enum_bool_true [@js true] [@@js.enum] (* or it should be mapped to the case with `js.default` *) type enum_mixed_partial_bool2 = | Enum_int_0 [@js 0] | Enum_int_1 [@js 1] | Enum_float_0_1 [@js 0.1] | Enum_float_1_1 [@js 1.1] | Enum_number_other of float [@js.default] | Enum_string_foo [@js "foo"] | Enum_string_bar [@js "bar"] | Enum_string_other of string [@js.default] | Enum_bool_true [@js true] | Enum_bool_other of bool [@js.default] [@@js.enum] type dummy1 type dummy2 type dummy3 type dummy4 type dummy5 type dummy6 type union_int = | Union_int_0 of dummy1 [@js 0] | Union_int_1 of dummy2 [@js 1] | Unknown of Ojs.t [@js.default] [@@js.union on_field "tag"] type union_float = | Union_float_0_1 of dummy1 [@js 0.1] | Union_float_1_1 of dummy2 [@js 1.1] | Unknown of Ojs.t [@js.default] [@@js.union on_field "tag"] type union_string = | Union_string_foo of dummy3 [@js "foo"] | Union_string_bar of dummy4 [@js "bar"] | Unknown of Ojs.t [@js.default] [@@js.union on_field "tag"] (* if both true and false are expected, the boolean part of `_of_js` should not have the default case *) type union_bool = | Union_bool_true of dummy5 [@js true] | Union_bool_false of dummy6 [@js false] [@@js.union on_field "tag"] (* otherwise, an unknown boolean value should trigger `assert false` *) type union_bool_partial = | Union_bool_true of dummy5 [@js true] [@@js.union on_field "tag"] (* or it should be mapped to `Unknown` *) type union_bool_partial2 = | Union_bool_true of dummy5 [@js true] | Unknown of Ojs.t [@js.default] [@@js.union on_field "tag"] (* if both true and false are expected, the boolean part of `_of_js` should not have the default case *) type union_mixed = | Union_int_0 of dummy1 [@js 0] | Union_int_1 of dummy2 [@js 1] | Union_float_0_1 of dummy1 [@js 0.1] | Union_float_1_1 of dummy2 [@js 1.1] | Union_string_foo of dummy3 [@js "foo"] | Union_string_bar of dummy4 [@js "bar"] | Union_bool_true of dummy5 [@js true] | Union_bool_false of dummy6 [@js false] | Unknown of Ojs.t [@js.default] [@@js.union on_field "tag"] (* otherwise, an unknown boolean value should be mapped to `Unknown` *) type union_mixed_partial_bool = | Union_int_0 of dummy1 [@js 0] | Union_int_1 of dummy2 [@js 1] | Union_float_0_1 of dummy1 [@js 0.1] | Union_float_1_1 of dummy2 [@js 1.1] | Union_string_foo of dummy3 [@js "foo"] | Union_string_bar of dummy4 [@js "bar"] | Union_bool_true of dummy5 [@js true] | Unknown of Ojs.t [@js.default] [@@js.union on_field "tag"]