Skip to content

Commit

Permalink
implement Objintf (js sameworld, jsoo) (#389)
Browse files Browse the repository at this point in the history
* add objintf spec
* add audits
* move ejv to bindoj_ppxlib_utils
* add e_runtime_expr and e_runtime_refl to ppxlib_utils
* update runtime
* add example/for_dev_objintf_examples/

---------

Co-authored-by: Haochen M. Kotoi-Xie <[email protected]>
Co-authored-by: Haochen M. Kotoi-Xie <[email protected]>
  • Loading branch information
3 people authored Jan 10, 2024
1 parent a7e0811 commit 7e34648
Show file tree
Hide file tree
Showing 126 changed files with 13,294 additions and 470 deletions.
41 changes: 21 additions & 20 deletions .github/workflows/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,27 @@ jobs:
name: odoc-html-${{ github.sha }}
path: _build/default/_doc/_html/

# https://github.com/amondnet/vercel-action/issues/170 が解決したら最新のタグに更新
- uses: amondnet/vercel-action@1d26fa06fdab094080b48d33eea2031cee54724d
if: github.ref != 'refs/heads/main'
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID}}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID}}
working-directory: ./
scope: "kxcinc"
github-comment: true
- uses: amondnet/vercel-action@1d26fa06fdab094080b48d33eea2031cee54724d
if: github.ref == 'refs/heads/main'
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID}}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID}}
working-directory: ./
scope: "kxcinc"
vercel-args: '--prod'
github-comment: true
# doc deployment temporarily disabled
# # https://github.com/amondnet/vercel-action/issues/170 が解決したら最新のタグに更新
# - uses: amondnet/vercel-action@1d26fa06fdab094080b48d33eea2031cee54724d
# if: github.ref != 'refs/heads/main'
# with:
# vercel-token: ${{ secrets.VERCEL_TOKEN }}
# vercel-org-id: ${{ secrets.VERCEL_ORG_ID}}
# vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID}}
# working-directory: ./
# scope: "kxcinc"
# github-comment: true
# - uses: amondnet/vercel-action@1d26fa06fdab094080b48d33eea2031cee54724d
# if: github.ref == 'refs/heads/main'
# with:
# vercel-token: ${{ secrets.VERCEL_TOKEN }}
# vercel-org-id: ${{ secrets.VERCEL_ORG_ID}}
# vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID}}
# working-directory: ./
# scope: "kxcinc"
# vercel-args: '--prod'
# github-comment: true

setup-ocaml-checked:
needs: build
Expand Down
109 changes: 109 additions & 0 deletions doc/design/objintf/js-sameworld-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# js-sameworld-bridge

### Labeled argument optional with default

When defining a `bridgeable_decl`, we don't want it to be an optional argument, but when calling it, it must be an optional argument.
Therefore, they are generated as optional arguments, but the default value is passed internally, so `None` (OCaml) or `undefined` (JS) are never passed.

## OCaml

### resolution strategy

The resolution_strategy determines how the referenced type_decl is generated.

- `` `no_resolution ``: Nothing.
- `` `inline_type_definition ``: Inline type generation is only possible for Alias or Polymorphic variants.
- `` `infile_type_definition ``: Types are defined in the file.

### Ident

For `Coretype.Ident`, the code is generated assuming that type `xxx` and function `xxx_{of/to}_json` can be referenced.

### type_decl

`type_decl`s referenced by the `bridgeable_decl` that `` `infile_type_definition `` is specified by `resolution_strategy` are generated.

### `Simple_interfaces` module

