Creating memory


WebAssembly provides i32, i64, f32 and f64. Then, where're the array, string and other common types in other programming language? Um…you have to implement them by yourself.

As you've seen in The Stack and number types, all processed data are bytes. What the point of view you take for these bytes is the concept of data types. Once you have a data type, you can use concrete concepts to deal with data, not drop into 0 and 1 directly.

So, what's an array? How to deal with a string? It depends on you. WebAssembly only provides you linear memory, a large array of bytes that can grow over time.

You may use memory to create the memory. One module can have only one linear memory.

(module
    (memory 1)
    ...
)

To avoid safety bugs, such as buffer overflows, the linear memory is isolated from variables, tables, etc. The attribute 1 indicates that the memory must have at least 1 page of memory. WebAssembly defines a page to be 64KiB. (1 KiB is 1024 bytes.) An optional maximum size can be set. In general, most WebAssembly modules shouldn't need to set a maximum.

After creating memory, all bits are 0. You can use data to write a string of bytes at a given offset at instantiation time. For example:

(module
    (memory $mem 1)
    (data (i32.const 0) "\48\65\6C\6C\6F")
    (export "mem" (memory $mem))
    (func $nope)
)

You can export the memory. The exported memory is an WebAssembly.Memory object. Its buffer property is an instance of ArrayBuffer that points at the whole linear memory.

In the above example, you write five bytes to memory. They are exactly five characters of "Hello" if you view them as unsigned 8-bit integers. You can use the following script to print Hello in the console.

WebAssembly.instantiateStreaming(fetch('program.wasm'))
           .then(prog => {
               console.log(String.fromCharCode.apply(null, 
                   new Uint8Array(prog.instance.exports.mem.buffer, 0, 5)
               ));
           });

You can create WebAssembly.Memory in JavaScript and import it to a module.

const mem = new WebAssembly.Memory({initial:1});

const importObj = {
    env: {mem}
};

WebAssembly.instantiateStreaming(fetch('program.wasm'), importObj)
           .then(prog => {
               console.log(String.fromCharCode.apply(null, 
                   new Uint8Array(mem.buffer, 0, 5)
               ));
           });

The following module writes data to the imported memory.

(module
    (import "env" "mem" (memory 1))
    (data (i32.const 0) "Hello")
    (func $nope)
)

current_memory pushes the current memory size in units of pages onto the stack. grow_memory grows linear memory and returns the previous memory size in units of pages or -1 on failure.

(module
    (import "env" "log" (func $log (param i32)))
    (memory $mem 1)
    (data (i32.const 0) "\48\65\6C\6C\6F")
    (func $main
        current_memory
        call $log  ;; print 1
        (grow_memory (i32.const 2))
        call $log  ;; print 1
    )
    (start $main)
)