Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add bindings for exception handling (try/catch/throw/rethrow) #197

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions src/expression.c
Original file line number Diff line number Diff line change
Expand Up @@ -1884,6 +1884,117 @@ caml_binaryen_ref_eq(value _module, value _left, value _right) {
CAMLreturn(alloc_BinaryenExpressionRef(exp));
}

// Exception handling operations
CAMLprim value
caml_binaryen_try_native(value _module, value _name, value _body, value _catchTags, value _catchBodies, value _delegateTarget) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we name this just caml_binaryen_try?

CAMLparam5(_module, _name, _body, _catchTags, _catchBodies);
CAMLxparam1(_delegateTarget);
BinaryenModuleRef module = BinaryenModuleRef_val(_module);
char *name;
if (Is_none(_name)) {
name = NULL;
} else {
name = Safe_String_val(Some_val(_name));
}
BinaryenExpressionRef body = BinaryenExpressionRef_val(_body);
_catchTags = array_of_list(_catchTags);
int catchTagsLen = array_length(_catchTags);
const char* catchTags[catchTagsLen];
for (int i = 0; i < catchTagsLen; i++) {
catchTags[i] = Safe_String_val(Field(_catchTags, i));
}
_catchBodies = array_of_list(_catchBodies);
int catchBodiesLen = array_length(_catchBodies);
BinaryenExpressionRef catchBodies[catchBodiesLen];
for (int i = 0; i < catchBodiesLen; i++) {
catchBodies[i] = BinaryenExpressionRef_val(Field(_catchBodies, i));
}
char *delegateTarget;
if (Is_none(_delegateTarget)) {
delegateTarget = NULL;
} else {
delegateTarget = Safe_String_val(Some_val(_delegateTarget));
}
BinaryenExpressionRef exp = BinaryenTry(module, name, body, catchTags, catchTagsLen, catchBodies, catchBodiesLen, delegateTarget);
CAMLreturn(alloc_BinaryenExpressionRef(exp));
}

