Skip to content

Commit

Permalink
Adds ModuleConfig.Validate() to allow pre-flight checks on source (#281)
Browse files Browse the repository at this point in the history
This adds `ModuleConfig.Validate()` as needed by wapc-go to ensure a
module instantiated many times later is checked first. This is a method
of config in case we later add the ability to force a format. For now,
it relies on detection and the call-site only use binary anyway.

Signed-off-by: Adrian Cole <[email protected]>
  • Loading branch information
codefromthecrypt authored Feb 23, 2022
1 parent 0186e41 commit fc26fdd
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 2 deletions.
21 changes: 21 additions & 0 deletions wasm.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ type ModuleConfig struct {
Name string
// Source is the WebAssembly 1.0 (MVP) text or binary encoding of the module.
Source []byte

validatedSource []byte
decodedModule *internalwasm.Module
}

// Validate eagerly decodes the Source and errs if it is invalid.
//
// This is used to pre-flight check and cache the module for later instantiation.
func (m *ModuleConfig) Validate() (err error) {
_, _, err = decodeModule(m)
return err
}

// InstantiateModule instantiates the module namespace or errs if the configuration was invalid.
Expand Down Expand Up @@ -96,6 +107,12 @@ func decodeModule(module *ModuleConfig) (m *internalwasm.Module, name string, er
return
}

// Check if this source was already decoded
if bytes.Equal(module.Source, module.validatedSource) {
m = module.decodedModule
return
}

// Peek to see if this is a binary or text format
if bytes.Equal(module.Source[0:4], binary.Magic) {
m, err = binary.DecodeModule(module.Source)
Expand All @@ -106,6 +123,10 @@ func decodeModule(module *ModuleConfig) (m *internalwasm.Module, name string, er
return
}

// Cache as tools like wapc-go re-instantiate the same module many times.
module.validatedSource = module.Source
module.decodedModule = m

name = module.Name
if name == "" && m.NameSection != nil {
name = m.NameSection.ModuleName
Expand Down
36 changes: 34 additions & 2 deletions wasm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,43 @@ func TestDecodeModule(t *testing.T) {
tc := tt

t.Run(tc.name, func(t *testing.T) {
_, name, err := decodeModule(&ModuleConfig{Name: tc.moduleName, Source: tc.source})
config := &ModuleConfig{Name: tc.moduleName, Source: tc.source}
_, name, err := decodeModule(config)
require.NoError(t, err)
if tc.moduleName == "" {
require.Equal(t, "test" /* from the text format */, name)
} else {
require.Equal(t, tc.moduleName, name)
}

// Avoid adding another test just to check Validate works
require.NoError(t, config.Validate())
})
}

t.Run("caches repetitive decodes", func(t *testing.T) {
config := &ModuleConfig{Source: wat}
m, _, err := decodeModule(config)
require.NoError(t, err)

again, _, err := decodeModule(config)
require.NoError(t, err)

require.Same(t, m, again)
})

t.Run("changing source invalidates decode cache", func(t *testing.T) {
config := &ModuleConfig{Source: wat}
m, _, err := decodeModule(config)
require.NoError(t, err)

config.Source = wasm
again, _, err := decodeModule(config)
require.NoError(t, err)

require.Equal(t, m, again)
require.NotSame(t, m, again)
})
}

func TestDecodeModule_Errors(t *testing.T) {
Expand Down Expand Up @@ -71,8 +99,12 @@ func TestDecodeModule_Errors(t *testing.T) {
tc := tt

t.Run(tc.name, func(t *testing.T) {
_, _, err := decodeModule(&ModuleConfig{Source: tc.source})
config := &ModuleConfig{Source: tc.source}
_, _, err := decodeModule(config)
require.EqualError(t, err, tc.expectedErr)

// Avoid adding another test just to check Validate works
require.EqualError(t, config.Validate(), tc.expectedErr)
})
}
}
Expand Down

0 comments on commit fc26fdd

Please sign in to comment.