diff --git a/README.md b/README.md index 808388a1..bdfbf0fd 100644 --- a/README.md +++ b/README.md @@ -203,8 +203,13 @@ as well as how to classify a request for a feature we don't yet support. ### WebAssembly Core wazero conforms with spectests [7] defined alongside WebAssembly Core -Specification [1.0][1]. There is also [work in progress][14] towards release -[2.0][2], despite it not being a Web Standard, yet. +Specification [1.0][1]. This is the default [RuntimeConfig][18]. + +The WebAssembly Core Specification [2.0][2] is in draft form and wazero has +[work in progress][14] towards that. Opt in via the below configuration: +```go +rConfig = wazero.NewRuntimeConfig().WithWasmCore2() +``` One current limitation of wazero is that it doesn't fully implement the Text Format, yet, e.g. compiling `.wat` files. The intent is to [finish this][15], diff --git a/builder_test.go b/builder_test.go index f7a7dfd6..bb250f44 100644 --- a/builder_test.go +++ b/builder_test.go @@ -362,12 +362,12 @@ func TestNewModuleBuilder_Build(t *testing.T) { func TestNewModuleBuilder_Build_Errors(t *testing.T) { tests := []struct { name string - input func(*RuntimeConfig) ModuleBuilder + input func(RuntimeConfig) ModuleBuilder expectedErr string }{ { name: "memory min > limit", // only one test to avoid duplicating tests in module_test.go - input: func(cfg *RuntimeConfig) ModuleBuilder { + input: func(cfg RuntimeConfig) ModuleBuilder { return NewRuntimeWithConfig(cfg).NewModuleBuilder(""). ExportMemory("memory", math.MaxUint32) }, @@ -375,7 +375,7 @@ func TestNewModuleBuilder_Build_Errors(t *testing.T) { }, { name: "memory cap < min", // only one test to avoid duplicating tests in module_test.go - input: func(cfg *RuntimeConfig) ModuleBuilder { + input: func(cfg RuntimeConfig) ModuleBuilder { cfg = cfg.WithMemoryCapacityPages(func(minPages uint32, maxPages *uint32) uint32 { return 1 }) diff --git a/config.go b/config.go index 2a75add9..f7aa199e 100644 --- a/config.go +++ b/config.go @@ -16,8 +16,122 @@ import ( // RuntimeConfig controls runtime behavior, with the default implementation as NewRuntimeConfig // +// Ex. To explicitly limit to Wasm Core 1.0 features as opposed to relying on defaults: +// rConfig = wazero.NewRuntimeConfig().WithWasmCore1() +// // Note: RuntimeConfig is immutable. Each WithXXX function returns a new instance including the corresponding change. -type RuntimeConfig struct { +type RuntimeConfig interface { + + // WithFeatureBulkMemoryOperations adds instructions modify ranges of memory or table entries + // ("bulk-memory-operations"). This defaults to false as the feature was not finished in WebAssembly 1.0 (20191205). + // + // Here are the notable effects: + // * Adds `memory.fill`, `memory.init`, `memory.copy` and `data.drop` instructions. + // * Adds `table.fill`, `table.init`, `table.copy` and `elem.drop` instructions. + // * Introduces a "passive" form of element and data segments. + // * Stops checking "active" element and data segment boundaries at compile-time, meaning they can error at runtime. + // + // Note: "bulk-memory-operations" is mixed with the "reference-types" proposal + // due to the WebAssembly Working Group merging them "mutually dependent". + // See https://github.com/WebAssembly/spec/blob/main/proposals/bulk-memory-operations/Overview.md + // See https://github.com/WebAssembly/spec/blob/main/proposals/reference-types/Overview.md + // See https://github.com/WebAssembly/spec/pull/1287 + WithFeatureBulkMemoryOperations(enabled bool) RuntimeConfig + + // WithFeatureMultiValue enables multiple values ("multi-value"). This defaults to false as the feature was not finished + // in WebAssembly 1.0 (20191205). + // + // Here are the notable effects: + // * Function (`func`) types allow more than one result + // * Block types (`block`, `loop` and `if`) can be arbitrary function types + // + // See https://github.com/WebAssembly/spec/blob/main/proposals/multi-value/Overview.md + WithFeatureMultiValue(enabled bool) RuntimeConfig + + // WithFeatureMutableGlobal allows globals to be mutable. This defaults to true as the feature was finished in + // WebAssembly 1.0 (20191205). + // + // When false, an api.Global can never be cast to an api.MutableGlobal, and any source that includes global vars + // will fail to parse. + WithFeatureMutableGlobal(enabled bool) RuntimeConfig + + // WithFeatureNonTrappingFloatToIntConversion enables non-trapping float-to-int conversions. + // ("nontrapping-float-to-int-conversion"). This defaults to false as the feature was not in WebAssembly 1.0 (20191205). + // + // The only effect of enabling this is allowing the following instructions, which return 0 on NaN instead of panicking. + // * `i32.trunc_sat_f32_s` + // * `i32.trunc_sat_f32_u` + // * `i32.trunc_sat_f64_s` + // * `i32.trunc_sat_f64_u` + // * `i64.trunc_sat_f32_s` + // * `i64.trunc_sat_f32_u` + // * `i64.trunc_sat_f64_s` + // * `i64.trunc_sat_f64_u` + // + // See https://github.com/WebAssembly/spec/blob/main/proposals/nontrapping-float-to-int-conversion/Overview.md + WithFeatureNonTrappingFloatToIntConversion(enabled bool) RuntimeConfig + + // WithFeatureSignExtensionOps enables sign extension instructions ("sign-extension-ops"). This defaults to false as the + // feature was not in WebAssembly 1.0 (20191205). + // + // Here are the notable effects: + // * Adds instructions `i32.extend8_s`, `i32.extend16_s`, `i64.extend8_s`, `i64.extend16_s` and `i64.extend32_s` + // + // See https://github.com/WebAssembly/spec/blob/main/proposals/sign-extension-ops/Overview.md + WithFeatureSignExtensionOps(enabled bool) RuntimeConfig + + // WithMemoryCapacityPages is a function that determines memory capacity in pages (65536 bytes per page). The inputs are + // the min and possibly nil max defined by the module, and the default is to return the min. + // + // Ex. To set capacity to max when exists: + // c = c.WithMemoryCapacityPages(func(minPages uint32, maxPages *uint32) uint32 { + // if maxPages != nil { + // return *maxPages + // } + // return minPages + // }) + // + // This function is used at compile time (ModuleBuilder.Build or Runtime.CompileModule). Compile will err if the + // function returns a value lower than minPages or greater than WithMemoryLimitPages. + WithMemoryCapacityPages(maxCapacityPages func(minPages uint32, maxPages *uint32) uint32) RuntimeConfig + + // WithMemoryLimitPages limits the maximum number of pages a module can define from 65536 pages (4GiB) to a lower value. + // + // Notes: + // * If a module defines no memory max value, Runtime.CompileModule sets max to the limit. + // * If a module defines a memory max larger than this limit, it will fail to compile (Runtime.CompileModule). + // * Any "memory.grow" instruction that results in a larger value than this results in an error at runtime. + // * Zero is a valid value and results in a crash if any module uses memory. + // + // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem + // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-types%E2%91%A0 + WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig + + // WithWasmCore1 enables features included in the WebAssembly Core Specification 1.0 (20191205). Selecting this + // overwrites any currently accumulated features with only those included in this W3C recommendation. + // + // This is default because as of mid 2022, this is the only version that is a Web Standard (W3C Recommendation). + // + // You can select the latest draft of the WebAssembly Core Specification 2.0 instead via WithWasmCore2. You can + // also enable or disable individual features via `WithXXX` methods. Ex. + // rConfig = wazero.NewRuntimeConfig().WithWasmCore1().WithFeatureMutableGlobal(false) + // + // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/ + WithWasmCore1() RuntimeConfig + + // WithWasmCore2 enables features included in the WebAssembly Core Specification 2.0 (20220419). Selecting this + // overwrites any currently accumulated features with only those included in this W3C working draft. + // + // This is not default because it is not yet incomplete and also not yet a Web Standard (W3C Recommendation). + // + // Even after selecting this, you can enable or disable individual features via `WithXXX` methods. Ex. + // rConfig = wazero.NewRuntimeConfig().WithWasmCore2().WithFeatureMutableGlobal(false) + // + // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/ + WithWasmCore2() RuntimeConfig +} + +type runtimeConfig struct { enabledFeatures wasm.Features newEngine func(wasm.Features) wasm.Engine memoryLimitPages uint32 @@ -25,15 +139,15 @@ type RuntimeConfig struct { } // engineLessConfig helps avoid copy/pasting the wrong defaults. -var engineLessConfig = &RuntimeConfig{ +var engineLessConfig = &runtimeConfig{ enabledFeatures: wasm.Features20191205, memoryLimitPages: wasm.MemoryLimitPages, memoryCapacityPages: func(minPages uint32, maxPages *uint32) uint32 { return minPages }, } // clone ensures all fields are copied even if nil. -func (c *RuntimeConfig) clone() *RuntimeConfig { - return &RuntimeConfig{ +func (c *runtimeConfig) clone() *runtimeConfig { + return &runtimeConfig{ enabledFeatures: c.enabledFeatures, newEngine: c.newEngine, memoryLimitPages: c.memoryLimitPages, @@ -45,49 +159,56 @@ func (c *RuntimeConfig) clone() *RuntimeConfig { // // Note: This panics at runtime the runtime.GOOS or runtime.GOARCH does not support JIT. Use NewRuntimeConfig to safely // detect and fallback to NewRuntimeConfigInterpreter if needed. -func NewRuntimeConfigJIT() *RuntimeConfig { +func NewRuntimeConfigJIT() RuntimeConfig { ret := engineLessConfig.clone() ret.newEngine = jit.NewEngine return ret } // NewRuntimeConfigInterpreter interprets WebAssembly modules instead of compiling them into assembly. -func NewRuntimeConfigInterpreter() *RuntimeConfig { +func NewRuntimeConfigInterpreter() RuntimeConfig { ret := engineLessConfig.clone() ret.newEngine = interpreter.NewEngine return ret } -// WithMemoryLimitPages limits the maximum number of pages a module can define from 65536 pages (4GiB) to a lower value. -// -// Notes: -// * If a module defines no memory max value, Runtime.CompileModule sets max to the limit. -// * If a module defines a memory max larger than this limit, it will fail to compile (Runtime.CompileModule). -// * Any "memory.grow" instruction that results in a larger value than this results in an error at runtime. -// * Zero is a valid value and results in a crash if any module uses memory. -// -// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem -// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-types%E2%91%A0 -func (c *RuntimeConfig) WithMemoryLimitPages(memoryLimitPages uint32) *RuntimeConfig { +// WithFeatureBulkMemoryOperations implements RuntimeConfig.WithFeatureBulkMemoryOperations +func (c *runtimeConfig) WithFeatureBulkMemoryOperations(enabled bool) RuntimeConfig { ret := c.clone() - ret.memoryLimitPages = memoryLimitPages + ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureBulkMemoryOperations, enabled) return ret } -// WithMemoryCapacityPages is a function that determines memory capacity in pages (65536 bytes per page). The inputs are -// the min and possibly nil max defined by the module, and the default is to return the min. -// -// Ex. To set capacity to max when exists: -// c = c.WithMemoryCapacityPages(func(minPages uint32, maxPages *uint32) uint32 { -// if maxPages != nil { -// return *maxPages -// } -// return minPages -// }) -// -// This function is used at compile time (ModuleBuilder.Build or Runtime.CompileModule). Compile will err if the -// function returns a value lower than minPages or greater than WithMemoryLimitPages. -func (c *RuntimeConfig) WithMemoryCapacityPages(maxCapacityPages func(minPages uint32, maxPages *uint32) uint32) *RuntimeConfig { +// WithFeatureMultiValue implements RuntimeConfig.WithFeatureMultiValue +func (c *runtimeConfig) WithFeatureMultiValue(enabled bool) RuntimeConfig { + ret := c.clone() + ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMultiValue, enabled) + return ret +} + +// WithFeatureMutableGlobal implements RuntimeConfig.WithFeatureMutableGlobal +func (c *runtimeConfig) WithFeatureMutableGlobal(enabled bool) RuntimeConfig { + ret := c.clone() + ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMutableGlobal, enabled) + return ret +} + +// WithFeatureNonTrappingFloatToIntConversion implements RuntimeConfig.WithFeatureNonTrappingFloatToIntConversion +func (c *runtimeConfig) WithFeatureNonTrappingFloatToIntConversion(enabled bool) RuntimeConfig { + ret := c.clone() + ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureNonTrappingFloatToIntConversion, enabled) + return ret +} + +// WithFeatureSignExtensionOps implements RuntimeConfig.WithFeatureSignExtensionOps +func (c *runtimeConfig) WithFeatureSignExtensionOps(enabled bool) RuntimeConfig { + ret := c.clone() + ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureSignExtensionOps, enabled) + return ret +} + +// WithMemoryCapacityPages implements RuntimeConfig.WithMemoryCapacityPages +func (c *runtimeConfig) WithMemoryCapacityPages(maxCapacityPages func(minPages uint32, maxPages *uint32) uint32) RuntimeConfig { if maxCapacityPages == nil { return c // Instead of erring. } @@ -96,94 +217,24 @@ func (c *RuntimeConfig) WithMemoryCapacityPages(maxCapacityPages func(minPages u return ret } -// WithFinishedFeatures enables currently supported "finished" feature proposals. Use this to improve compatibility with -// tools that enable all features by default. -// -// Note: The features implied can vary and can lead to unpredictable behavior during updates. -// Note: This only includes "finished" features, but "finished" is not an official W3C term: it is possible that -// "finished" features do not make the next W3C recommended WebAssembly core specification. -// See https://github.com/WebAssembly/spec/tree/main/proposals -func (c *RuntimeConfig) WithFinishedFeatures() *RuntimeConfig { +// WithMemoryLimitPages implements RuntimeConfig.WithMemoryLimitPages +func (c *runtimeConfig) WithMemoryLimitPages(memoryLimitPages uint32) RuntimeConfig { ret := c.clone() - ret.enabledFeatures = wasm.FeaturesFinished + ret.memoryLimitPages = memoryLimitPages return ret } -// WithFeatureBulkMemoryOperations adds instructions modify ranges of memory or table entries -// ("bulk-memory-operations"). This defaults to false as the feature was not finished in WebAssembly 1.0 (20191205). -// -// Here are the notable effects: -// * Adds `memory.fill`, `memory.init`, `memory.copy` and `data.drop` instructions. -// * Adds `table.fill`, `table.init`, `table.copy` and `elem.drop` instructions. -// * Introduces a "passive" form of element and data segments. -// * Stops checking "active" element and data segment boundaries at compile-time, meaning they can error at runtime. -// -// Note: "bulk-memory-operations" is mixed with the "reference-types" proposal -// due to the WebAssembly Working Group merging them "mutually dependent". -// See https://github.com/WebAssembly/spec/blob/main/proposals/bulk-memory-operations/Overview.md -// See https://github.com/WebAssembly/spec/blob/main/proposals/reference-types/Overview.md -// See https://github.com/WebAssembly/spec/pull/1287 -func (c *RuntimeConfig) WithFeatureBulkMemoryOperations(enabled bool) *RuntimeConfig { +// WithWasmCore1 implements RuntimeConfig.WithWasmCore1 +func (c *runtimeConfig) WithWasmCore1() RuntimeConfig { ret := c.clone() - ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureBulkMemoryOperations, enabled) + ret.enabledFeatures = wasm.Features20191205 return ret } -// WithFeatureMultiValue enables multiple values ("multi-value"). This defaults to false as the feature was not finished -// in WebAssembly 1.0 (20191205). -// -// Here are the notable effects: -// * Function (`func`) types allow more than one result -// * Block types (`block`, `loop` and `if`) can be arbitrary function types -// -// See https://github.com/WebAssembly/spec/blob/main/proposals/multi-value/Overview.md -func (c *RuntimeConfig) WithFeatureMultiValue(enabled bool) *RuntimeConfig { +// WithWasmCore2 implements RuntimeConfig.WithWasmCore2 +func (c *runtimeConfig) WithWasmCore2() RuntimeConfig { ret := c.clone() - ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMultiValue, enabled) - return ret -} - -// WithFeatureMutableGlobal allows globals to be mutable. This defaults to true as the feature was finished in -// WebAssembly 1.0 (20191205). -// -// When false, an api.Global can never be cast to an api.MutableGlobal, and any source that includes global vars -// will fail to parse. -func (c *RuntimeConfig) WithFeatureMutableGlobal(enabled bool) *RuntimeConfig { - ret := c.clone() - ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureMutableGlobal, enabled) - return ret -} - -// WithFeatureNonTrappingFloatToIntConversion enables non-trapping float-to-int conversions. -// ("nontrapping-float-to-int-conversion"). This defaults to false as the feature was not in WebAssembly 1.0 (20191205). -// -// The only effect of enabling this is allowing the following instructions, which return 0 on NaN instead of panicking. -// * `i32.trunc_sat_f32_s` -// * `i32.trunc_sat_f32_u` -// * `i32.trunc_sat_f64_s` -// * `i32.trunc_sat_f64_u` -// * `i64.trunc_sat_f32_s` -// * `i64.trunc_sat_f32_u` -// * `i64.trunc_sat_f64_s` -// * `i64.trunc_sat_f64_u` -// -// See https://github.com/WebAssembly/spec/blob/main/proposals/nontrapping-float-to-int-conversion/Overview.md -func (c *RuntimeConfig) WithFeatureNonTrappingFloatToIntConversion(enabled bool) *RuntimeConfig { - ret := c.clone() - ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureNonTrappingFloatToIntConversion, enabled) - return ret -} - -// WithFeatureSignExtensionOps enables sign extension instructions ("sign-extension-ops"). This defaults to false as the -// feature was not in WebAssembly 1.0 (20191205). -// -// Here are the notable effects: -// * Adds instructions `i32.extend8_s`, `i32.extend16_s`, `i64.extend8_s`, `i64.extend16_s` and `i64.extend32_s` -// -// See https://github.com/WebAssembly/spec/blob/main/proposals/sign-extension-ops/Overview.md -func (c *RuntimeConfig) WithFeatureSignExtensionOps(enabled bool) *RuntimeConfig { - ret := c.clone() - ret.enabledFeatures = ret.enabledFeatures.Set(wasm.FeatureSignExtensionOps, enabled) + ret.enabledFeatures = wasm.Features20220419 return ret } diff --git a/config_supported.go b/config_supported.go index 13ae5d2b..9ee8a141 100644 --- a/config_supported.go +++ b/config_supported.go @@ -5,6 +5,6 @@ package wazero const JITSupported = true // NewRuntimeConfig returns NewRuntimeConfigJIT -func NewRuntimeConfig() *RuntimeConfig { +func NewRuntimeConfig() RuntimeConfig { return NewRuntimeConfigJIT() } diff --git a/config_test.go b/config_test.go index 0c871a13..dcc77cf5 100644 --- a/config_test.go +++ b/config_test.go @@ -14,90 +14,108 @@ import ( func TestRuntimeConfig(t *testing.T) { tests := []struct { name string - with func(*RuntimeConfig) *RuntimeConfig - expected *RuntimeConfig + with func(RuntimeConfig) RuntimeConfig + expected RuntimeConfig }{ { name: "WithMemoryLimitPages", - with: func(c *RuntimeConfig) *RuntimeConfig { + with: func(c RuntimeConfig) RuntimeConfig { return c.WithMemoryLimitPages(1) }, - expected: &RuntimeConfig{ + expected: &runtimeConfig{ memoryLimitPages: 1, }, }, { name: "bulk-memory-operations", - with: func(c *RuntimeConfig) *RuntimeConfig { + with: func(c RuntimeConfig) RuntimeConfig { return c.WithFeatureBulkMemoryOperations(true) }, - expected: &RuntimeConfig{ + expected: &runtimeConfig{ enabledFeatures: wasm.FeatureBulkMemoryOperations, }, }, { name: "multi-value", - with: func(c *RuntimeConfig) *RuntimeConfig { + with: func(c RuntimeConfig) RuntimeConfig { return c.WithFeatureMultiValue(true) }, - expected: &RuntimeConfig{ + expected: &runtimeConfig{ enabledFeatures: wasm.FeatureMultiValue, }, }, { name: "mutable-global", - with: func(c *RuntimeConfig) *RuntimeConfig { + with: func(c RuntimeConfig) RuntimeConfig { return c.WithFeatureMutableGlobal(true) }, - expected: &RuntimeConfig{ + expected: &runtimeConfig{ enabledFeatures: wasm.FeatureMutableGlobal, }, }, { name: "nontrapping-float-to-int-conversion", - with: func(c *RuntimeConfig) *RuntimeConfig { + with: func(c RuntimeConfig) RuntimeConfig { return c.WithFeatureNonTrappingFloatToIntConversion(true) }, - expected: &RuntimeConfig{ + expected: &runtimeConfig{ enabledFeatures: wasm.FeatureNonTrappingFloatToIntConversion, }, }, { name: "sign-extension-ops", - with: func(c *RuntimeConfig) *RuntimeConfig { + with: func(c RuntimeConfig) RuntimeConfig { return c.WithFeatureSignExtensionOps(true) }, - expected: &RuntimeConfig{ + expected: &runtimeConfig{ enabledFeatures: wasm.FeatureSignExtensionOps, }, }, + { + name: "REC-wasm-core-1-20191205", + with: func(c RuntimeConfig) RuntimeConfig { + return c.WithFeatureSignExtensionOps(true).WithWasmCore1() + }, + expected: &runtimeConfig{ + enabledFeatures: wasm.Features20191205, + }, + }, + { + name: "WD-wasm-core-2-20220419", + with: func(c RuntimeConfig) RuntimeConfig { + return c.WithFeatureMutableGlobal(false).WithWasmCore2() + }, + expected: &runtimeConfig{ + enabledFeatures: wasm.Features20220419, + }, + }, } for _, tt := range tests { tc := tt t.Run(tc.name, func(t *testing.T) { - input := &RuntimeConfig{} + input := &runtimeConfig{} rc := tc.with(input) require.Equal(t, tc.expected, rc) // The source wasn't modified - require.Equal(t, &RuntimeConfig{}, input) + require.Equal(t, &runtimeConfig{}, input) }) } t.Run("WithMemoryCapacityPages", func(t *testing.T) { - c := NewRuntimeConfig() + c := NewRuntimeConfig().(*runtimeConfig) // Test default returns min require.Equal(t, uint32(1), c.memoryCapacityPages(1, nil)) // Nil ignored - c = c.WithMemoryCapacityPages(nil) + c = c.WithMemoryCapacityPages(nil).(*runtimeConfig) require.Equal(t, uint32(1), c.memoryCapacityPages(1, nil)) // Assign a valid function c = c.WithMemoryCapacityPages(func(minPages uint32, maxPages *uint32) uint32 { return 2 - }) + }).(*runtimeConfig) // Returns updated value require.Equal(t, uint32(2), c.memoryCapacityPages(1, nil)) }) @@ -108,13 +126,13 @@ func TestRuntimeConfig_FeatureToggle(t *testing.T) { name string feature wasm.Features expectDefault bool - setFeature func(*RuntimeConfig, bool) *RuntimeConfig + setFeature func(RuntimeConfig, bool) RuntimeConfig }{ { name: "bulk-memory-operations", feature: wasm.FeatureBulkMemoryOperations, expectDefault: false, - setFeature: func(c *RuntimeConfig, v bool) *RuntimeConfig { + setFeature: func(c RuntimeConfig, v bool) RuntimeConfig { return c.WithFeatureBulkMemoryOperations(v) }, }, @@ -122,7 +140,7 @@ func TestRuntimeConfig_FeatureToggle(t *testing.T) { name: "multi-value", feature: wasm.FeatureMultiValue, expectDefault: false, - setFeature: func(c *RuntimeConfig, v bool) *RuntimeConfig { + setFeature: func(c RuntimeConfig, v bool) RuntimeConfig { return c.WithFeatureMultiValue(v) }, }, @@ -130,7 +148,7 @@ func TestRuntimeConfig_FeatureToggle(t *testing.T) { name: "mutable-global", feature: wasm.FeatureMutableGlobal, expectDefault: true, - setFeature: func(c *RuntimeConfig, v bool) *RuntimeConfig { + setFeature: func(c RuntimeConfig, v bool) RuntimeConfig { return c.WithFeatureMutableGlobal(v) }, }, @@ -138,7 +156,7 @@ func TestRuntimeConfig_FeatureToggle(t *testing.T) { name: "nontrapping-float-to-int-conversion", feature: wasm.FeatureNonTrappingFloatToIntConversion, expectDefault: false, - setFeature: func(c *RuntimeConfig, v bool) *RuntimeConfig { + setFeature: func(c RuntimeConfig, v bool) RuntimeConfig { return c.WithFeatureNonTrappingFloatToIntConversion(v) }, }, @@ -146,7 +164,7 @@ func TestRuntimeConfig_FeatureToggle(t *testing.T) { name: "sign-extension-ops", feature: wasm.FeatureSignExtensionOps, expectDefault: false, - setFeature: func(c *RuntimeConfig, v bool) *RuntimeConfig { + setFeature: func(c RuntimeConfig, v bool) RuntimeConfig { return c.WithFeatureSignExtensionOps(v) }, }, @@ -156,19 +174,19 @@ func TestRuntimeConfig_FeatureToggle(t *testing.T) { tc := tt t.Run(tc.name, func(t *testing.T) { - c := NewRuntimeConfig() + c := NewRuntimeConfig().(*runtimeConfig) require.Equal(t, tc.expectDefault, c.enabledFeatures.Get(tc.feature)) // Set to false even if it was initially false. - c = tc.setFeature(c, false) + c = tc.setFeature(c, false).(*runtimeConfig) require.False(t, c.enabledFeatures.Get(tc.feature)) // Set true makes it true - c = tc.setFeature(c, true) + c = tc.setFeature(c, true).(*runtimeConfig) require.True(t, c.enabledFeatures.Get(tc.feature)) // Set false makes it false again - c = tc.setFeature(c, false) + c = tc.setFeature(c, false).(*runtimeConfig) require.False(t, c.enabledFeatures.Get(tc.feature)) }) } diff --git a/config_unsupported.go b/config_unsupported.go index c247b7d2..3a1ea520 100644 --- a/config_unsupported.go +++ b/config_unsupported.go @@ -5,6 +5,6 @@ package wazero const JITSupported = false // NewRuntimeConfig returns NewRuntimeConfigInterpreter -func NewRuntimeConfig() *RuntimeConfig { +func NewRuntimeConfig() RuntimeConfig { return NewRuntimeConfigInterpreter() } diff --git a/examples/multiple-results/multiple-results.go b/examples/multiple-results/multiple-results.go index 30302c33..c9d583cd 100644 --- a/examples/multiple-results/multiple-results.go +++ b/examples/multiple-results/multiple-results.go @@ -48,7 +48,7 @@ func main() { // wazero enables only W3C recommended features by default. Opt-in to other features like so: runtimeWithMultiValue := wazero.NewRuntimeWithConfig( wazero.NewRuntimeConfig().WithFeatureMultiValue(true), - // ^^ Note: You can enable all features via WithFinishedFeatures. + // ^^ Note: WebAssembly 2.0 (WithWasmCore2) includes "multi-value". ) // Add a module that uses multiple results values, with functions defined in WebAssembly. diff --git a/internal/integration_test/bench/bench_test.go b/internal/integration_test/bench/bench_test.go index 218e03ae..b381f3db 100644 --- a/internal/integration_test/bench/bench_test.go +++ b/internal/integration_test/bench/bench_test.go @@ -153,8 +153,8 @@ func runRandomMatMul(b *testing.B, m api.Module) { } } -func instantiateHostFunctionModuleWithEngine(b *testing.B, engine *wazero.RuntimeConfig) api.Module { - r := createRuntime(b, engine) +func instantiateHostFunctionModuleWithEngine(b *testing.B, config wazero.RuntimeConfig) api.Module { + r := createRuntime(b, config) // InstantiateModuleFromCode runs the "_start" function which is what TinyGo compiles "main" to. m, err := r.InstantiateModuleFromCode(testCtx, caseWasm) @@ -164,7 +164,7 @@ func instantiateHostFunctionModuleWithEngine(b *testing.B, engine *wazero.Runtim return m } -func createRuntime(b *testing.B, engine *wazero.RuntimeConfig) wazero.Runtime { +func createRuntime(b *testing.B, config wazero.RuntimeConfig) wazero.Runtime { getRandomString := func(ctx context.Context, m api.Module, retBufPtr uint32, retBufSize uint32) { results, err := m.ExportedFunction("allocate_buffer").Call(ctx, 10) if err != nil { @@ -179,7 +179,7 @@ func createRuntime(b *testing.B, engine *wazero.RuntimeConfig) wazero.Runtime { m.Memory().Write(ctx, offset, b) } - r := wazero.NewRuntimeWithConfig(engine) + r := wazero.NewRuntimeWithConfig(config) _, err := r.NewModuleBuilder("env"). ExportFunction("get_random_string", getRandomString). diff --git a/internal/integration_test/engine/adhoc_test.go b/internal/integration_test/engine/adhoc_test.go index 76310b2d..b7e5f16c 100644 --- a/internal/integration_test/engine/adhoc_test.go +++ b/internal/integration_test/engine/adhoc_test.go @@ -42,7 +42,7 @@ func TestEngineInterpreter(t *testing.T) { runAllTests(t, tests, wazero.NewRuntimeConfigInterpreter()) } -func runAllTests(t *testing.T, tests map[string]func(t *testing.T, r wazero.Runtime), config *wazero.RuntimeConfig) { +func runAllTests(t *testing.T, tests map[string]func(t *testing.T, r wazero.Runtime), config wazero.RuntimeConfig) { for name, testf := range tests { name := name // pin testf := testf // pin diff --git a/internal/integration_test/post1_0/bulk-memory-operations/spec_test.go b/internal/integration_test/post1_0/bulk-memory-operations/spec_test.go index 0619f891..23c6d2a2 100644 --- a/internal/integration_test/post1_0/bulk-memory-operations/spec_test.go +++ b/internal/integration_test/post1_0/bulk-memory-operations/spec_test.go @@ -45,7 +45,7 @@ var ( elemDropWasm []byte ) -func requireErrorOnBulkMemoryFeatureDisabled(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig, bin []byte) { +func requireErrorOnBulkMemoryFeatureDisabled(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig, bin []byte) { t.Run("disabled", func(t *testing.T) { // bulk-memory-operations is disabled by default. r := wazero.NewRuntimeWithConfig(newRuntimeConfig()) @@ -54,7 +54,7 @@ func requireErrorOnBulkMemoryFeatureDisabled(t *testing.T, newRuntimeConfig func }) } -func testTableCopy(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func testTableCopy(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig) { t.Run("table.copy", func(t *testing.T) { requireErrorOnBulkMemoryFeatureDisabled(t, newRuntimeConfig, tableCopyWasm) @@ -123,7 +123,7 @@ func testTableCopy(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) }) } -func testTableInit(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func testTableInit(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig) { t.Run("table.init", func(t *testing.T) { requireErrorOnBulkMemoryFeatureDisabled(t, newRuntimeConfig, tableInitWasm) @@ -166,7 +166,7 @@ func testTableInit(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) }) } -func testElemDrop(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func testElemDrop(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig) { t.Run("elem.drop", func(t *testing.T) { requireErrorOnBulkMemoryFeatureDisabled(t, newRuntimeConfig, elemDropWasm) @@ -194,7 +194,7 @@ func testElemDrop(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { }) } -func testBulkMemoryOperations(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func testBulkMemoryOperations(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig) { requireErrorOnBulkMemoryFeatureDisabled(t, newRuntimeConfig, bulkMemoryOperationsWasm) t.Run("enabled", func(t *testing.T) { diff --git a/internal/integration_test/post1_0/multi-value/spec_test.go b/internal/integration_test/post1_0/multi-value/spec_test.go index 795400c2..b74043f3 100644 --- a/internal/integration_test/post1_0/multi-value/spec_test.go +++ b/internal/integration_test/post1_0/multi-value/spec_test.go @@ -28,7 +28,7 @@ func TestMultiValue_Interpreter(t *testing.T) { //go:embed testdata/multi_value.wasm var multiValueWasm []byte -func testMultiValue(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func testMultiValue(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig) { t.Run("disabled", func(t *testing.T) { // multi-value is disabled by default. r := wazero.NewRuntimeWithConfig(newRuntimeConfig()) diff --git a/internal/integration_test/post1_0/nontrapping-float-to-int-conversion/spec_test.go b/internal/integration_test/post1_0/nontrapping-float-to-int-conversion/spec_test.go index 44a43d56..0b318ecd 100644 --- a/internal/integration_test/post1_0/nontrapping-float-to-int-conversion/spec_test.go +++ b/internal/integration_test/post1_0/nontrapping-float-to-int-conversion/spec_test.go @@ -56,7 +56,7 @@ var nonTrappingFloatToIntConversion = []byte(`(module $conversions.wast ) `) -func testNonTrappingFloatToIntConversion(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func testNonTrappingFloatToIntConversion(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig) { t.Run("disabled", func(t *testing.T) { // Non-trapping Float-to-int Conversions are disabled by default. r := wazero.NewRuntimeWithConfig(newRuntimeConfig()) diff --git a/internal/integration_test/post1_0/sign-extension-ops/spec_test.go b/internal/integration_test/post1_0/sign-extension-ops/spec_test.go index 598615a0..434ef3a2 100644 --- a/internal/integration_test/post1_0/sign-extension-ops/spec_test.go +++ b/internal/integration_test/post1_0/sign-extension-ops/spec_test.go @@ -45,7 +45,7 @@ var signExtend = []byte(`(module ) `) -func testSignExtensionOps(t *testing.T, newRuntimeConfig func() *wazero.RuntimeConfig) { +func testSignExtensionOps(t *testing.T, newRuntimeConfig func() wazero.RuntimeConfig) { t.Run("disabled", func(t *testing.T) { // Sign-extension is disabled by default. r := wazero.NewRuntimeWithConfig(newRuntimeConfig()) diff --git a/internal/integration_test/vs/codec.go b/internal/integration_test/vs/codec.go index 12e79140..629ce324 100644 --- a/internal/integration_test/vs/codec.go +++ b/internal/integration_test/vs/codec.go @@ -92,7 +92,7 @@ func newExample() *wasm.Module { func BenchmarkWat2Wasm(b *testing.B, vsName string, vsWat2Wasm func([]byte) error) { b.Run("wazero", func(b *testing.B) { for i := 0; i < b.N; i++ { - if m, err := text.DecodeModule(exampleText, wasm.FeaturesFinished, wasm.MemoryLimitPages); err != nil { + if m, err := text.DecodeModule(exampleText, wasm.Features20220419, wasm.MemoryLimitPages); err != nil { b.Fatal(err) } else { _ = binary.EncodeModule(m) diff --git a/internal/integration_test/vs/codec_test.go b/internal/integration_test/vs/codec_test.go index 585edabb..d77c4489 100644 --- a/internal/integration_test/vs/codec_test.go +++ b/internal/integration_test/vs/codec_test.go @@ -14,19 +14,19 @@ import ( func TestExampleUpToDate(t *testing.T) { t.Run("binary.DecodeModule", func(t *testing.T) { - m, err := binary.DecodeModule(exampleBinary, wasm.FeaturesFinished, wasm.MemoryLimitPages) + m, err := binary.DecodeModule(exampleBinary, wasm.Features20220419, wasm.MemoryLimitPages) require.NoError(t, err) require.Equal(t, example, m) }) t.Run("text.DecodeModule", func(t *testing.T) { - m, err := text.DecodeModule(exampleText, wasm.FeaturesFinished, wasm.MemoryLimitPages) + m, err := text.DecodeModule(exampleText, wasm.Features20220419, wasm.MemoryLimitPages) require.NoError(t, err) require.Equal(t, example, m) }) t.Run("Executable", func(t *testing.T) { - r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().WithFinishedFeatures()) + r := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().WithWasmCore2()) // Add WASI to satisfy import tests wm, err := wasi.InstantiateSnapshotPreview1(testCtx, r) @@ -49,7 +49,7 @@ func BenchmarkCodec(b *testing.B) { b.Run("binary.DecodeModule", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - if _, err := binary.DecodeModule(exampleBinary, wasm.FeaturesFinished, wasm.MemoryLimitPages); err != nil { + if _, err := binary.DecodeModule(exampleBinary, wasm.Features20220419, wasm.MemoryLimitPages); err != nil { b.Fatal(err) } } @@ -63,7 +63,7 @@ func BenchmarkCodec(b *testing.B) { b.Run("text.DecodeModule", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - if _, err := text.DecodeModule(exampleText, wasm.FeaturesFinished, wasm.MemoryLimitPages); err != nil { + if _, err := text.DecodeModule(exampleText, wasm.Features20220419, wasm.MemoryLimitPages); err != nil { b.Fatal(err) } } diff --git a/internal/integration_test/vs/runtime.go b/internal/integration_test/vs/runtime.go index 197dca61..e70d3f91 100644 --- a/internal/integration_test/vs/runtime.go +++ b/internal/integration_test/vs/runtime.go @@ -38,20 +38,20 @@ type Module interface { } func NewWazeroInterpreterRuntime() Runtime { - return newWazeroRuntime("wazero-interpreter", wazero.NewRuntimeConfigInterpreter().WithFinishedFeatures()) + return newWazeroRuntime("wazero-interpreter", wazero.NewRuntimeConfigInterpreter().WithWasmCore2()) } func NewWazeroJITRuntime() Runtime { - return newWazeroRuntime(jitRuntime, wazero.NewRuntimeConfigJIT().WithFinishedFeatures()) + return newWazeroRuntime(jitRuntime, wazero.NewRuntimeConfigJIT().WithWasmCore2()) } -func newWazeroRuntime(name string, config *wazero.RuntimeConfig) *wazeroRuntime { +func newWazeroRuntime(name string, config wazero.RuntimeConfig) *wazeroRuntime { return &wazeroRuntime{name: name, config: config} } type wazeroRuntime struct { name string - config *wazero.RuntimeConfig + config wazero.RuntimeConfig runtime wazero.Runtime logFn func([]byte) error env, compiled *wazero.CompiledCode diff --git a/internal/modgen/modgen_test.go b/internal/modgen/modgen_test.go index e5801066..cbadafd9 100644 --- a/internal/modgen/modgen_test.go +++ b/internal/modgen/modgen_test.go @@ -28,7 +28,7 @@ const ( func TestModGen(t *testing.T) { tested := map[string]struct{}{} rand := rand.New(rand.NewSource(0)) // use deterministic seed source for easy debugging. - runtime := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().WithFinishedFeatures()) + runtime := wazero.NewRuntimeWithConfig(wazero.NewRuntimeConfig().WithWasmCore2()) for _, size := range []int{1, 2, 5, 10, 50, 100} { for i := 0; i < 100; i++ { seed := make([]byte, size) diff --git a/internal/wasm/binary/function_test.go b/internal/wasm/binary/function_test.go index ac59b9c6..7608ddb4 100644 --- a/internal/wasm/binary/function_test.go +++ b/internal/wasm/binary/function_test.go @@ -72,7 +72,7 @@ func TestFunctionType(t *testing.T) { }) t.Run(fmt.Sprintf("decode - %s", tc.name), func(t *testing.T) { - binary, err := decodeFunctionType(wasm.FeaturesFinished, bytes.NewReader(b)) + binary, err := decodeFunctionType(wasm.Features20220419, bytes.NewReader(b)) require.NoError(t, err) require.Equal(t, binary, tc.input) }) diff --git a/internal/wasm/features.go b/internal/wasm/features.go index 7c978490..0c6abe1f 100644 --- a/internal/wasm/features.go +++ b/internal/wasm/features.go @@ -12,14 +12,20 @@ type Features uint64 // Features20191205 include those finished in WebAssembly 1.0 (20191205). // -// See https://github.com/WebAssembly/proposals/blob/main/finished-proposals.md // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205 const Features20191205 = FeatureMutableGlobal -// FeaturesFinished include all supported finished features, regardless of W3C status. +// Features20220419 include those finished in WebAssembly 2.0 (20220419). // -// See https://github.com/WebAssembly/proposals/blob/main/finished-proposals.md -const FeaturesFinished = 0xffffffffffffffff +// TODO: not yet complete https://github.com/tetratelabs/wazero/issues/484 +// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#release-1-1 +const Features20220419 = Features20191205 | + FeatureBulkMemoryOperations | + FeatureMultiValue | + FeatureNonTrappingFloatToIntConversion | + // TODO: FeatureReferenceTypes | + FeatureSignExtensionOps + // TODO: FeatureSIMD const ( // FeatureBulkMemoryOperations decides if parsing should succeed on the following instructions: diff --git a/internal/wasm/features_test.go b/internal/wasm/features_test.go index 08e08c7a..6291ce4d 100644 --- a/internal/wasm/features_test.go +++ b/internal/wasm/features_test.go @@ -62,7 +62,7 @@ func TestFeatures_String(t *testing.T) { {name: "multi-value", feature: FeatureMultiValue, expected: "multi-value"}, {name: "features", feature: FeatureMutableGlobal | FeatureMultiValue, expected: "multi-value|mutable-global"}, {name: "undefined", feature: 1 << 63, expected: ""}, - {name: "all", feature: FeaturesFinished, expected: "bulk-memory-operations|multi-value|mutable-global|nontrapping-float-to-int-conversion|sign-extension-ops"}, + {name: "2.0", feature: Features20220419, expected: "bulk-memory-operations|multi-value|mutable-global|nontrapping-float-to-int-conversion|sign-extension-ops"}, } for _, tt := range tests { diff --git a/internal/wasm/gofunc_test.go b/internal/wasm/gofunc_test.go index fe629047..d06ee15c 100644 --- a/internal/wasm/gofunc_test.go +++ b/internal/wasm/gofunc_test.go @@ -247,7 +247,7 @@ func TestPopGoFuncParams(t *testing.T) { t.Run(tc.name, func(t *testing.T) { goFunc := reflect.ValueOf(tc.inputFunc) - fk, _, err := getFunctionType(&goFunc, FeaturesFinished) + fk, _, err := getFunctionType(&goFunc, Features20220419) require.NoError(t, err) vals := PopGoFuncParams(&FunctionInstance{Kind: fk, GoFunc: &goFunc}, (&stack{stackVals}).pop) @@ -387,7 +387,7 @@ func TestCallGoFunc(t *testing.T) { t.Run(tc.name, func(t *testing.T) { goFunc := reflect.ValueOf(tc.inputFunc) - fk, _, err := getFunctionType(&goFunc, FeaturesFinished) + fk, _, err := getFunctionType(&goFunc, Features20220419) require.NoError(t, err) results := CallGoFunc(testCtx, callCtx, &FunctionInstance{Kind: fk, GoFunc: &goFunc}, tc.inputParams) diff --git a/internal/wasm/text/decoder_test.go b/internal/wasm/text/decoder_test.go index 969ffb31..de411d0b 100644 --- a/internal/wasm/text/decoder_test.go +++ b/internal/wasm/text/decoder_test.go @@ -1564,7 +1564,7 @@ func TestDecodeModule(t *testing.T) { tc := tt t.Run(tc.name, func(t *testing.T) { - m, err := DecodeModule([]byte(tc.input), wasm.FeaturesFinished, wasm.MemoryLimitPages) + m, err := DecodeModule([]byte(tc.input), wasm.Features20220419, wasm.MemoryLimitPages) require.NoError(t, err) require.Equal(t, tc.expected, m) }) diff --git a/internal/wasm/text/func_parser_test.go b/internal/wasm/text/func_parser_test.go index 56a68e83..3e9adbec 100644 --- a/internal/wasm/text/func_parser_test.go +++ b/internal/wasm/text/func_parser_test.go @@ -272,7 +272,7 @@ func TestFuncParser(t *testing.T) { } module := &wasm.Module{} - fp := newFuncParser(wasm.FeaturesFinished, &typeUseParser{module: module}, newIndexNamespace(module.SectionElementCount), setFunc) + fp := newFuncParser(wasm.Features20220419, &typeUseParser{module: module}, newIndexNamespace(module.SectionElementCount), setFunc) require.NoError(t, parseFunc(fp, tc.source)) require.Equal(t, tc.expected, parsedCode) }) @@ -390,7 +390,7 @@ func TestFuncParser_Call_Resolved(t *testing.T) { return parseErr, nil } - fp := newFuncParser(wasm.FeaturesFinished, &typeUseParser{module: &wasm.Module{}}, funcNamespace, setFunc) + fp := newFuncParser(wasm.Features20220419, &typeUseParser{module: &wasm.Module{}}, funcNamespace, setFunc) require.NoError(t, parseFunc(fp, tc.source)) require.Equal(t, tc.expected, parsedCode) }) diff --git a/internal/wasm/text/type_parser_test.go b/internal/wasm/text/type_parser_test.go index 0bbfca26..49409351 100644 --- a/internal/wasm/text/type_parser_test.go +++ b/internal/wasm/text/type_parser_test.go @@ -189,7 +189,7 @@ func TestTypeParser(t *testing.T) { require.Equal(t, wasm.SectionIDType, sectionID) return 0 }) - parsed, tp, err := parseFunctionType(wasm.FeaturesFinished, typeNamespace, tc.input) + parsed, tp, err := parseFunctionType(wasm.Features20220419, typeNamespace, tc.input) require.NoError(t, err) require.Equal(t, tc.expected, parsed) require.Equal(t, uint32(1), tp.typeNamespace.count) @@ -291,13 +291,13 @@ func TestTypeParser_Errors(t *testing.T) { { name: "result second wrong", input: "(type (func (result i32) (result i33)))", - enabledFeatures: wasm.FeaturesFinished, + enabledFeatures: wasm.Features20220419, expectedErr: "unknown type: i33", }, { name: "result second redundant type wrong", input: "(type (func (result i32) (result i32 i33)))", - enabledFeatures: wasm.FeaturesFinished, + enabledFeatures: wasm.Features20220419, expectedErr: "unknown type: i33", }, { diff --git a/internal/wasm/text/typeuse_parser_test.go b/internal/wasm/text/typeuse_parser_test.go index d5eb4756..64ff20e5 100644 --- a/internal/wasm/text/typeuse_parser_test.go +++ b/internal/wasm/text/typeuse_parser_test.go @@ -160,7 +160,7 @@ func TestTypeUseParser_InlinesTypesWhenNotYetAdded(t *testing.T) { runTypeUseParserTests(t, tests, func(tc *typeUseParserTest) (*typeUseParser, func(t *testing.T)) { module := &wasm.Module{} - tp := newTypeUseParser(wasm.FeaturesFinished, module, newIndexNamespace(module.SectionElementCount)) + tp := newTypeUseParser(wasm.Features20220419, module, newIndexNamespace(module.SectionElementCount)) return tp, func(t *testing.T) { // We should have inlined the type, and it is the first type use, which means the inlined index is zero require.Zero(t, tp.inlinedTypeIndices[0].inlinedIdx) @@ -196,7 +196,7 @@ func TestTypeUseParser_UnresolvedType(t *testing.T) { } runTypeUseParserTests(t, tests, func(tc *typeUseParserTest) (*typeUseParser, func(t *testing.T)) { module := &wasm.Module{} - tp := newTypeUseParser(wasm.FeaturesFinished, module, newIndexNamespace(module.SectionElementCount)) + tp := newTypeUseParser(wasm.Features20220419, module, newIndexNamespace(module.SectionElementCount)) return tp, func(t *testing.T) { require.NotNil(t, tp.typeNamespace.unresolvedIndices) if tc.expectedInlinedType == nil { @@ -306,7 +306,7 @@ func TestTypeUseParser_ReuseExistingType(t *testing.T) { require.NoError(t, err) typeNamespace.count++ - tp := newTypeUseParser(wasm.FeaturesFinished, module, typeNamespace) + tp := newTypeUseParser(wasm.Features20220419, module, typeNamespace) return tp, func(t *testing.T) { require.Zero(t, len(tp.typeNamespace.unresolvedIndices)) require.Zero(t, len(tp.inlinedTypes)) @@ -340,7 +340,7 @@ func TestTypeUseParser_ReuseExistingInlinedType(t *testing.T) { } runTypeUseParserTests(t, tests, func(tc *typeUseParserTest) (*typeUseParser, func(t *testing.T)) { module := &wasm.Module{} - tp := newTypeUseParser(wasm.FeaturesFinished, module, newIndexNamespace(module.SectionElementCount)) + tp := newTypeUseParser(wasm.Features20220419, module, newIndexNamespace(module.SectionElementCount)) // inline a type that doesn't match the test require.NoError(t, parseTypeUse(tp, "((param i32 i64))", ignoreTypeUse)) // inline the test type @@ -386,7 +386,7 @@ func TestTypeUseParser_BeginResets(t *testing.T) { } runTypeUseParserTests(t, tests, func(tc *typeUseParserTest) (*typeUseParser, func(t *testing.T)) { module := &wasm.Module{} - tp := newTypeUseParser(wasm.FeaturesFinished, module, newIndexNamespace(module.SectionElementCount)) + tp := newTypeUseParser(wasm.Features20220419, module, newIndexNamespace(module.SectionElementCount)) // inline a type that uses all fields require.NoError(t, parseTypeUse(tp, "((type $i32i64_i32) (param $x i32) (param $y i64) (result i32))", ignoreTypeUse)) require.NoError(t, parseTypeUse(tp, tc.input, ignoreTypeUse)) @@ -510,13 +510,13 @@ func TestTypeUseParser_Errors(t *testing.T) { { name: "result second wrong", input: "((result i32) (result i33))", - enabledFeatures: wasm.FeaturesFinished, + enabledFeatures: wasm.Features20220419, expectedErr: "1:23: unknown type: i33", }, { name: "result second redundant type wrong", input: "((result i32) (result i32 i33))", - enabledFeatures: wasm.FeaturesFinished, + enabledFeatures: wasm.Features20220419, expectedErr: "1:27: unknown type: i33", }, { @@ -600,7 +600,7 @@ func TestTypeUseParser_FailsMatch(t *testing.T) { require.NoError(t, err) typeNamespace.count++ - tp := newTypeUseParser(wasm.FeaturesFinished, module, typeNamespace) + tp := newTypeUseParser(wasm.Features20220419, module, typeNamespace) tests := []struct{ name, source, expectedErr string }{ { name: "nullary index", diff --git a/internal/wazeroir/compiler_test.go b/internal/wazeroir/compiler_test.go index 61fc243e..948a5d3c 100644 --- a/internal/wazeroir/compiler_test.go +++ b/internal/wazeroir/compiler_test.go @@ -85,7 +85,7 @@ func TestCompile(t *testing.T) { t.Run(tc.name, func(t *testing.T) { enabledFeatures := tc.enabledFeatures if enabledFeatures == 0 { - enabledFeatures = wasm.FeaturesFinished + enabledFeatures = wasm.Features20220419 } res, err := CompileFunctions(ctx, enabledFeatures, tc.module) @@ -487,7 +487,7 @@ func TestCompile_MultiValue(t *testing.T) { t.Run(tc.name, func(t *testing.T) { enabledFeatures := tc.enabledFeatures if enabledFeatures == 0 { - enabledFeatures = wasm.FeaturesFinished + enabledFeatures = wasm.Features20220419 } res, err := CompileFunctions(ctx, enabledFeatures, tc.module) require.NoError(t, err) @@ -550,7 +550,7 @@ func TestCompile_SignExtensionOps(t *testing.T) { func requireCompilationResult(t *testing.T, enabledFeatures wasm.Features, expected *CompilationResult, module *wasm.Module) { if enabledFeatures == 0 { - enabledFeatures = wasm.FeaturesFinished + enabledFeatures = wasm.Features20220419 } res, err := CompileFunctions(ctx, enabledFeatures, module) require.NoError(t, err) @@ -558,7 +558,7 @@ func requireCompilationResult(t *testing.T, enabledFeatures wasm.Features, expec } func requireModuleText(t *testing.T, source string) *wasm.Module { - m, err := text.DecodeModule([]byte(source), wasm.FeaturesFinished, wasm.MemoryLimitPages) + m, err := text.DecodeModule([]byte(source), wasm.Features20220419, wasm.MemoryLimitPages) require.NoError(t, err) return m } diff --git a/wasm.go b/wasm.go index 96d0e644..7296d012 100644 --- a/wasm.go +++ b/wasm.go @@ -122,7 +122,11 @@ func NewRuntime() Runtime { } // NewRuntimeWithConfig returns a runtime with the given configuration. -func NewRuntimeWithConfig(config *RuntimeConfig) Runtime { +func NewRuntimeWithConfig(rConfig RuntimeConfig) Runtime { + config, ok := rConfig.(*runtimeConfig) + if !ok { + panic(fmt.Errorf("unsupported wazero.RuntimeConfig implementation: %#v", rConfig)) + } return &runtime{ store: wasm.NewStore(config.enabledFeatures, config.newEngine(config.enabledFeatures)), enabledFeatures: config.enabledFeatures, diff --git a/wasm_test.go b/wasm_test.go index 9ae7953a..b6a4c49b 100644 --- a/wasm_test.go +++ b/wasm_test.go @@ -18,6 +18,16 @@ import ( // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors. var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary") +func TestNewRuntimeWithConfig_PanicsOnWrongImpl(t *testing.T) { + // It is too burdensome to define an impl of RuntimeConfig in tests just to verify the error when it is wrong. + // Instead, we pass nil which is implicitly the wrong type, as that's less work! + err := require.CapturePanic(func() { + NewRuntimeWithConfig(nil) + }) + + require.EqualError(t, err, "unsupported wazero.RuntimeConfig implementation: ") +} + func TestRuntime_CompileModule(t *testing.T) { tests := []struct { name string