172 lines
4.5 KiB
Go
172 lines
4.5 KiB
Go
|
// Copyright 2017 The go-interpreter Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package wasm
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
// Import is an intreface implemented by types that can be imported by a WebAssembly module.
|
||
|
type Import interface {
|
||
|
isImport()
|
||
|
}
|
||
|
|
||
|
// ImportEntry describes an import statement in a Wasm module.
|
||
|
type ImportEntry struct {
|
||
|
ModuleName string // module name string
|
||
|
FieldName string // field name string
|
||
|
Kind External
|
||
|
|
||
|
// If Kind is Function, Type is a FuncImport containing the type index of the function signature
|
||
|
// If Kind is Table, Type is a TableImport containing the type of the imported table
|
||
|
// If Kind is Memory, Type is a MemoryImport containing the type of the imported memory
|
||
|
// If the Kind is Global, Type is a GlobalVarImport
|
||
|
Type Import
|
||
|
}
|
||
|
|
||
|
type FuncImport struct {
|
||
|
Type uint32
|
||
|
}
|
||
|
|
||
|
func (FuncImport) isImport() {}
|
||
|
|
||
|
type TableImport struct {
|
||
|
Type Table
|
||
|
}
|
||
|
|
||
|
func (TableImport) isImport() {}
|
||
|
|
||
|
type MemoryImport struct {
|
||
|
Type Memory
|
||
|
}
|
||
|
|
||
|
func (MemoryImport) isImport() {}
|
||
|
|
||
|
type GlobalVarImport struct {
|
||
|
Type GlobalVar
|
||
|
}
|
||
|
|
||
|
func (GlobalVarImport) isImport() {}
|
||
|
|
||
|
var (
|
||
|
ErrImportMutGlobal = errors.New("wasm: cannot import global mutable variable")
|
||
|
ErrNoExportsInImportedModule = errors.New("wasm: imported module has no exports")
|
||
|
)
|
||
|
|
||
|
type InvalidExternalError uint8
|
||
|
|
||
|
func (e InvalidExternalError) Error() string {
|
||
|
return fmt.Sprintf("wasm: invalid external_kind value %d", uint8(e))
|
||
|
}
|
||
|
|
||
|
type ExportNotFoundError struct {
|
||
|
ModuleName string
|
||
|
FieldName string
|
||
|
}
|
||
|
|
||
|
type KindMismatchError struct {
|
||
|
ModuleName string
|
||
|
FieldName string
|
||
|
Import External
|
||
|
Export External
|
||
|
}
|
||
|
|
||
|
func (e KindMismatchError) Error() string {
|
||
|
return fmt.Sprintf("wasm: Mismatching import and export external kind values for %s.%s (%v, %v)", e.FieldName, e.ModuleName, e.Import, e.Export)
|
||
|
}
|
||
|
|
||
|
func (e ExportNotFoundError) Error() string {
|
||
|
return fmt.Sprintf("wasm: couldn't find export with name %s in module %s", e.FieldName, e.ModuleName)
|
||
|
}
|
||
|
|
||
|
type InvalidFunctionIndexError uint32
|
||
|
|
||
|
func (e InvalidFunctionIndexError) Error() string {
|
||
|
return fmt.Sprintf("wasm: Invalid index to function index space: %#x", uint32(e))
|
||
|
}
|
||
|
|
||
|
func (module *Module) resolveImports(resolve ResolveFunc) error {
|
||
|
if module.Import == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
modules := make(map[string]*Module)
|
||
|
|
||
|
var funcs uint32
|
||
|
for _, importEntry := range module.Import.Entries {
|
||
|
importedModule, ok := modules[importEntry.ModuleName]
|
||
|
if !ok {
|
||
|
var err error
|
||
|
importedModule, err = resolve(importEntry.ModuleName)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
modules[importEntry.ModuleName] = importedModule
|
||
|
}
|
||
|
|
||
|
if importedModule.Export == nil {
|
||
|
return ErrNoExportsInImportedModule
|
||
|
}
|
||
|
|
||
|
exportEntry, ok := importedModule.Export.Entries[importEntry.FieldName]
|
||
|
if !ok {
|
||
|
return ExportNotFoundError{importEntry.ModuleName, importEntry.FieldName}
|
||
|
}
|
||
|
|
||
|
if exportEntry.Kind != importEntry.Kind {
|
||
|
return KindMismatchError{
|
||
|
FieldName: importEntry.FieldName,
|
||
|
ModuleName: importEntry.ModuleName,
|
||
|
Import: importEntry.Kind,
|
||
|
Export: exportEntry.Kind,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
index := exportEntry.Index
|
||
|
|
||
|
switch exportEntry.Kind {
|
||
|
case ExternalFunction:
|
||
|
fn := importedModule.GetFunction(int(index))
|
||
|
if fn == nil {
|
||
|
return InvalidFunctionIndexError(index)
|
||
|
}
|
||
|
module.FunctionIndexSpace = append(module.FunctionIndexSpace, *fn)
|
||
|
module.Code.Bodies = append(module.Code.Bodies, *fn.Body)
|
||
|
module.imports.Funcs = append(module.imports.Funcs, funcs)
|
||
|
funcs++
|
||
|
case ExternalGlobal:
|
||
|
glb := importedModule.GetGlobal(int(index))
|
||
|
if glb == nil {
|
||
|
return InvalidGlobalIndexError(index)
|
||
|
}
|
||
|
if glb.Type.Mutable {
|
||
|
return ErrImportMutGlobal
|
||
|
}
|
||
|
module.GlobalIndexSpace = append(module.GlobalIndexSpace, *glb)
|
||
|
module.imports.Globals++
|
||
|
|
||
|
// In both cases below, index should be always 0 (according to the MVP)
|
||
|
// We check it against the length of the index space anyway.
|
||
|
case ExternalTable:
|
||
|
if int(index) >= len(importedModule.TableIndexSpace) {
|
||
|
return InvalidTableIndexError(index)
|
||
|
}
|
||
|
module.TableIndexSpace[0] = importedModule.TableIndexSpace[0]
|
||
|
module.imports.Tables++
|
||
|
case ExternalMemory:
|
||
|
if int(index) >= len(importedModule.LinearMemoryIndexSpace) {
|
||
|
return InvalidLinearMemoryIndexError(index)
|
||
|
}
|
||
|
module.LinearMemoryIndexSpace[0] = importedModule.LinearMemoryIndexSpace[0]
|
||
|
module.imports.Memories++
|
||
|
default:
|
||
|
return InvalidExternalError(exportEntry.Kind)
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|