From a91140f7f733c8355e41b7269f773856cc630458 Mon Sep 17 00:00:00 2001 From: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com> Date: Mon, 2 May 2022 10:29:38 +0800 Subject: [PATCH] Changes RuntimeConfig to an interface and exposes WithWasmCore2 (#518) WebAssembly Core Working Draft 1 recently came out. Before that, we had a toe-hold feature bucked called FinishedFeatures. This replaces `RuntimeConfig.WithFinishedFeatures` with `RuntimeConfig.WithWasmCore2`. This also adds `WithWasmCore1` for those who want to lock into 1.0 features as opposed to relying on defaults. This also fixes some design debt where we hadn't finished migrating public types that require constructor functions (NewXxx) to interfaces. By using interfaces, we prevent people from accidentally initializing key configuration directly (via &Xxx), causing nil field refs. This also helps prevent confusion about how to use the type (ex pointer or not) as there's only one way (as an interface). See https://github.com/tetratelabs/wazero/issues/516 Signed-off-by: Adrian Cole --- README.md | 9 +- builder_test.go | 6 +- config.go | 273 +++++++++++------- config_supported.go | 2 +- config_test.go | 76 +++-- config_unsupported.go | 2 +- examples/multiple-results/multiple-results.go | 2 +- internal/integration_test/bench/bench_test.go | 8 +- .../integration_test/engine/adhoc_test.go | 2 +- .../bulk-memory-operations/spec_test.go | 10 +- .../post1_0/multi-value/spec_test.go | 2 +- .../spec_test.go | 2 +- .../post1_0/sign-extension-ops/spec_test.go | 2 +- internal/integration_test/vs/codec.go | 2 +- internal/integration_test/vs/codec_test.go | 10 +- internal/integration_test/vs/runtime.go | 8 +- internal/modgen/modgen_test.go | 2 +- internal/wasm/binary/function_test.go | 2 +- internal/wasm/features.go | 14 +- internal/wasm/features_test.go | 2 +- internal/wasm/gofunc_test.go | 4 +- internal/wasm/text/decoder_test.go | 2 +- internal/wasm/text/func_parser_test.go | 4 +- internal/wasm/text/type_parser_test.go | 6 +- internal/wasm/text/typeuse_parser_test.go | 16 +- internal/wazeroir/compiler_test.go | 8 +- wasm.go | 6 +- wasm_test.go | 10 + 28 files changed, 293 insertions(+), 199 deletions(-) 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