Top-levels CoreFeatures and defaults to 2.0 (#800)

While compilers should be conservative when targeting WebAssembly Core
features, runtimes should be lenient as otherwise people need to
constantly turn on all features. Currently, most examples have to turn
on 2.0 features because compilers such as AssemblyScript and TinyGo use
them by default. This matches the policy with the reality, and should
make first time use easier.

This top-levels an internal type as `api.CoreFeatures` and defaults to
2.0 as opposed to 1.0, our previous default. This is less cluttered than
the excess of `WithXXX` methods we had prior to implementing all
planned WebAssembly Core Specification 1.0 features.

Finally, this backfills rationale as flat config types were a distinct
decision even if feature set selection muddied the topic.

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-09-06 15:14:36 +08:00
committed by GitHub
parent 2f56371078
commit b01effc8a9
67 changed files with 793 additions and 1017 deletions

103
api/features_test.go Normal file
View File

@@ -0,0 +1,103 @@
package api
import (
"testing"
"github.com/tetratelabs/wazero/internal/testing/require"
)
// TestCoreFeatures_ZeroIsInvalid reminds maintainers that a bitset cannot use zero as a flag!
// This is why we start iota with 1.
func TestCoreFeatures_ZeroIsInvalid(t *testing.T) {
f := CoreFeatures(0)
f = f.SetEnabled(0, true)
require.False(t, f.IsEnabled(0))
}
// TestCoreFeatures tests the bitset works as expected
func TestCoreFeatures(t *testing.T) {
tests := []struct {
name string
feature CoreFeatures
}{
{
name: "one is the smallest flag",
feature: 1,
},
{
name: "63 is the largest feature flag", // because uint64
feature: 1 << 63,
},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
f := CoreFeatures(0)
// Defaults to false
require.False(t, f.IsEnabled(tc.feature))
// Set true makes it true
f = f.SetEnabled(tc.feature, true)
require.True(t, f.IsEnabled(tc.feature))
// Set false makes it false again
f = f.SetEnabled(tc.feature, false)
require.False(t, f.IsEnabled(tc.feature))
})
}
}
func TestCoreFeatures_String(t *testing.T) {
tests := []struct {
name string
feature CoreFeatures
expected string
}{
{name: "none", feature: 0, expected: ""},
{name: "mutable-global", feature: CoreFeatureMutableGlobal, expected: "mutable-global"},
{name: "sign-extension-ops", feature: CoreFeatureSignExtensionOps, expected: "sign-extension-ops"},
{name: "multi-value", feature: CoreFeatureMultiValue, expected: "multi-value"},
{name: "simd", feature: CoreFeatureSIMD, expected: "simd"},
{name: "features", feature: CoreFeatureMutableGlobal | CoreFeatureMultiValue, expected: "multi-value|mutable-global"},
{name: "undefined", feature: 1 << 63, expected: ""},
{name: "2.0", feature: CoreFeaturesV2,
expected: "bulk-memory-operations|multi-value|mutable-global|" +
"nontrapping-float-to-int-conversion|reference-types|sign-extension-ops|simd"},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.expected, tc.feature.String())
})
}
}
func TestCoreFeatures_Require(t *testing.T) {
tests := []struct {
name string
feature CoreFeatures
expectedErr string
}{
{name: "none", feature: 0, expectedErr: "feature \"mutable-global\" is disabled"},
{name: "mutable-global", feature: CoreFeatureMutableGlobal},
{name: "sign-extension-ops", feature: CoreFeatureSignExtensionOps, expectedErr: "feature \"mutable-global\" is disabled"},
{name: "multi-value", feature: CoreFeatureMultiValue, expectedErr: "feature \"mutable-global\" is disabled"},
{name: "undefined", feature: 1 << 63, expectedErr: "feature \"mutable-global\" is disabled"},
}
for _, tt := range tests {
tc := tt
t.Run(tc.name, func(t *testing.T) {
err := tc.feature.RequireEnabled(CoreFeatureMutableGlobal)
if tc.expectedErr != "" {
require.EqualError(t, err, tc.expectedErr)
} else {
require.NoError(t, err)
}
})
}
}