CAMLprim value caml_binaryen_try_bytecode(value *argv, int argn) {
return caml_binaryen_try_native(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
}

CAMLprim value
caml_binaryen_trycatch(value _module, value _name, value _body, value _catchTags, value _catchBodies) {
CAMLparam5(_module, _name, _body, _catchTags, _catchBodies);
BinaryenModuleRef module = BinaryenModuleRef_val(_module);
char *name;
if (Is_none(_name)) {
name = NULL;
} else {
name = Safe_String_val(Some_val(_name));
}
BinaryenExpressionRef body = BinaryenExpressionRef_val(_body);
_catchTags = array_of_list(_catchTags);
int catchTagsLen = array_length(_catchTags);
const char* catchTags[catchTagsLen];
for (int i = 0; i < catchTagsLen; i++) {
catchTags[i] = Safe_String_val(Field(_catchTags, i));
}
_catchBodies = array_of_list(_catchBodies);
int catchBodiesLen = array_length(_catchBodies);
BinaryenExpressionRef catchBodies[catchBodiesLen];
for (int i = 0; i < catchBodiesLen; i++) {
catchBodies[i] = BinaryenExpressionRef_val(Field(_catchBodies, i));
}
char *delegateTarget = NULL;
BinaryenExpressionRef exp = BinaryenTry(module, name, body, catchTags, catchTagsLen, catchBodies, catchBodiesLen, delegateTarget);
CAMLreturn(alloc_BinaryenExpressionRef(exp));
}

CAMLprim value
caml_binaryen_trydelegate(value _module, value _name, value _body, value _delegateTarget) {
CAMLparam4(_module, _name, _body,_delegateTarget);
BinaryenModuleRef module = BinaryenModuleRef_val(_module);
char *name;
if (Is_none(_name)) {
name = NULL;
} else {
name = Safe_String_val(Some_val(_name));
}
BinaryenExpressionRef body = BinaryenExpressionRef_val(_body);
char *delegateTarget = Safe_String_val(_delegateTarget);
int catchTagsLen = 0;
int catchBodiesLen = 0;
const char *catchTags[1] = {NULL};
BinaryenExpressionRef catchBodies[1] = {NULL};
BinaryenExpressionRef exp = BinaryenTry(module, name, body, catchTags, catchTagsLen, catchBodies, catchBodiesLen, delegateTarget);
CAMLreturn(alloc_BinaryenExpressionRef(exp));
}

CAMLprim value
caml_binaryen_throw(value _module, value _tag, value _operands) {
CAMLparam3(_module, _tag, _operands);
BinaryenModuleRef module = BinaryenModuleRef_val(_module);
char *tag = Safe_String_val(_tag);
_operands = array_of_list(_operands);
int operandsLen = array_length(_operands);
BinaryenExpressionRef operands[operandsLen];
for (int i = 0; i < operandsLen; i++) {
operands[i] = BinaryenExpressionRef_val(Field(_operands, i));
}
BinaryenExpressionRef exp = BinaryenThrow(module, tag, operands, operandsLen);
CAMLreturn(alloc_BinaryenExpressionRef(exp));
}

CAMLprim value
caml_binaryen_rethrow(value _module, value _target) {
CAMLparam2(_module, _target);
BinaryenModuleRef module = BinaryenModuleRef_val(_module);
char *target = Safe_String_val(_target);
BinaryenExpressionRef exp = BinaryenRethrow(module, target);
CAMLreturn(alloc_BinaryenExpressionRef(exp));
}

// Table operations
CAMLprim value
caml_binaryen_table_get(value _module, value _name, value _index, value _ty) {
Expand Down
61 changes: 61 additions & 0 deletions src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -1706,6 +1706,67 @@ function caml_binaryen_ref_eq(wasm_mod, left, right) {
return wasm_mod.ref.func(left, right);
}

// Exception handling operations

//Provides: caml_binaryen_try_native
//Requires: caml_jsstring_of_string, caml_list_to_js_array
function caml_binaryen_try_native(wasm_mod, name, body, catch_tags, catch_bodies, delegate_target) {
return wasm_mod.try(
caml_jsstring_of_string(name),
body,
caml_list_to_js_array(catch_tags).map(caml_jsstring_of_string),
caml_list_to_js_array(catch_bodies),
delegate_target ? caml_jsstring_of_string(delegate_target[1]) : null,
);
}

//Provides: caml_binaryen_try_bytecode
//Requires: caml_binaryen_try_native
function caml_binaryen_try_bytecode() {
return caml_binaryen_try_native(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
}

//Provides: caml_binaryen_trycatch
//Requires: caml_jsstring_of_string, caml_list_to_js_array
function caml_binaryen_trycatch(wasm_mod, name, body, catch_tags, catch_bodies) {
return wasm_mod.try(
caml_jsstring_of_string(name),
body,
caml_list_to_js_array(catch_tags).map(caml_jsstring_of_string),
caml_list_to_js_array(catch_bodies),
null,
);
}

//Provides: caml_binaryen_trydelegate
//Requires: caml_jsstring_of_string, caml_list_to_js_array
function caml_binaryen_trydelegate(wasm_mod, name, body, delegate_target) {
return wasm_mod.try(
caml_jsstring_of_string(name),
body,
[],
[],
delegate_target ? caml_jsstring_of_string(delegate_target[1]) : null,
);
}

//Provides: caml_binaryen_throw
//Requires: caml_jsstring_of_string, caml_list_to_js_array
function caml_binaryen_throw(wasm_mod, tag, operands) {
return wasm_mod.throw(
caml_jsstring_of_string(tag),
caml_list_to_js_array(operands),
);
}

//Provides: caml_binaryen_rethrow
//Requires: caml_jsstring_of_string, caml_list_to_js_array
function caml_binaryen_rethrow(wasm_mod, target) {
return wasm_mod.rethrow(
caml_jsstring_of_string(target),
)
}

// Table operations

//Provides: caml_binaryen_table_get
Expand Down
26 changes: 26 additions & 0 deletions src/expression.ml
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,32 @@ module Ref = struct
(** Module, left, right *)
end

(** Bindings for `try` instruction. For better validation, use `Try_catch` or `Try_Delegate`. *)
module Try = struct
external make : Module.t -> string option -> t -> string list -> t list -> string option -> t = "caml_binaryen_try_bytecode" "caml_binaryen_try_native"
(** Module, name, body, catch tags, catch bodies, delegate target *)
end

module Try_Catch = struct
external make : Module.t -> string option -> t -> string list -> t list -> t = "caml_binaryen_trycatch"
(** Module, name, body, catch tags, catch bodies *)
end

module Try_Delegate = struct
external make : Module.t -> string option -> t -> string -> t = "caml_binaryen_trydelegate"
(** Module, name, body, delegate *)
end

module Throw = struct
external make : Module.t -> string -> t list -> t = "caml_binaryen_throw"
(** Module, tag, operands *)
end

module Rethrow = struct
external make : Module.t -> string -> t = "caml_binaryen_rethrow"
(** Module, target *)
end

module Table = struct
external get : Module.t -> string -> t -> Type.t -> t
= "caml_binaryen_table_get"
Expand Down
26 changes: 26 additions & 0 deletions src/expression.mli
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,32 @@ module Ref : sig
(** Module, left, right *)
end

(** Bindings for `try` instruction. For better validation, use `Try_catch` or `Try_Delegate`. *)
module Try : sig
val make : Module.t -> string option -> t -> string list -> t list -> string option -> t
(** Module, name, body, catch tags, catch bodies, delegate target *)
end

module Try_Catch : sig
val make : Module.t -> string option -> t -> string list -> t list -> t
(** Module, name, body, catch tags, catch bodies *)
end

module Try_Delegate : sig
val make : Module.t -> string option -> t -> string -> t
(** Module, name, body, delegate *)
end

module Throw : sig
val make : Module.t -> string -> t list -> t
(** Module, tag, operands *)
end

module Rethrow : sig
val make : Module.t -> string -> t
(** Module, target *)
end

module Table : sig
val get : Module.t -> string -> t -> Type.t -> t
(** Module, name, index, type *)
Expand Down
58 changes: 58 additions & 0 deletions test/test.expected
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,64 @@
(ref.null nofunc)
(i32.const 0)
)
(try $tc1 (result i32)
(do
(throw $foo
(i32.const 1)
)
)
(catch $foo
(i32.const 2)
)
(catch $bar
(i32.const 3)
)
)
(try $tc2 (result i32)
(do
(rethrow $foo)
)
(catch $foo
(i32.const 2)
)
(catch $bar
(i32.const 3)
)
)
(try $tc3 (result i32)
(do
(i32.const 1)
)
(catch $foo
(i32.const 2)
)
(catch_all
(i32.const 3)
)
)
(try $tc4 (result i32)
(do
(i32.const 1)
)
(catch $foo
(i32.const 2)
)
(catch_all
(i32.const 3)
)
)
(try $td1 (result i32)
(do
(i32.const 1)
)
(delegate $del1)
)
(try $td2 (result i32)
(do
(i32.const 1)
)
(delegate $del1)
)
(module
(type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
(type $none_=>_none (func))
Expand Down
44 changes: 44 additions & 0 deletions test/test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,50 @@ let _ =
assert (
Bytes.equal (Memory.get_segment_data wasm_mod 1) (Bytes.of_string "world"))

(* Exception handling *)
let try_catch_1 =
Expression.Try.make wasm_mod (Some "tc1")
(Expression.Throw.make wasm_mod "foo" [Expression.Const.make wasm_mod (Literal.int32 1l)])
["foo"; "bar"]
[Expression.Const.make wasm_mod (Literal.int32 2l); Expression.Const.make wasm_mod (Literal.int32 3l)]
None
let try_catch_2 =
Expression.Try_Catch.make wasm_mod (Some "tc2")
(Expression.Rethrow.make wasm_mod "foo")
["foo"; "bar"]
[Expression.Const.make wasm_mod (Literal.int32 2l); Expression.Const.make wasm_mod (Literal.int32 3l)]

(* One more catch-body than catch-tag; last body becomes the catch_all *)
let try_catch_all_1 =
Expression.Try.make wasm_mod (Some "tc3")
(Expression.Const.make wasm_mod (Literal.int32 1l))
["foo"]
[Expression.Const.make wasm_mod (Literal.int32 2l); Expression.Const.make wasm_mod (Literal.int32 3l)]
None
let try_catch_all_2 =
Expression.Try_Catch.make wasm_mod (Some "tc4")
(Expression.Const.make wasm_mod (Literal.int32 1l))
["foo"]
[Expression.Const.make wasm_mod (Literal.int32 2l); Expression.Const.make wasm_mod (Literal.int32 3l)]

let try_delegate_1 =
Expression.Try.make wasm_mod (Some "td1")
(Expression.Const.make wasm_mod (Literal.int32 1l))
[]
[]
(Some "del1")
let try_delegate_2 =
Expression.Try_Delegate.make wasm_mod (Some "td2")
(Expression.Const.make wasm_mod (Literal.int32 1l))
"del1"

let _ = Expression.print try_catch_1
let _ = Expression.print try_catch_2
let _ = Expression.print try_catch_all_1
let _ = Expression.print try_catch_all_2
let _ = Expression.print try_delegate_1
let _ = Expression.print try_delegate_2

(* Create an imported "write" function i32 (externref, i32, i32) *)
(* Similar to the example here: https://bytecodealliance.org/articles/reference-types-in-wasmtime *)

Expand Down
Loading