`` ([> `simple ], 'bridgeable_ident) bridgeable_decl ``s are generated.

Note that they are basically generated according to `method_descriptor_body`, with the only exception that if both positional & labelled arguments are empty, the module style generates them as `unit -> ret_type`.

### `Initial_registry_object_registers` module

`object_registry_decl`s whose party is `` `endemic `` are generated.

### `Concrete_bridge` module type

#### `Complex_interfaces` module

`` ([> `complex ], 'bridgeable_ident) bridgeable_decl ``s are generated.

The notes on arguments are the same as for simple interfaces.

#### `Peer_object_registry` module

`object_registry_decl`s whose party is `` `peer `` are generated.

Stored as an OCaml StringMap.

#### `Peer_objects` module

`named_object_decl`s whose party is `` `peer `` are generated.

#### `Endemic_object_registry` module

Same as `Initial_registry_object_registers`.

#### `endemic_full_bridge` variable

Variable for the setup process with JavaScript.

Internally, the following type definition is expected.

```typescript
{
readonly setupCalled: () => boolean;
readonly setup: (fullBridgeOfJs: { readonly instance: ConcreteBridgeOfJs }) => void;
}
```

### `Concrete_bridge_interfaces` module

Only interfaces are generated.

### Full Bridge

A (functor) module type definition is generated to generate concrete_bridge. Based on the `named_object_decl` and `object_registry_decl` parties in the given `sameworld_objintf`, one of the following four types of full_bridge is generated.

* `Setup_less_full_bridge`: Nothing included.
* `Endemic_setup_only_full_bridge`: Only `` `peer `` is included.
* `Peer_setup_only_full_bridge`: Only `` `endemic `` is included.
* `Dual_setup_full_bridge`: Both `` `peer `` and `` `endemic `` are included.

If `named_object_decl`s whose party is ``endemic `` are included, they are generated as functor parameters.

## Js_of_ocaml

### type_decl

Currently, the json encoder/decoder generated by `Bindoj_gen` is used for type_decl conversion.

## TypeScript

Basically, the data is converted by the code generated on the OCaml side, so only the type definition and some implementation for the full bridge is generated as TypeScript code.

The basic structure is the same as OCaml, but differs in some respects.

### resolution stragety

- `` `import_location ``: Types are imported from the specified file.
- `` `inline_type_definition ``: Types are generated inline.
- `` `infile_type_definition ``: Types are defined in the file.

### The arguments of the generated method
In TS, convert to a list of types to be taken by the arguments and then delete all trailing `unit`s. (Naturally, this may result in an empty list, in which case a type such as `foo : () => RetType` is generated.)

### Runtime implementation

For the generation of full bridges, types and functions defined in [objintf.ts](../../../with_js/public-packages/runtime/src/objintf.ts) are used.
2 changes: 1 addition & 1 deletion doc/tests_src/ocaml_datatype_basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ type nonrec type_decl =
# let person_desc =
variant_decl "person" [
variant_constructor "Anonymous" `no_param;
variant_constructor "With_id" (`tuple_like [variant_argument @@ Coretype.mk_prim `int]);
variant_constructor "With_id" (`tuple_like [variant_argument & Coretype.mk_prim `int]);
variant_constructor "Student" (`inline_record [
record_field "student_id" (Coretype.mk_prim `int);
record_field "name" (Coretype.mk_prim `string);
Expand Down
4 changes: 2 additions & 2 deletions doc/tests_src/ocaml_datatype_polymorphic-variant.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ AnchorZ Inc. to satisfy its needs in its product development workflow.
# let int_or_string_desc =
variant_decl "int_or_string" [
variant_constructor "int" (`tuple_like [variant_argument @@ Coretype.mk_prim `int]);
variant_constructor "string" (`tuple_like [variant_argument @@ Coretype.mk_prim `string]);
variant_constructor "int" (`tuple_like [variant_argument & Coretype.mk_prim `int]);
variant_constructor "string" (`tuple_like [variant_argument & Coretype.mk_prim `string]);
] ~configs:[
Caml_config.variant_type `polymorphic
];;
Expand Down
2 changes: 1 addition & 1 deletion doc/tests_src/ocaml_json_codec.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ type nonrec type_decl =
# let person_desc =
variant_decl "person" [
variant_constructor "Anonymous" `no_param;
variant_constructor "With_id" (`tuple_like [variant_argument @@ Coretype.mk_prim `int]);
variant_constructor "With_id" (`tuple_like [variant_argument & Coretype.mk_prim `int]);
variant_constructor "Student" (`inline_record [
record_field "student_id" (Coretype.mk_prim `int);
record_field "name" (Coretype.mk_prim `string);
Expand Down
10 changes: 10 additions & 0 deletions example/for_dev/dune
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@
(action (with-stdout-to %{target}
(run generator/duneinc_generator.exe -target %{target}))))
(include apidir_examples-dune.inc)

(rule
(alias dune-gen)
(mode (promote))
(action (diff objintf_examples-dune.inc objintf_examples-dune.inc.gen)))
(rule
(target objintf_examples-dune.inc.gen)
(action (with-stdout-to %{target}
(run generator/duneinc_generator.exe -target %{target}))))
(include objintf_examples-dune.inc)
3 changes: 2 additions & 1 deletion example/for_dev/generator/dune
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
(libraries
kxclib fmt dune_file_parts
bindoj_test_common_typedesc_examples
bindoj_test_common_apidir_examples)
bindoj_test_common_apidir_examples
bindoj_test_common_objintf_examples)
(flags :standard))
89 changes: 89 additions & 0 deletions example/for_dev/generator/duneinc_generator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ open Kxclib

let lib_gen_generator_dep = "%{workspace_root}/src/lib_gen/unit_test/gen/gen.exe"
let lib_gen_ts_generator_dep = "%{workspace_root}/src/lib_gen_ts/unit_test/gen/gen.exe"

let json_examples_generator_dep = "%{workspace_root}/with_js/compile-tests/generator/gen_json.exe"
let json_schema_generator_dep = "%{workspace_root}/with_js/compile-tests/generator/gen_schema.exe"

let lib_objintf_gen_generator_dep = "%{workspace_root}/src/lib_objintf_gen/unit_test/gen/gen.exe"
let lib_objintf_gen_jsoo_generator_dep = "%{workspace_root}/src/lib_objintf_gen_jsoo/unit_test/gen/gen.exe"
let lib_objintf_gen_ts_generator_dep = "%{workspace_root}/src/lib_objintf_gen_ts/unit_test/gen/gen.exe"

let chop_suffix_exn suffix = String.(chop_suffix suffix &> Option.get)

module Dparts = Dune_file_parts
Expand Down Expand Up @@ -191,4 +196,88 @@ let () =
]
|> const & Bindoj_test_common_apidir_examples.All.all |&> fst
)
| _, Some ("objintf_examples-dune.inc" as target_name) ->
let objintf_gen_lib_name =
"dev_example_" ^ chop_suffix_exn "-dune.inc" target_name ^ "-lib_objintf_gen"
|> Dcomb.mangle_library_name
in
output_duneinc_header target_name;
output_fmtd_newline & Dcomb.(
let single args f =
let open Action in
with_stdout_to (`expr f) (
run "%{generator}" & f :: args
)
in
aggregate [
subdir "objintf_examples/lib_objintf_gen" [
Library.mk []
~name:(str objintf_gen_lib_name)
~modules':[ list ~sep:sp (fmt "%s_gen"); list ~sep:sp (fmt "%s_trans_gen") ]
~libraries:[
"bindoj.base";
"bindoj.std_runtime";
"bindoj.objintf_shared";
"bindoj_objintf_gen_test_gen_utils";
];
many (
let outputs = [ fmt "%s_gen.ml"; fmt "%s_trans_gen.ml" ] in
Rule.mkp_gen [
targets outputs;
Deps.(mk [named "generator" & str lib_objintf_gen_generator_dep]);
Action.mk_progn (outputs |&> single []);
]
)
];
subdir "objintf_examples/lib_objintf_gen_jsoo" [
Library.mk []
~name:("dev_example_" ^ chop_suffix_exn "-dune.inc" target_name ^ "-lib_objintf_gen_jsoo"
|> mangle_library_name
|> str)
~modules':[ list ~sep:sp (fmt "%s_jsoo_gen"); list ~sep:sp (fmt "%s_jsoo_trans_gen") ]
~libraries:[
"bindoj.base";
"bindoj.std_runtime";
"bindoj.objintf_shared";
"bindoj_objintf_gen_jsoo_test_gen_utils";
"js_of_ocaml";
"yojson";
objintf_gen_lib_name;
]
~preprocess:[ "(pps js_of_ocaml-ppx)" ];
many (
let outputs = [ fmt "%s_jsoo_gen.ml"; fmt "%s_jsoo_trans_gen.ml" ] in
Rule.mkp_gen [
targets outputs;
Deps.(mk [named "generator" & str lib_objintf_gen_jsoo_generator_dep]);
Action.mk_progn (outputs |&> single []);
]
)
];
subdir "objintf_examples/lib_objintf_gen_ts" [
many (
let outputs = [ fmt "%s_gen.ts"; fmt "%s_trans_gen.ts" ] in
Rule.mkp_gen [
targets [
fmt "%s_gen.ts";
fmt "%s_trans_gen.ts";
];
Deps.(mk [
named "generator" (atomic lib_objintf_gen_ts_generator_dep);
]);
Action.(vpbox ~lead:"action" &.
progn (outputs |&> single [])
);
]
)
];
]
|> const & (
let open Bindoj_test_common_objintf_examples in
All.all
|&>> (fun (module M: Utils.Ex) ->
let name = String.lowercase_ascii M.module_name in
[name])
)
)
| t, _ -> eprintf "invalid -target value %S" t; exit 2
Loading

0 comments on commit 7e34648

Please sign in to comment.