Previous: Pattern matching, Up: Scheme reference [Contents][Index]
WebAssembly follows the capability security model, which means that
modules cannot do much on their own. Wasm modules are guests within a
host. They must be given capabilities by the host in order to
interact with the outside world. Modules request capabilities by
declaring imports, which the host then fills out with concrete
implementations at instantiation time. Hoot provides a foreign
function interface (FFI) in the (hoot ffi)
module to embed
these import declarations within Scheme code.
The define-foreign
form declares an import with a given type
signature (Wasm is statically typed) and defines a procedure for
calling it. The FFI takes care of converting Scheme values to Wasm
values and vice versa. For example, declaring an import for creating
text nodes in a web browser could look like this:
(define-foreign make-text-node "document" "createTextNode" (ref string) -> (ref null extern))
In the above example, the procedure is bound to the variable
make-text-node
. In the Wasm binary, this import is named
“createTextNode” and resides in the “document” namespace of the
import table. A Wasm host is expected to satisfy this import by
providing a function that accepts one argument, a string, and returns
an arbitary host value which may be null.
Note that declaring an import does not do anything to bind that import to an implementation on the host. The Wasm guest cannot grant capabilities unto itself. Furthermore, the host could be any Wasm runtime, so the actual implementation will vary. In the context of a web browser, the JavaScript code that instantiates a module with this import could look like this:
Scheme.load_main("hello.wasm", {}, { document: { createTextNode: Document.prototype.createTextNode.bind(document) } });
And here’s what it might look like when using the Hoot interpreter:
(use-modules (hoot reflect)) (hoot-instantiate (call-with-input-file "hello.wasm" parse-wasm) `(("document" . (("createTextNode" . ,(lambda (str) `(text ,str)))))))
Once defined, make-text-node
can be called like any other
procedure:
(define text-node (make-text-node "Hello, world!"))
Since the return type of make-text-node
is (ref null
extern
), the value of text-node
is an external
reference. To check if a value is an external reference, use the
external?
predicate:
(external? text-node) ; => #t
External references may be null, which could indicate failure, a cache
miss, etc. To check if an external value is null, use the
external-null?
predicate:
(if (external-null? text-node) 'yay 'uh-oh)
Define scheme-name, a procedure wrapping the Wasm import import-name in the namespace namespace.
The signature of the function is specified by param-types and result-type, which are all Wasm types expressed in WAT form.
Valid parameter types are:
Valid result types are:
Return #t
if obj is an external reference.
Return #t
if extern is null.
Return #t
if extern is not null.
Previous: Pattern matching, Up: Scheme reference [Contents][Index]