Skip to content

Commit

Permalink
Implement module linking proposal
Browse files Browse the repository at this point in the history
This commit binds the support added in bytecodealliance/wasmtime#2472 to
bring support for the module linking to this Go extension. The support
here is similar to bytecodealliance/wasmtime-py#47, which is hooking up
modules/instances to `Extern` as well as adding dedicated
instance/module types.
  • Loading branch information
alexcrichton committed Dec 3, 2020
1 parent e5fed01 commit 0339dcb
Show file tree
Hide file tree
Showing 12 changed files with 422 additions and 50 deletions.
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ func (cfg *Config) SetWasmMultiValue(enabled bool) {
runtime.KeepAlive(cfg)
}

// SetWasmModuleLinking configures whether the wasm module linking proposal is enabled
func (cfg *Config) SetWasmModuleLinking(enabled bool) {
C.wasmtime_config_wasm_module_linking_set(cfg.ptr(), C.bool(enabled))
runtime.KeepAlive(cfg)
}

// SetStrategy configures what compilation strategy is used to compile wasm code
func (cfg *Config) SetStrategy(strat Strategy) error {
err := C.wasmtime_config_strategy_set(cfg.ptr(), C.wasmtime_strategy_t(strat))
Expand Down
22 changes: 21 additions & 1 deletion extern.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package wasmtime

// #include <wasm.h>
// #include <wasmtime.h>
import "C"
import "runtime"

Expand Down Expand Up @@ -91,6 +91,26 @@ func (e *Extern) Table() *Table {
return mkTable(ret, e.freelist, e.owner())
}

// Module returns a Module if this export is a module or nil otherwise
func (e *Extern) Module() *Module {
ret := C.wasm_extern_as_module(e.ptr())
if ret == nil {
return nil
}

return mkModule(ret, e.owner())
}

// Instance returns a Instance if this export is a module or nil otherwise
func (e *Extern) Instance() *Instance {
ret := C.wasm_extern_as_instance(e.ptr())
if ret == nil {
return nil
}

return mkInstance(ret, e.freelist, e.owner())
}

func (e *Extern) AsExtern() *Extern {
return e
}
12 changes: 9 additions & 3 deletions importtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,18 @@ func (ty *ImportType) Module() string {
return ret
}

// Name returns the name in the module this import type is importing
func (ty *ImportType) Name() string {
// Name returns the name in the module this import type is importing.
//
// Note that the returned string may be `nil` with the module linking proposal
// where this field is optional in the import type.
func (ty *ImportType) Name() *string {
ptr := C.wasm_importtype_name(ty.ptr())
if ptr == nil {
return nil
}
ret := C.GoStringN(ptr.data, C.int(ptr.size))
runtime.KeepAlive(ty)
return ret
return &ret
}

// Type returns the type of item this import type expects
Expand Down
4 changes: 2 additions & 2 deletions importtype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func TestImportType(t *testing.T) {
if ty.Module() != "a" {
panic("invalid module")
}
if ty.Name() != "b" {
if *ty.Name() != "b" {
panic("invalid name")
}
if ty.Type().FuncType() == nil {
Expand All @@ -20,7 +20,7 @@ func TestImportType(t *testing.T) {
if ty.Module() != "" {
panic("invalid module")
}
if ty.Name() != "" {
if *ty.Name() != "" {
panic("invalid name")
}
if ty.Type().GlobalType() == nil {
Expand Down
65 changes: 48 additions & 17 deletions instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import (
// Instance is an instantiated module instance.
// Once a module has been instantiated as an Instance, any exported function can be invoked externally via its function address funcaddr in the store S and an appropriate list val∗ of argument values.
type Instance struct {
_ptr *C.wasm_instance_t
exports map[string]*Extern
freelist *freeList
_ptr *C.wasm_instance_t
exports map[string]*Extern
exportsPopulated bool
freelist *freeList
_owner interface{}
}

// NewInstance instantiates a WebAssembly `module` with the `imports` provided.
Expand Down Expand Up @@ -52,24 +54,24 @@ func NewInstance(store *Store, module *Module, imports []*Extern) (*Instance, er
if trap != nil {
return nil, mkTrap(trap)
}
return mkInstance(ptr, store, module), nil
return mkInstance(ptr, store.freelist, nil), nil
}

func mkInstance(ptr *C.wasm_instance_t, store *Store, module *Module) *Instance {
func mkInstance(ptr *C.wasm_instance_t, freelist *freeList, owner interface{}) *Instance {
instance := &Instance{
_ptr: ptr,
exports: make(map[string]*Extern),
freelist: store.freelist,
_ptr: ptr,
exports: make(map[string]*Extern),
exportsPopulated: false,
freelist: freelist,
_owner: owner,
}
runtime.SetFinalizer(instance, func(instance *Instance) {
freelist := instance.freelist
freelist.lock.Lock()
defer freelist.lock.Unlock()
freelist.instances = append(freelist.instances, instance._ptr)
})
exports := instance.Exports()
for i, ty := range module.Exports() {
instance.exports[ty.Name()] = exports[i]
if owner == nil {
runtime.SetFinalizer(instance, func(instance *Instance) {
freelist := instance.freelist
freelist.lock.Lock()
defer freelist.lock.Unlock()
freelist.instances = append(freelist.instances, instance._ptr)
})
}
return instance
}
Expand All @@ -80,6 +82,20 @@ func (i *Instance) ptr() *C.wasm_instance_t {
return ret
}

func (i *Instance) owner() interface{} {
if i._owner != nil {
return i._owner
}
return i
}

// Type returns an `InstanceType` that corresponds for this instance.
func (i *Instance) Type() *InstanceType {
ptr := C.wasm_instance_type(i.ptr())
runtime.KeepAlive(i)
return mkInstanceType(ptr, nil)
}

type externList struct {
vec C.wasm_extern_vec_t
}
Expand Down Expand Up @@ -114,5 +130,20 @@ func (i *Instance) Exports() []*Extern {
//
// May return `nil` if this instance has no export named `name`
func (i *Instance) GetExport(name string) *Extern {
if !i.exportsPopulated {
i.populateExports()
}
return i.exports[name]
}

func (i *Instance) populateExports() {
exports := i.Exports()
for j, ty := range i.Type().Exports() {
i.exports[ty.Name()] = exports[j]
}
}

func (i *Instance) AsExtern() *Extern {
ptr := C.wasm_instance_as_extern(i.ptr())
return mkExtern(ptr, i.freelist, i.owner())
}
49 changes: 49 additions & 0 deletions instancetype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package wasmtime

// #include <wasmtime.h>
import "C"
import "runtime"

// InstanceType describes the exports of an instance.
type InstanceType struct {
_ptr *C.wasm_instancetype_t
_owner interface{}
}

func mkInstanceType(ptr *C.wasm_instancetype_t, owner interface{}) *InstanceType {
instancetype := &InstanceType{_ptr: ptr, _owner: owner}
if owner == nil {
runtime.SetFinalizer(instancetype, func(instancetype *InstanceType) {
C.wasm_instancetype_delete(instancetype._ptr)
})
}
return instancetype
}

func (ty *InstanceType) ptr() *C.wasm_instancetype_t {
ret := ty._ptr
maybeGC()
return ret
}

func (ty *InstanceType) owner() interface{} {
if ty._owner != nil {
return ty._owner
}
return ty
}

// AsExternType converts this type to an instance of `ExternType`
func (ty *InstanceType) AsExternType() *ExternType {
ptr := C.wasm_instancetype_as_externtype_const(ty.ptr())
return mkExternType(ptr, ty.owner())
}

// Exports returns a list of `ExportType` items which are the items that will
// be exported by this instance after instantiation.
func (ty *InstanceType) Exports() []*ExportType {
exports := &exportTypeList{}
C.wasm_instancetype_exports(ty.ptr(), &exports.vec)
runtime.KeepAlive(ty)
return exports.mkGoList()
}
2 changes: 1 addition & 1 deletion linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,5 @@ func (l *Linker) Instantiate(module *Module) (*Instance, error) {
if trap != nil {
return nil, mkTrap(trap)
}
return mkInstance(ret, l.Store, module), nil
return mkInstance(ret, l.Store.freelist, nil), nil
}
79 changes: 60 additions & 19 deletions module.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (
// Modules organized WebAssembly programs as the unit of deployment, loading, and compilation.
type Module struct {
_ptr *C.wasm_module_t
Engine *Engine
_owner interface{}
}

// NewModule compiles a new `Module` from the `wasm` provided with the given configuration
Expand All @@ -58,7 +58,7 @@ func NewModule(engine *Engine, wasm []byte) (*Module, error) {
return nil, mkError(err)
}

return mkModule(ptr, engine), nil
return mkModule(ptr, nil), nil
}

// NewModuleFromFile reads the contents of the `file` provided and interprets them as either the
Expand Down Expand Up @@ -99,11 +99,13 @@ func ModuleValidate(store *Store, wasm []byte) error {
return mkError(err)
}

func mkModule(ptr *C.wasm_module_t, engine *Engine) *Module {
module := &Module{_ptr: ptr, Engine: engine}
runtime.SetFinalizer(module, func(module *Module) {
C.wasm_module_delete(module._ptr)
})
func mkModule(ptr *C.wasm_module_t, owner interface{}) *Module {
module := &Module{_ptr: ptr}
if owner == nil {
runtime.SetFinalizer(module, func(module *Module) {
C.wasm_module_delete(module._ptr)
})
}
return module
}

Expand All @@ -113,10 +115,40 @@ func (m *Module) ptr() *C.wasm_module_t {
return ret
}

func (m *Module) owner() interface{} {
if m._owner != nil {
return m._owner
}
return m
}

// Type returns a `ModuleType` that corresponds for this module.
func (m *Module) Type() *ModuleType {
ptr := C.wasm_module_type(m.ptr())
runtime.KeepAlive(m)
return mkModuleType(ptr, nil)
}

type importTypeList struct {
vec C.wasm_importtype_vec_t
}

func (list *importTypeList) mkGoList() []*ImportType {
runtime.SetFinalizer(list, func(imports *importTypeList) {
C.wasm_importtype_vec_delete(&imports.vec)
})

ret := make([]*ImportType, int(list.vec.size))
base := unsafe.Pointer(list.vec.data)
var ptr *C.wasm_importtype_t
for i := 0; i < int(list.vec.size); i++ {
ptr := *(**C.wasm_importtype_t)(unsafe.Pointer(uintptr(base) + unsafe.Sizeof(ptr)*uintptr(i)))
ty := mkImportType(ptr, list)
ret[i] = ty
}
return ret
}

// Imports returns a list of `ImportType` items which are the items imported by this
// module and are required for instantiation.
func (m *Module) Imports() []*ImportType {
Expand All @@ -142,27 +174,31 @@ type exportTypeList struct {
vec C.wasm_exporttype_vec_t
}

// Exports returns a list of `ExportType` items which are the items that will
// be exported by this module after instantiation.
func (m *Module) Exports() []*ExportType {
exports := &exportTypeList{}
C.wasm_module_exports(m.ptr(), &exports.vec)
runtime.KeepAlive(m)
runtime.SetFinalizer(exports, func(exports *exportTypeList) {
func (list *exportTypeList) mkGoList() []*ExportType {
runtime.SetFinalizer(list, func(exports *exportTypeList) {
C.wasm_exporttype_vec_delete(&exports.vec)
})

ret := make([]*ExportType, int(exports.vec.size))
base := unsafe.Pointer(exports.vec.data)
ret := make([]*ExportType, int(list.vec.size))
base := unsafe.Pointer(list.vec.data)
var ptr *C.wasm_exporttype_t
for i := 0; i < int(exports.vec.size); i++ {
for i := 0; i < int(list.vec.size); i++ {
ptr := *(**C.wasm_exporttype_t)(unsafe.Pointer(uintptr(base) + unsafe.Sizeof(ptr)*uintptr(i)))
ty := mkExportType(ptr, exports)
ty := mkExportType(ptr, list)
ret[i] = ty
}
return ret
}

// Exports returns a list of `ExportType` items which are the items that will
// be exported by this module after instantiation.
func (m *Module) Exports() []*ExportType {
exports := &exportTypeList{}
C.wasm_module_exports(m.ptr(), &exports.vec)
runtime.KeepAlive(m)
return exports.mkGoList()
}

// NewModuleDeserialize decodes and deserializes in-memory bytes previously
// produced by `module.Serialize()`.
//
Expand Down Expand Up @@ -195,7 +231,7 @@ func NewModuleDeserialize(engine *Engine, encoded []byte) (*Module, error) {
return nil, mkError(err)
}

return mkModule(ptr, engine), nil
return mkModule(ptr, nil), nil
}

// Serialize will convert this in-memory compiled module into a list of bytes.
Expand All @@ -217,3 +253,8 @@ func (m *Module) Serialize() ([]byte, error) {
C.wasm_byte_vec_delete(&retVec)
return ret, nil
}

func (m *Module) AsExtern() *Extern {
ptr := C.wasm_module_as_extern(m.ptr())
return mkExtern(ptr, nil, m.owner())
}
Loading

0 comments on commit 0339dcb

Please sign in to comment.