Skip to content

inNative cref Extension

Erik McClure edited this page May 18, 2019 · 2 revisions

inNative implements a custom cref value type that represents a C pointer. Most of the time, it behaves mostly like anyref would from the Reference Type Proposal, acting as an opaque pointer to C memory that is passed around. Unlike anyref, however, you are allowed to pass either i32 or i64 into a cref type. The type has a binary encoding and can be implemented into any standard webassembly module, but because no compiler can generate this value, it's mostly intended to be used in .wat files.

To demonstrate how cref works, lets start with a WebAssembly module that calls windows kernel functions to write to standard output, using the _innative_to_c Helper Function.

(import "!STD" "GetStdHandle" (func $getstdhandle (param i32) (result i64)))
(import "!STD" "WriteConsoleA" (func $writeconsole (param i64 i64 i32 i64 i64) (result i32)))
(import "" "_innative_to_c" (func $toc (param i64) (result i64)))

(func $caller
  i32.const -11
  call $getstdhandle
  i64.const 0
  call $toc
  i32.const 14
  i64.const 16
  call $toc
  i64.const 0
  call $writeconsole
  drop
)

Here, we are representing C pointer values with the i64 type (and consequently, this will only work on 64-bit architectures). To acquire a C pointer from a WebAssembly memory offset, we call _innative_to_c which adds the base value of the memory pointer to the offset and returns the result. We have two pointers here, one for the string and one for the character count output. Unfortunately, most of the other parameters are also integers, so it can be hard to keep track of what's what. We can clean this up by using cref instead.

(import "!STD" "GetStdHandle" (func $getstdhandle (param i32) (result cref)))
(import "!STD" "WriteConsoleA" (func $writeconsole (param cref cref i32 cref i64) (result i32)))

(func $caller
  i32.const -11
  call $getstdhandle
  i64.const 0
  i32.const 14
  i64.const 16
  i64.const 0
  call $writeconsole
  drop
)

We change our pointer parameters from i64 to cref and simply remove our calls to _innative_to_c. Even though we have pushed i64 parameters on to the stack, when the call consumes those values, they will be automatically converted to cref values.

It's important to note that the final parameter must be a null pointer, which cannot be represented by cref. Instead, we keep it as an i64 and pass in 0. If your pointer has a chance of being null, you'll have to make a null check and conditionally call _innative_to_c, because WebAssembly has no null reference (yet).

We also changed GetStdHandle to return a cref pointer, but this must remain an opaque pointer. We simply take this pointer and pass it unmodified into the WriteConsoleA method. If we had attempted to store it into an integer, it would have failed. If you need to transform a C pointer back into a WebAssembly offset, you must use _innative_from_c.