Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Begin adding WASI support #72

Merged
merged 4 commits into from
Nov 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 123 additions & 18 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ categories = ["wasm"]
crate-type = ["cdylib"]

[dependencies]
wasmer-runtime-c-api = "0.8.0"
wasmer-runtime-c-api = { path = "../wasmer/lib/runtime-c-api", version = "0.8.0", features = ["wasi"] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just require wasmer 0.11.0, which has wasi as a default feature.

148 changes: 148 additions & 0 deletions wasmer/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ type cWasmerImportDescriptorsT C.wasmer_import_descriptors_t
type cWasmerImportExportKind C.wasmer_import_export_kind
type cWasmerImportExportValue C.wasmer_import_export_value
type cWasmerImportFuncT C.wasmer_import_func_t
type cWasmerImportObjectT C.wasmer_import_object_t
type cWasmerImportT C.wasmer_import_t
type cWasmerInstanceContextT C.wasmer_instance_context_t
type cWasmerInstanceT C.wasmer_instance_t
type cWasmerWasiMapDirEntryT C.wasmer_wasi_map_dir_entry_t
type cWasmerMemoryT C.wasmer_memory_t
type cWasmerModuleT C.wasmer_module_t
type cWasmerResultT C.wasmer_result_t
Expand Down Expand Up @@ -56,6 +58,109 @@ func cNewWasmerImportT(moduleName string, importName string, function *cWasmerIm
return (cWasmerImportT)(importedFunction)
}

func cGetParamsForImportFunc(function *cWasmerImportFuncT) []cWasmerValueTag {
var arity C.uint32_t = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 is the value, it's suggested to remove it.

var result = C.wasmer_import_func_params_arity((*C.wasmer_import_func_t)(function), &arity)

if result != C.WASMER_OK {
return nil
}

var params = make([]cWasmerValueTag, (int)(arity))
if arity == 0 {
return params
}

result = C.wasmer_import_func_params(
(*C.wasmer_import_func_t)(function),
(*C.wasmer_value_tag)(unsafe.Pointer(&params[0])),
arity)
if result != C.WASMER_OK {
return nil
}

return params
}

func cGetReturnsForImportFunc(function *cWasmerImportFuncT) []cWasmerValueTag {
var arity C.uint32_t = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 is the default value, it's suggested to remove it.

var result = C.wasmer_import_func_returns_arity((*C.wasmer_import_func_t)(function), &arity)

if result != C.WASMER_OK {
return nil
}

var returns = make([]cWasmerValueTag, (int)(arity))
if arity == 0 {
return returns
}

result = C.wasmer_import_func_returns(
(*C.wasmer_import_func_t)(function),
(*C.wasmer_value_tag)(unsafe.Pointer(&returns[0])),
arity)
if result != C.WASMER_OK {
return nil
}

return returns
}

func cNewWasmerDefaultWasiImportObject() *cWasmerImportObjectT {
return (*cWasmerImportObjectT)(C.wasmer_wasi_generate_default_import_object())
}

func cNewWasmerWasiImportObject(
args *cWasmerByteArray, argsLen int,
envs *cWasmerByteArray, envsLen int,
preopenedFiles *cWasmerByteArray, preopenFilesLen int,
mappedDirs *cWasmerWasiMapDirEntryT, mappedDirsLen int,
) *cWasmerImportObjectT {
return (*cWasmerImportObjectT)(C.wasmer_wasi_generate_import_object(
(*C.wasmer_byte_array)(args), (C.uint)(argsLen),
(*C.wasmer_byte_array)(envs), (C.uint)(envsLen),
(*C.wasmer_byte_array)(preopenedFiles), (C.uint)(preopenFilesLen),
(*C.wasmer_wasi_map_dir_entry_t)(mappedDirs), (C.uint)(mappedDirsLen),
))
}

func cWasmerImportObjectDestroy(importObject *cWasmerImportObjectT) {
C.wasmer_import_object_destroy((*C.wasmer_import_object_t)(importObject))
}

func cWasmerImportObjectExtend(importObject *cWasmerImportObjectT, imports *cWasmerImportT, importLen cUint) cWasmerResultT {
return (cWasmerResultT)(C.wasmer_import_object_extend((*C.wasmer_import_object_t)(importObject),
(*C.wasmer_import_t)(imports),
(C.uint)(importLen),
))
}

func cNewWasmerImportObject() *cWasmerImportObjectT {
return (*cWasmerImportObjectT)(C.wasmer_import_object_new())
}

func cWasmerImportObjectGetFunctions(importObject *cWasmerImportObjectT) []cWasmerImportT {
var iter = C.wasmer_import_object_iterate_functions((*C.wasmer_import_object_t)(importObject))
if iter == nil {
return nil
}
var imports []cWasmerImportT

for !C.wasmer_import_object_iter_at_end(iter) {
var imp cWasmerImportT
result := C.wasmer_import_object_iter_next(iter, (*C.wasmer_import_t)(&imp))
if result != C.WASMER_OK {
C.wasmer_import_object_imports_destroy((*C.wasmer_import_t)(&imports[0]), (C.uint)(len(imports)))
C.wasmer_import_object_iter_destroy(iter)
return nil
break
}
imports = append(imports, imp)
}

return imports
}

func cWasmerCompile(module **cWasmerModuleT, wasmBytes *cUchar, wasmBytesLength cUint) cWasmerResultT {
return (cWasmerResultT)(C.wasmer_compile(
(**C.wasmer_module_t)(unsafe.Pointer(module)),
Expand Down Expand Up @@ -342,6 +447,18 @@ func cWasmerModuleDestroy(module *cWasmerModuleT) {
C.wasmer_module_destroy((*C.wasmer_module_t)(module))
}

func cWasmerModuleImportInstantiate(
instance **cWasmerInstanceT,
module *cWasmerModuleT,
importObject *cWasmerImportObjectT,
) cWasmerResultT {
return (cWasmerResultT)(C.wasmer_module_import_instantiate(
(**C.wasmer_instance_t)(unsafe.Pointer(instance)),
(*C.wasmer_module_t)(module),
(*C.wasmer_import_object_t)(importObject),
))
}

func cWasmerModuleInstantiate(
module *cWasmerModuleT,
instance **cWasmerInstanceT,
Expand Down Expand Up @@ -421,3 +538,34 @@ func cGoStringToWasmerByteArray(string string) cWasmerByteArray {

return byteArray
}

func cAliasAndHostPathToWasiDirEntry(alias string, hostPath string) cWasmerWasiMapDirEntryT {
var wasiMappedDir cWasmerWasiMapDirEntryT
wasiMappedDir.alias = (C.wasmer_byte_array)(cGoStringToWasmerByteArray(alias))
wasiMappedDir.host_file_path = (C.wasmer_byte_array)(cGoStringToWasmerByteArray(hostPath))

return wasiMappedDir
}

// returns the module name and import name for a given import
func cGetInfoFromImport(inner *cWasmerImportT) (string, string) {
moduleName := string(C.GoBytes(
unsafe.Pointer(inner.module_name.bytes),
(C.int)(inner.module_name.bytes_len)))
importName := string(C.GoBytes(
unsafe.Pointer(inner.import_name.bytes),
(C.int)(inner.import_name.bytes_len)))

return moduleName, importName
}

// returns the raw pointer to the inner function or nil if it's not a function
func cGetFunctionFromImport(inner *cWasmerImportT) *cWasmerImportFuncT {
if inner.tag == C.WASM_FUNCTION {
var funcPtrBytes [8]byte = inner.value
var funcPtrAddr *byte = &funcPtrBytes[0]
var funcPtrPtr = (**cWasmerImportFuncT)(unsafe.Pointer(funcPtrAddr))
return *funcPtrPtr
}
return nil
}
139 changes: 139 additions & 0 deletions wasmer/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,90 @@ type ImportedFunctionError struct {
message string
}

// ImportObjectError represents errors related to `ImportObject`s
type ImportObjectError struct {
message string
}

// A type that owns a set of imports.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation must start by the name of the type or the name of the function. So it becomes:

ImportObject owns a set of imports.

It's a Go rule.

// It can be combined with a `Module` to create an `Instance`
type ImportObject struct {
inner *cWasmerImportObjectT
}

// Creates an empty `ImportObject`
func NewImportObject() *ImportObject {
var inner = cNewWasmerImportObject()

return &ImportObject{inner}
}

// Returns `*Imports` for a given `ImportObject`
func (importObject *ImportObject) GetImports() (*Imports, error) {
imports := cWasmerImportObjectGetFunctions(importObject.inner)
if imports == nil {
return nil, NewImportObjectError("Could not get functions from import object")
}
outImports := NewImports()
for _, imp := range imports {
rawFunc := cGetFunctionFromImport(&imp)
if rawFunc == nil {
// this should never happen
continue
}
namespaceName, importName := cGetInfoFromImport(&imp)

oi, err := outImports.appendRaw(namespaceName, importName, rawFunc)
if err != nil {
return nil, err
}
outImports = oi
}

return outImports, nil
}

// Adds the given imports to the exsiting import object
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- exsiting
+ existing

func (importObject *ImportObject) Extend(imports Imports) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think to turn this API to:

func (importObject *ImportObject) Extend(incomingImportObject ImportObject) error;

It will help to slowly deprecate the Imports API in the future if we need to.

var numberOfImports = len(imports.imports)
if numberOfImports == 0 {
return nil
}
var cImports = make([]cWasmerImportT, numberOfImports)
var importFunctionNth = 0

for importName, importFunction := range imports.imports {
cImports[importFunctionNth] = *getCWasmerImport(importName, importFunction)
importFunctionNth++
}

var extendResult = cWasmerImportObjectExtend(importObject.inner,
(*cWasmerImportT)(unsafe.Pointer(&cImports[0])),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must check that importFunctionNth is greater than 0 before reading &cImports[0].

(cUint)(len(imports.imports)))

if extendResult != cWasmerOk {
return NewImportObjectError("Could not extend import object with the given imports")
}

return nil
}

// Frees the `ImportObject`
func (importObject *ImportObject) Close() {
cWasmerImportObjectDestroy(importObject.inner)
}

// Constructs a new `ImportObjectError`
func NewImportObjectError(message string) *ImportObjectError {
return &ImportObjectError{message}
}

// ImportedFunctionError is an actual error. The `Error` function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- ImportedFunctionError
+ ImportObjectError

// returns the error message.
func (error *ImportObjectError) Error() string {
return fmt.Sprintf(error.message)
}

// NewImportedFunctionError constructs a new `ImportedFunctionError`,
// where `functionName` is the name of the imported function, and
// `message` is the error message. If the error message contains `%s`,
Expand Down Expand Up @@ -148,6 +232,29 @@ func (imports *Imports) Append(importName string, implementation interface{}, cg
return imports, nil
}

// Like Append but not for Go imports
func (imports *Imports) appendRaw(namespace string, importName string, wasmerImportFunc *cWasmerImportFuncT) (*Imports, error) {
params := cGetParamsForImportFunc(wasmerImportFunc)
if params == nil {
return imports, NewImportedFunctionError(importName, fmt.Sprintf("could not get parameters for %s %s", namespace, importName))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewImportedFunctionError already formats the importName, so you can write something like:

fmt.Sprintf("Could not get parameters for `%%s` in namespace `%s`", namespace)

}
returns := cGetParamsForImportFunc(wasmerImportFunc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't we suppose to use cGetReturnsForImportfunc here?

if returns == nil {
return imports, NewImportedFunctionError(importName, fmt.Sprintf("could not get returns for %s %s", namespace, importName))
}

imports.imports[importName] = Import{
nil,
unsafe.Pointer(wasmerImportFunc),
wasmerImportFunc,
params,
returns,
namespace,
}

return imports, nil
}

// Close closes/frees all imported functions that have been registered by Wasmer.
func (imports *Imports) Close() {
for _, importFunction := range imports.imports {
Expand All @@ -157,6 +264,38 @@ func (imports *Imports) Close() {
}
}

// Helper function: Get a C import for a given import
func getCWasmerImport(importName string, importFunction Import) *cWasmerImportT {
var wasmInputsArity = len(importFunction.wasmInputs)
var wasmOutputsArity = len(importFunction.wasmOutputs)

var importFunctionInputsCPointer *cWasmerValueTag
var importFunctionOutputsCPointer *cWasmerValueTag

if wasmInputsArity > 0 {
importFunctionInputsCPointer = (*cWasmerValueTag)(unsafe.Pointer(&importFunction.wasmInputs[0]))
}

if wasmOutputsArity > 0 {
importFunctionOutputsCPointer = (*cWasmerValueTag)(unsafe.Pointer(&importFunction.wasmOutputs[0]))
}

importFunction.importedFunctionPointer = cWasmerImportFuncNew(
importFunction.cgoPointer,
importFunctionInputsCPointer,
cUint(wasmInputsArity),
importFunctionOutputsCPointer,
cUint(wasmOutputsArity),
)
var newImport = cNewWasmerImportT(
importFunction.namespace,
importName,
importFunction.importedFunctionPointer,
)

return &newImport
}

// InstanceContext represents a way to access instance API from within
// an imported context.
type InstanceContext struct {
Expand Down
Loading