WebAssembly Objects: Memory, Globals, and Tables
⚠️ Implementation Status: Object access functionality is currently under development. This page documents the planned API.
Current Limitations
The following WebAssembly object types are not yet fully implemented:
- Memory object access and manipulation
- Global variable access and modification
- Table operations and function references
- Complete export resolution
Basic Export Access (🚧 Partial)
# Currently available (limited functionality)
engine = WasmEngine()
store = WasmStore(engine)
# Create instance
wat_content = """
(module
(memory $mem 1)
(global $counter (mut i32) (i32.const 0))
(table $table 10 funcref)
(export "memory" (memory $mem))
(export "counter" (global $counter))
(export "table" (table $table)))
"""
wasm_bytes = wat2wasm(wat_content)
module_obj = WasmModule(engine, wasm_bytes)
instance = WasmInstance(store, module_obj)
# Export enumeration currently returns placeholder data
exports_info = exports(module_obj) # Dict{String,Any}()
Planned Features (📋 Future Release)
Memory Objects (Future API)
WebAssembly linear memory will be accessible through:
# Planned API for memory operations
struct Memory <: AbstractMemory
ptr::Ptr{LibWasmtime.wasmtime_memory_t}
store::Store
end
# Memory size operations
function memory_size(memory::Memory)::Int32
# Get current size in pages (64KB each)
return LibWasmtime.wasmtime_memory_size(memory.store.context, memory.ptr)
end
function memory_grow!(memory::Memory, delta_pages::Int32)::Int32
# Grow memory by delta pages, return previous size
prev_size_ref = Ref{Int32}()
error_ptr = LibWasmtime.wasmtime_memory_grow(
memory.store.context,
memory.ptr,
delta_pages,
prev_size_ref
)
check_error(error_ptr)
return prev_size_ref[]
end
Memory Data Access (Future API)
# Read/write memory data
function read_memory(memory::Memory, offset::Int32, length::Int32)::Vector{UInt8}
# Get raw memory pointer
data_ptr = LibWasmtime.wasmtime_memory_data(memory.store.context, memory.ptr)
data_size = LibWasmtime.wasmtime_memory_data_size(memory.store.context, memory.ptr)
# Bounds checking
if offset + length > data_size
throw(BoundsError("Memory access out of bounds"))
end
# Copy data safely
result = Vector{UInt8}(undef, length)
unsafe_copyto!(pointer(result), data_ptr + offset, length)
return result
end
function write_memory!(memory::Memory, offset::Int32, data::Vector{UInt8})
data_ptr = LibWasmtime.wasmtime_memory_data(memory.store.context, memory.ptr)
data_size = LibWasmtime.wasmtime_memory_data_size(memory.store.context, memory.ptr)
if offset + length(data) > data_size
throw(BoundsError("Memory write out of bounds"))
end
unsafe_copyto!(data_ptr + offset, pointer(data), length(data))
end
Global Basics
WebAssembly globals are typed values that can be either mutable or immutable:
# Globals are retrieved from exports
global_export = get_export(instance, "global_var")
# TODO: Convert to Global type
# global_var = Global(export_ptr, store)
Global Operations (Future API)
struct Global <: AbstractGlobal
ptr::Ptr{LibWasmtime.wasmtime_global_t}
store::Store
end
# Read global value
function get_global(global_var::Global)::WasmValue
val_ref = Ref{LibWasmtime.wasmtime_val_t}()
LibWasmtime.wasmtime_global_get(
global_var.store.context,
global_var.ptr,
val_ref
)
return convert_wasmtime_val_to_julia(val_ref[])
end
# Write global value (if mutable)
function set_global!(global_var::Global, value::WasmValue)
val = convert_julia_to_wasmtime_val(value)
error_ptr = LibWasmtime.wasmtime_global_set(
global_var.store.context,
global_var.ptr,
Ref(val)
)
check_error(error_ptr)
end
Global Type Information (Future API)
# Get global type information
function global_type(global_var::Global)
type_ptr = LibWasmtime.wasmtime_global_type(
global_var.store.context,
global_var.ptr
)
# Extract type information
return (
value_type = get_global_value_type(type_ptr),
is_mutable = get_global_mutability(type_ptr)
)
end
Tables
Table Basics
WebAssembly tables are arrays of opaque values (typically function references):
# Tables are retrieved from exports
table_export = get_export(instance, "table")
# TODO: Convert to Table type
# table = Table(export_ptr, store)
Table Operations (Future API)
struct Table <: AbstractTable
ptr::Ptr{LibWasmtime.wasmtime_table_t}
store::Store
end
# Get table size
function table_size(table::Table)::Int32
return LibWasmtime.wasmtime_table_size(table.store.context, table.ptr)
end
# Get table element
function get_table_element(table::Table, index::Int32)::Union{WasmValue, Nothing}
val_ref = Ref{LibWasmtime.wasmtime_val_t}()
found = LibWasmtime.wasmtime_table_get(
table.store.context,
table.ptr,
index,
val_ref
)
if found != 0
return convert_wasmtime_val_to_julia(val_ref[])
else
return nothing
end
end
# Set table element
function set_table_element!(table::Table, index::Int32, value::WasmValue)
val = convert_julia_to_wasmtime_val(value)
error_ptr = LibWasmtime.wasmtime_table_set(
table.store.context,
table.ptr,
index,
Ref(val)
)
check_error(error_ptr)
end
# Grow table
function table_grow!(table::Table, delta::Int32, init_value::WasmValue)::Int32
init_val = convert_julia_to_wasmtime_val(init_value)
prev_size_ref = Ref{Int32}()
error_ptr = LibWasmtime.wasmtime_table_grow(
table.store.context,
table.ptr,
delta,
Ref(init_val),
prev_size_ref
)
check_error(error_ptr)
return prev_size_ref[]
end
Export Management
Current Export Retrieval
# Current implementation returns raw wasmtime_extern_t
function get_memory_export(instance::Instance, name::String)
export_item = get_export(instance, name)
# TODO: Type checking and conversion
return export_item
end
function get_global_export(instance::Instance, name::String)
export_item = get_export(instance, name)
# TODO: Type checking and conversion
return export_item
end
function get_table_export(instance::Instance, name::String)
export_item = get_export(instance, name)
# TODO: Type checking and conversion
return export_item
end
Future Export Management
# Future type-safe export retrieval
function get_export_typed(instance::Instance, name::String, ::Type{T}) where T
export_item = get_export(instance, name)
if T == Memory
return convert_to_memory(export_item, instance.store)
elseif T == Global
return convert_to_global(export_item, instance.store)
elseif T == Table
return convert_to_table(export_item, instance.store)
elseif T == Func
return convert_to_func(export_item, instance.store)
else
throw(ArgumentError("Unsupported export type: $T"))
end
end
# Convenience methods
get_memory(instance, name) = get_export_typed(instance, name, Memory)
get_global(instance, name) = get_export_typed(instance, name, Global)
get_table(instance, name) = get_export_typed(instance, name, Table)
Working with Object Types
Type Identification (Future API)
# Identify export types
function identify_export_type(export_item)
extern_type = LibWasmtime.wasmtime_extern_type(context, export_item)
kind = LibWasmtime.wasm_externtype_kind(extern_type)
if kind == LibWasmtime.WASMTIME_EXTERN_FUNC
return Func
elseif kind == LibWasmtime.WASMTIME_EXTERN_MEMORY
return Memory
elseif kind == LibWasmtime.WASMTIME_EXTERN_GLOBAL
return Global
elseif kind == LibWasmtime.WASMTIME_EXTERN_TABLE
return Table
else
throw(ArgumentError("Unknown export type"))
end
end
Generic Export Processing
# Process all exports generically
function process_all_exports(instance::Instance)
# This would work when export enumeration is implemented
for (name, export_item) in exports(instance)
export_type = identify_export_type(export_item)
if export_type == Memory
memory = convert_to_memory(export_item, instance.store)
println("Memory '$name': $(memory_size(memory)) pages")
elseif export_type == Global
global_var = convert_to_global(export_item, instance.store)
value = get_global(global_var)
println("Global '$name': $value")
elseif export_type == Table
table = convert_to_table(export_item, instance.store)
size = table_size(table)
println("Table '$name': $size elements")
elseif export_type == Func
func = convert_to_func(export_item, instance.store)
println("Function '$name': callable")
end
end
end
Advanced Object Operations
Memory Mapping Patterns (Future API)
# Safe memory mapping
function with_memory_view(f, memory::Memory, offset::Int32, length::Int32)
# Bounds checking
current_size = memory_size(memory) * 65536 # Convert pages to bytes
if offset + length > current_size
throw(BoundsError("Memory view out of bounds"))
end
# Get raw memory pointer
data_ptr = LibWasmtime.wasmtime_memory_data(memory.store.context, memory.ptr)
# Create a view (be careful with GC)
try
# Create unsafe array view
view = unsafe_wrap(Array{UInt8}, data_ptr + offset, length, own=false)
return f(view)
catch e
rethrow(e)
end
end
# Usage
result = with_memory_view(memory, 0, 1024) do view
# Work with memory as Julia array
sum(view)
end
Global Variable Patterns
# Type-safe global access
function typed_global_get(global_var::Global, ::Type{T}) where T
value = get_global(global_var)
if value isa WasmValue{T}
return value.value
else
throw(TypeError("Global value is not of type $T"))
end
end
function typed_global_set!(global_var::Global, value::T) where T
wasm_value = to_wasm(value)
set_global!(global_var, wasm_value)
end
# Usage
counter = get_global(instance, "counter")
current_value = typed_global_get(counter, Int32)
typed_global_set!(counter, current_value + 1)
Table Management Patterns
# Function table management
function add_function_to_table(table::Table, func::Func)::Int32
current_size = table_size(table)
# Grow table by one element
prev_size = table_grow!(table, 1, WasmFuncRef(nothing))
# Set the new function
func_ref = WasmFuncRef(func)
set_table_element!(table, prev_size, func_ref)
return prev_size # Return index of added function
end
# Call function from table
function call_table_function(table::Table, index::Int32, params...)
func_ref = get_table_element(table, index)
if func_ref isa WasmFuncRef && func_ref.func !== nothing
return call(func_ref.func, collect(params))
else
throw(ArgumentError("No function at table index $index"))
end
end
Error Handling
Object Access Errors
function safe_object_access(instance::Instance, name::String, expected_type::Type)
try
export_item = get_export(instance, name)
actual_type = identify_export_type(export_item)
if actual_type != expected_type
throw(TypeError("Export '$name' is $actual_type, expected $expected_type"))
end
return export_item
catch e::WasmtimeError
if occursin("not found", e.message)
throw(KeyError("Export '$name' not found"))
else
rethrow(e)
end
end
end
Memory Safety
function safe_memory_operation(f, memory::Memory, offset::Int32, length::Int32)
current_size_bytes = memory_size(memory) * 65536
if offset < 0
throw(BoundsError("Negative offset"))
end
if length < 0
throw(BoundsError("Negative length"))
end
if offset + length > current_size_bytes
throw(BoundsError("Access beyond memory bounds"))
end
try
return f()
catch e
@error "Memory operation failed" offset=offset length=length exception=e
rethrow(e)
end
end
Best Practices
Object Lifecycle Management
# Objects are tied to their store's lifetime
function manage_object_lifecycle()
engine = Engine()
store = Store(engine)
instance = Instance(store, module_obj)
# Get objects
memory = get_memory(instance, "memory")
global_var = get_global(instance, "counter")
table = get_table(instance, "func_table")
# Objects remain valid as long as store is valid
# No manual cleanup needed
return (memory, global_var, table)
end
Type Safety
# Always check types before operations
function ensure_mutable_global(global_var::Global)
type_info = global_type(global_var)
if !type_info.is_mutable
throw(ArgumentError("Global is immutable"))
end
return global_var
end
Performance Optimization
# Cache frequently accessed objects
struct CachedObjects
memory::Union{Memory, Nothing}
globals::Dict{String, Global}
tables::Dict{String, Table}
end
function cache_objects(instance::Instance, names...)
cache = CachedObjects(nothing, Dict(), Dict())
for name in names
try
export_item = get_export(instance, name)
export_type = identify_export_type(export_item)
if export_type == Memory
cache.memory = convert_to_memory(export_item, instance.store)
elseif export_type == Global
cache.globals[name] = convert_to_global(export_item, instance.store)
elseif export_type == Table
cache.tables[name] = convert_to_table(export_item, instance.store)
end
catch e
@warn "Failed to cache object: $name" exception=e
end
end
return cache
end
WebAssembly objects provide powerful capabilities for data sharing and state management between Julia and WebAssembly code. The type-safe wrappers ensure memory safety while maintaining performance.