181 lines
4.8 KiB
Go
181 lines
4.8 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 (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
type InvalidTableIndexError uint32
|
|
|
|
func (e InvalidTableIndexError) Error() string {
|
|
return fmt.Sprintf("wasm: Invalid table to table index space: %d", uint32(e))
|
|
}
|
|
|
|
type InvalidValueTypeInitExprError struct {
|
|
Wanted reflect.Kind
|
|
Got reflect.Kind
|
|
}
|
|
|
|
func (e InvalidValueTypeInitExprError) Error() string {
|
|
return fmt.Sprintf("wasm: Wanted initializer expression to return %v value, got %v", e.Wanted, e.Got)
|
|
}
|
|
|
|
type InvalidLinearMemoryIndexError uint32
|
|
|
|
func (e InvalidLinearMemoryIndexError) Error() string {
|
|
return fmt.Sprintf("wasm: Invalid linear memory index: %d", uint32(e))
|
|
}
|
|
|
|
// Functions for populating and looking up entries in a module's index space.
|
|
// More info: http://webassembly.org/docs/modules/#function-index-space
|
|
|
|
func (m *Module) populateFunctions() error {
|
|
if m.Types == nil || m.Function == nil {
|
|
return nil
|
|
}
|
|
|
|
for codeIndex, typeIndex := range m.Function.Types {
|
|
if int(typeIndex) >= len(m.Types.Entries) {
|
|
return InvalidFunctionIndexError(typeIndex)
|
|
}
|
|
|
|
fn := Function{
|
|
Sig: &m.Types.Entries[typeIndex],
|
|
Body: &m.Code.Bodies[codeIndex],
|
|
}
|
|
|
|
m.FunctionIndexSpace = append(m.FunctionIndexSpace, fn)
|
|
}
|
|
|
|
funcs := make([]uint32, 0, len(m.Function.Types)+len(m.imports.Funcs))
|
|
funcs = append(funcs, m.imports.Funcs...)
|
|
funcs = append(funcs, m.Function.Types...)
|
|
m.Function.Types = funcs
|
|
return nil
|
|
}
|
|
|
|
// GetFunction returns a *Function, based on the function's index in
|
|
// the function index space. Returns nil when the index is invalid
|
|
func (m *Module) GetFunction(i int) *Function {
|
|
if i >= len(m.FunctionIndexSpace) || i < 0 {
|
|
return nil
|
|
}
|
|
|
|
return &m.FunctionIndexSpace[i]
|
|
}
|
|
|
|
func (m *Module) populateGlobals() error {
|
|
if m.Global == nil {
|
|
return nil
|
|
}
|
|
|
|
m.GlobalIndexSpace = append(m.GlobalIndexSpace, m.Global.Globals...)
|
|
logger.Printf("There are %d entries in the global index spaces.", len(m.GlobalIndexSpace))
|
|
return nil
|
|
}
|
|
|
|
// GetGlobal returns a *GlobalEntry, based on the global index space.
|
|
// Returns nil when the index is invalid
|
|
func (m *Module) GetGlobal(i int) *GlobalEntry {
|
|
if i >= len(m.GlobalIndexSpace) || i < 0 {
|
|
return nil
|
|
}
|
|
|
|
return &m.GlobalIndexSpace[i]
|
|
}
|
|
|
|
func (m *Module) populateTables() error {
|
|
if m.Table == nil || len(m.Table.Entries) == 0 || m.Elements == nil || len(m.Elements.Entries) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, elem := range m.Elements.Entries {
|
|
// the MVP dictates that index should always be zero, we shuold
|
|
// probably check this
|
|
if int(elem.Index) >= len(m.TableIndexSpace) {
|
|
return InvalidTableIndexError(elem.Index)
|
|
}
|
|
|
|
val, err := m.ExecInitExpr(elem.Offset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
offset, ok := val.(int32)
|
|
if !ok {
|
|
return InvalidValueTypeInitExprError{reflect.Int32, reflect.TypeOf(offset).Kind()}
|
|
}
|
|
|
|
table := m.TableIndexSpace[int(elem.Index)]
|
|
if int(offset)+len(elem.Elems) > len(table) {
|
|
data := make([]uint32, int(offset)+len(elem.Elems))
|
|
copy(data[offset:], elem.Elems)
|
|
copy(data, table)
|
|
m.TableIndexSpace[int(elem.Index)] = data
|
|
} else {
|
|
copy(table[int(offset):], elem.Elems)
|
|
m.TableIndexSpace[int(elem.Index)] = table
|
|
}
|
|
}
|
|
|
|
logger.Printf("There are %d entries in the table index space.", len(m.TableIndexSpace))
|
|
return nil
|
|
}
|
|
|
|
// GetTableElement returns an element from the tableindex space indexed
|
|
// by the integer index. It returns an error if index is invalid.
|
|
func (m *Module) GetTableElement(index int) (uint32, error) {
|
|
if index >= len(m.TableIndexSpace[0]) {
|
|
return 0, InvalidTableIndexError(index)
|
|
}
|
|
|
|
return m.TableIndexSpace[0][index], nil
|
|
}
|
|
|
|
func (m *Module) populateLinearMemory() error {
|
|
if m.Data == nil || len(m.Data.Entries) == 0 {
|
|
return nil
|
|
}
|
|
// each module can only have a single linear memory in the MVP
|
|
|
|
for _, entry := range m.Data.Entries {
|
|
if entry.Index != 0 {
|
|
return InvalidLinearMemoryIndexError(entry.Index)
|
|
}
|
|
|
|
val, err := m.ExecInitExpr(entry.Offset)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
offset, ok := val.(int32)
|
|
if !ok {
|
|
return InvalidValueTypeInitExprError{reflect.Int32, reflect.TypeOf(offset).Kind()}
|
|
}
|
|
|
|
memory := m.LinearMemoryIndexSpace[int(entry.Index)]
|
|
if int(offset)+len(entry.Data) > len(memory) {
|
|
data := make([]byte, int(offset)+len(entry.Data))
|
|
copy(data[offset:], entry.Data)
|
|
copy(data, memory)
|
|
m.LinearMemoryIndexSpace[int(entry.Index)] = data
|
|
} else {
|
|
copy(memory[int(offset):], entry.Data)
|
|
m.LinearMemoryIndexSpace[int(entry.Index)] = memory
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *Module) GetLinearMemoryData(index int) (byte, error) {
|
|
if index >= len(m.LinearMemoryIndexSpace[0]) {
|
|
return 0, InvalidLinearMemoryIndexError(uint32(index))
|
|
|
|
}
|
|
|
|
return m.LinearMemoryIndexSpace[0][index], nil
|
|
}
|