github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/wasm/global_test.go (about)

     1  package wasm
     2  
     3  import (
     4  	"context"
     5  	"math"
     6  	"testing"
     7  
     8  	"github.com/tetratelabs/wazero/api"
     9  	"github.com/tetratelabs/wazero/internal/internalapi"
    10  	"github.com/tetratelabs/wazero/internal/leb128"
    11  	"github.com/tetratelabs/wazero/internal/testing/require"
    12  	"github.com/tetratelabs/wazero/internal/u64"
    13  )
    14  
    15  func TestGlobalTypes(t *testing.T) {
    16  	tests := []struct {
    17  		name            string
    18  		global          api.Global
    19  		expectedType    api.ValueType
    20  		expectedVal     uint64
    21  		expectedString  string
    22  		expectedMutable bool
    23  	}{
    24  		{
    25  			name: "i32 - immutable",
    26  			global: constantGlobal{g: &GlobalInstance{
    27  				Type: GlobalType{ValType: ValueTypeI32},
    28  				Val:  1,
    29  			}},
    30  			expectedType:   ValueTypeI32,
    31  			expectedVal:    1,
    32  			expectedString: "global(1)",
    33  		},
    34  		{
    35  			name: "i32 - immutable - max",
    36  			global: constantGlobal{g: &GlobalInstance{
    37  				Type: GlobalType{ValType: ValueTypeI32},
    38  				Val:  math.MaxInt32,
    39  			}},
    40  			expectedType:   ValueTypeI32,
    41  			expectedVal:    math.MaxInt32,
    42  			expectedString: "global(2147483647)",
    43  		},
    44  		{
    45  			name: "i64 - immutable",
    46  			global: constantGlobal{g: &GlobalInstance{
    47  				Type: GlobalType{ValType: ValueTypeI64},
    48  				Val:  1,
    49  			}},
    50  			expectedType:   ValueTypeI64,
    51  			expectedVal:    1,
    52  			expectedString: "global(1)",
    53  		},
    54  		{
    55  			name: "i64 - immutable - max",
    56  			global: constantGlobal{g: &GlobalInstance{
    57  				Type: GlobalType{ValType: ValueTypeI64},
    58  				Val:  math.MaxInt64,
    59  			}},
    60  			expectedType:   ValueTypeI64,
    61  			expectedVal:    math.MaxInt64,
    62  			expectedString: "global(9223372036854775807)",
    63  		},
    64  		{
    65  			name: "f32 - immutable",
    66  			global: constantGlobal{g: &GlobalInstance{
    67  				Type: GlobalType{ValType: ValueTypeF32},
    68  				Val:  api.EncodeF32(1.0),
    69  			}},
    70  			expectedType:   ValueTypeF32,
    71  			expectedVal:    api.EncodeF32(1.0),
    72  			expectedString: "global(1.000000)",
    73  		},
    74  		{
    75  			name: "f32 - immutable - max",
    76  			global: constantGlobal{g: &GlobalInstance{
    77  				Type: GlobalType{ValType: ValueTypeF32},
    78  				Val:  api.EncodeF32(math.MaxFloat32),
    79  			}},
    80  			expectedType:   ValueTypeF32,
    81  			expectedVal:    api.EncodeF32(math.MaxFloat32),
    82  			expectedString: "global(340282346638528859811704183484516925440.000000)",
    83  		},
    84  		{
    85  			name: "f64 - immutable",
    86  			global: constantGlobal{g: &GlobalInstance{
    87  				Type: GlobalType{ValType: ValueTypeF64},
    88  				Val:  api.EncodeF64(1.0),
    89  			}},
    90  			expectedType:   ValueTypeF64,
    91  			expectedVal:    api.EncodeF64(1.0),
    92  			expectedString: "global(1.000000)",
    93  		},
    94  		{
    95  			name: "f64 - immutable - max",
    96  			global: constantGlobal{g: &GlobalInstance{
    97  				Type: GlobalType{ValType: ValueTypeF64},
    98  				Val:  api.EncodeF64(math.MaxFloat64),
    99  			}},
   100  			expectedType:   ValueTypeF64,
   101  			expectedVal:    api.EncodeF64(math.MaxFloat64),
   102  			expectedString: "global(179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000)",
   103  		},
   104  		{
   105  			name: "i32 - mutable",
   106  			global: mutableGlobal{internalapi.WazeroOnlyType{}, &GlobalInstance{
   107  				Type: GlobalType{ValType: ValueTypeI32, Mutable: true},
   108  				Val:  1,
   109  			}},
   110  			expectedType:    ValueTypeI32,
   111  			expectedVal:     1,
   112  			expectedString:  "global(1)",
   113  			expectedMutable: true,
   114  		},
   115  		{
   116  			name: "i64 - mutable",
   117  			global: mutableGlobal{internalapi.WazeroOnlyType{}, &GlobalInstance{
   118  				Type: GlobalType{ValType: ValueTypeI64, Mutable: true},
   119  				Val:  1,
   120  			}},
   121  			expectedType:    ValueTypeI64,
   122  			expectedVal:     1,
   123  			expectedString:  "global(1)",
   124  			expectedMutable: true,
   125  		},
   126  		{
   127  			name: "f32 - mutable",
   128  			global: mutableGlobal{internalapi.WazeroOnlyType{}, &GlobalInstance{
   129  				Type: GlobalType{ValType: ValueTypeF32, Mutable: true},
   130  				Val:  api.EncodeF32(1.0),
   131  			}},
   132  			expectedType:    ValueTypeF32,
   133  			expectedVal:     api.EncodeF32(1.0),
   134  			expectedString:  "global(1.000000)",
   135  			expectedMutable: true,
   136  		},
   137  		{
   138  			name: "f64 - mutable",
   139  			global: mutableGlobal{internalapi.WazeroOnlyType{}, &GlobalInstance{
   140  				Type: GlobalType{ValType: ValueTypeF64, Mutable: true},
   141  				Val:  api.EncodeF64(1.0),
   142  			}},
   143  			expectedType:    ValueTypeF64,
   144  			expectedVal:     api.EncodeF64(1.0),
   145  			expectedString:  "global(1.000000)",
   146  			expectedMutable: true,
   147  		},
   148  	}
   149  
   150  	for _, tt := range tests {
   151  		tc := tt
   152  
   153  		t.Run(tc.name, func(t *testing.T) {
   154  			require.Equal(t, tc.expectedType, tc.global.Type())
   155  			require.Equal(t, tc.expectedVal, tc.global.Get())
   156  			require.Equal(t, tc.expectedString, tc.global.String())
   157  
   158  			mutable, ok := tc.global.(api.MutableGlobal)
   159  			require.Equal(t, tc.expectedMutable, ok)
   160  			if ok {
   161  				mutable.Set(2)
   162  				require.Equal(t, uint64(2), tc.global.Get())
   163  
   164  				mutable.Set(tc.expectedVal) // Set it back!
   165  				require.Equal(t, tc.expectedVal, tc.global.Get())
   166  			}
   167  		})
   168  	}
   169  }
   170  
   171  func TestPublicModule_Global(t *testing.T) {
   172  	tests := []struct {
   173  		name     string
   174  		module   *Module // module as wat doesn't yet support globals
   175  		expected api.Global
   176  	}{
   177  		{
   178  			name:   "no global",
   179  			module: &Module{},
   180  		},
   181  		{
   182  			name: "global not exported",
   183  			module: &Module{
   184  				GlobalSection: []Global{
   185  					{
   186  						Type: GlobalType{ValType: ValueTypeI32},
   187  						Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
   188  					},
   189  				},
   190  			},
   191  		},
   192  		{
   193  			name: "global exported - immutable I32",
   194  			module: &Module{
   195  				GlobalSection: []Global{
   196  					{
   197  						Type: GlobalType{ValType: ValueTypeI32},
   198  						Init: ConstantExpression{Opcode: OpcodeI32Const, Data: const1},
   199  					},
   200  				},
   201  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   202  			},
   203  			expected: constantGlobal{g: &GlobalInstance{
   204  				Type: GlobalType{ValType: ValueTypeI32},
   205  				Val:  1,
   206  			}},
   207  		},
   208  		{
   209  			name: "global exported - immutable I64",
   210  			module: &Module{
   211  				GlobalSection: []Global{
   212  					{
   213  						Type: GlobalType{ValType: ValueTypeI64},
   214  						Init: ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
   215  					},
   216  				},
   217  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   218  			},
   219  			expected: constantGlobal{g: &GlobalInstance{
   220  				Type: GlobalType{ValType: ValueTypeI64},
   221  				Val:  1,
   222  			}},
   223  		},
   224  		{
   225  			name: "global exported - immutable F32",
   226  			module: &Module{
   227  				GlobalSection: []Global{
   228  					{
   229  						Type: GlobalType{ValType: ValueTypeF32},
   230  						Init: ConstantExpression{
   231  							Opcode: OpcodeF32Const,
   232  							Data:   u64.LeBytes(api.EncodeF32(1.0)),
   233  						},
   234  					},
   235  				},
   236  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   237  			},
   238  			expected: constantGlobal{g: &GlobalInstance{
   239  				Type: GlobalType{ValType: ValueTypeF32},
   240  				Val:  api.EncodeF32(1.0),
   241  			}},
   242  		},
   243  		{
   244  			name: "global exported - immutable F64",
   245  			module: &Module{
   246  				GlobalSection: []Global{
   247  					{
   248  						Type: GlobalType{ValType: ValueTypeF64},
   249  						Init: ConstantExpression{
   250  							Opcode: OpcodeF64Const,
   251  							Data:   u64.LeBytes(api.EncodeF64(1.0)),
   252  						},
   253  					},
   254  				},
   255  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   256  			},
   257  			expected: constantGlobal{g: &GlobalInstance{
   258  				Type: GlobalType{ValType: ValueTypeF64},
   259  				Val:  api.EncodeF64(1.0),
   260  			}},
   261  		},
   262  		{
   263  			name: "global exported - mutable I32",
   264  			module: &Module{
   265  				GlobalSection: []Global{
   266  					{
   267  						Type: GlobalType{ValType: ValueTypeI32, Mutable: true},
   268  						Init: ConstantExpression{Opcode: OpcodeI32Const, Data: leb128.EncodeInt32(1)},
   269  					},
   270  				},
   271  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   272  			},
   273  			expected: mutableGlobal{
   274  				internalapi.WazeroOnlyType{}, &GlobalInstance{
   275  					Type: GlobalType{ValType: ValueTypeI32, Mutable: true}, Val: 1,
   276  				},
   277  			},
   278  		},
   279  		{
   280  			name: "global exported - mutable I64",
   281  			module: &Module{
   282  				GlobalSection: []Global{
   283  					{
   284  						Type: GlobalType{ValType: ValueTypeI64, Mutable: true},
   285  						Init: ConstantExpression{Opcode: OpcodeI64Const, Data: leb128.EncodeInt64(1)},
   286  					},
   287  				},
   288  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   289  			},
   290  			expected: mutableGlobal{
   291  				internalapi.WazeroOnlyType{}, &GlobalInstance{
   292  					Type: GlobalType{ValType: ValueTypeI64, Mutable: true}, Val: 1,
   293  				},
   294  			},
   295  		},
   296  		{
   297  			name: "global exported - mutable F32",
   298  			module: &Module{
   299  				GlobalSection: []Global{
   300  					{
   301  						Type: GlobalType{ValType: ValueTypeF32, Mutable: true},
   302  						Init: ConstantExpression{
   303  							Opcode: OpcodeF32Const,
   304  							Data:   u64.LeBytes(api.EncodeF32(1.0)),
   305  						},
   306  					},
   307  				},
   308  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   309  			},
   310  			expected: mutableGlobal{
   311  				internalapi.WazeroOnlyType{}, &GlobalInstance{
   312  					Type: GlobalType{ValType: ValueTypeF32, Mutable: true}, Val: api.EncodeF32(1.0),
   313  				},
   314  			},
   315  		},
   316  		{
   317  			name: "global exported - mutable F64",
   318  			module: &Module{
   319  				GlobalSection: []Global{
   320  					{
   321  						Type: GlobalType{ValType: ValueTypeF64, Mutable: true},
   322  						Init: ConstantExpression{
   323  							Opcode: OpcodeF64Const,
   324  							Data:   u64.LeBytes(api.EncodeF64(1.0)),
   325  						},
   326  					},
   327  				},
   328  				Exports: map[string]*Export{"global": {Type: ExternTypeGlobal, Name: "global"}},
   329  			},
   330  			expected: mutableGlobal{
   331  				internalapi.WazeroOnlyType{}, &GlobalInstance{
   332  					Type: GlobalType{ValType: ValueTypeF64, Mutable: true}, Val: api.EncodeF64(1.0),
   333  				},
   334  			},
   335  		},
   336  	}
   337  
   338  	for _, tt := range tests {
   339  		tc := tt
   340  
   341  		s := newStore()
   342  		t.Run(tc.name, func(t *testing.T) {
   343  			// Instantiate the module and get the export of the above global
   344  			module, err := s.Instantiate(context.Background(), tc.module, t.Name(), nil, nil)
   345  			require.NoError(t, err)
   346  
   347  			if global := module.ExportedGlobal("global"); tc.expected != nil {
   348  				require.Equal(t, tc.expected, global)
   349  			} else {
   350  				require.Nil(t, global)
   351  			}
   352  		})
   353  	}
   354  }