// 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 }