summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/exceptions/side-effects-in-try.js
blob: d5d5567fca2a9337c3c9f3ad057be3a6e19c45c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// -----------------------------------------------------------------------------
// The tests in this file assert that any side effects that happened in try code
// before an exception was thrown, will be known to the landing pad. It checks
// local throws and throws from direct calls (of local and imported functions).
// Side effects checked are changes to locals, and to globals.
// -----------------------------------------------------------------------------

load(libdir + "eqArrayHelper.js");


function testSideEffectsOnLocals() {
  // Locals set before and after throwing instructions, either locally, or from
  // a direct wasm function call, for locals of all Wasm numtype and a local of
  // reftype (externref), and the thrown exception carrying a value from each
  // Wasm numtype and one of Wasm vectype (Simd128). Testing to see if the state
  // of the locals at the moment $exn is thrown, is known to the landing pad
  // when $exn is caught.

  let localThrow = "(throw $exn)";

  // The following is taken from calls.js
  // Some variables to be used in all tests.
  let typesJS = ["i32", "i64", "f32", "f64"];
  let types = typesJS.join(" ");
  let exnTypeDef = `(type $exnType (func (param ${types})))`;
  let correctLocalValues =
      `;; Correct local values
             (i32.const 2)
             (i64.const 3)
             (f32.const 4)
             (f64.const 13.37)`;
  let correctLocalValuesJS = [2, 3n, 4, 13.37];

  let wrongValues =
      `;; Wrong values.
             (i32.const 5)
             (i64.const 6)
             (f32.const 0.1)
             (f64.const 0.6437)`;
  let wrongValuesJS = [5, 6n, 0.1, 0.6437];

  // These variables are specific to the tests in this file.
  let throwValues =
      `;; Values to throw and catch.
             (i32.const 7)
             (i64.const 8)
             (f32.const 9)
             (f64.const 27.11)`;
  let thrownValuesJS = [7, 8n, 9, 27.11];

  let correctResultsJS = function(externref) {
    return [].concat(thrownValuesJS,
                     correctLocalValuesJS,
                     [externref, 1]);
  }

  // Testing also locals of Wasm vectype.
  // The following depend on whether simd is enabled or not. We write it like
  // this so we can run this test also when SIMD is not enabled.
  let wrongV128 = "";
  let correctV128 = "";
  let checkV128Value = "";

  if (wasmSimdEnabled()) {
    wrongV128 = `(v128.const i32x4 11 22 33 44)`;
    correctV128 = `(v128.const i32x4 55 66 77 88)`;
    checkV128Value =
      `       ${correctV128}
              (i32x4.eq)
              (i32x4.all_true)`;
    v128Type = " v128";
  } else {
    wrongV128 = "(i32.const 0)";
    correctV128 = "(i32.const 1)";
    v128Type = " i32";
  }

  let localTypes = types + " externref";
  let resultTypes = types + " " + localTypes;

  // The last i32 in the results is the v128 check.
  let testFuncTypeInline =
      `(param $argCorrectRef externref)
       (param $argWrongRef externref)
       (result ${resultTypes} i32)
       (local $localI32 i32)
       (local $localI64 i64)
       (local $localF32 f32)
       (local $localF64 f64)
       (local $localExternref externref)
       (local $localV128 ${v128Type})`;

  let localsSet =
      `;; Set locals.
             (local.set $localV128)
             (local.set $localExternref)
             (local.set $localF64)
             (local.set $localF32)
             (local.set $localI64)
             (local.set $localI32)`;
  let localsGet =
      `;; Get locals.
             (local.get $localI32)
             (local.get $localI64)
             (local.get $localF32)
             (local.get $localF64)
             (local.get $localExternref)
             (local.get $localV128)`;

  // The test module parts. ----------------------------------------------------

  let importsModule =
      `(module
         (type $exnType (func (param ${types})))
         (tag $exn (export "exn") (type $exnType))
         (func (export "throwif") (param $ifPredicate i32)
           (if (local.get $ifPredicate)
             (then
               ${throwValues}
               ${localThrow}))))`;

  let moduleHeader = `
       (module
         ${exnTypeDef}
         (import "m" "exn" (tag $exn (type $exnType)))
         (tag $emptyExn)
         (import "m" "throwif" (func $throwif (param $ifPredicate i32)))
         (func $wontThrow
           (throw $emptyExn))
         (func $localCallThrow
             ${throwValues}
             ${localThrow})
         (func (export "testFunc") ${testFuncTypeInline}
           (try (result ${resultTypes} ${v128Type})
             (do
               ;; Locals not set.
               (i32.const 0) ;; Predicate for $throwif.
               (call $throwif)  ;; So this doesn't throw.
               ;; Set correct locals before throw to be caught.
               ${correctLocalValues}
               (local.get $argCorrectRef)
               ${correctV128}
               ${localsSet}
               ;; Next up should be $exn being thrown locally or via a call.`;

  let moduleRest = ` ;; The above throw to $exn should be caught here --------.
               ;; Set wrong locals after throw to be caught.              ;;    |
               ${wrongValues}                                             ;;    |
               (local.get $argWrongRef) ;; The wrong externref param.     ;;    |
               ${wrongV128}                                               ;;    |
               ${localsSet}                                               ;;    |
               (call $wontThrow)                                          ;;    |
               ${wrongValues}                                             ;;    |
               ${localsGet});; End of try code.                           ;;    |
             (catch $emptyExn                                             ;;    |
               ${wrongValues}                                             ;;    |
               ${localsGet})                                              ;;    |
             (catch $exn ;; <---------------------------------------------------'
               ${localsGet})
             (catch_all
               ${wrongValues}
               ${localsGet}))
           ;; Check if the local has the correct v128 value.
           ${checkV128Value}))`;

  let localThrowValues = `
             ${throwValues}
             (throw $exn)`;
  let directLocalCall = `
             (call $localCallThrow)`;
  let directImportCall = `
             (i32.const 1)
             (call $throwif)`;

  // Run test for side effects on locals before throwing an exception locally,
  // or from a direct call.

  let callInstructions = [localThrowValues, directLocalCall, directImportCall];

  for (let callThrow of callInstructions) {
    console.log("callThrow = " + callThrow); // Uncomment for debugging.
    moduleText = moduleHeader + callThrow + moduleRest;
    console.log("moduleText = " + moduleText); // Uncomment for debugging.
    assertEqArray(
      wasmEvalText(moduleText,
                   { m : wasmEvalText(importsModule).exports }
                  ).exports.testFunc("foo", "wrongFoo"),
      correctResultsJS("foo"));
  }
}

// Setting globals in try code, and testing to see if the changes are known to
// the landing pad.
function testGlobals() {
  let test = function (type, initialValue, resultValue, wrongValue, coercion) {
    let exports = wasmEvalText(
      `(module
         (tag (export "exn"))
         (func (export "throws")
           (throw 0)))`
    ).exports;

    assertEq(
      wasmEvalText(
        `(module
           (import "m" "exn" (tag $exn))
           (tag $notThrownExn)
           (import "m" "throws" (func $throws))
           (global (mut ${type}) (${type}.const ${initialValue}))
           (func (export "testFunc") (result ${type})
             (try (result ${type})
               (do
                 (global.set 0 (${type}.const ${resultValue}))
                 (call $throws)
                 (global.set 0 (${type}.const ${wrongValue}))
                 (global.get 0))
               (catch $notThrownExn
                 (${type}.const ${wrongValue}))
               (catch $exn
                 (global.get 0)))))`,
        { m: exports }
      ).exports.testFunc(), coercion(resultValue));
  };

  test("i32", 2, 7, 27, x => x);
  test("i64", 2n, 7n, 27n, x => x);
  test("f32", 0.3, 0.1, 0.6, Math.fround);
  test("f64", 13.37, 0.6437244242412325666666, 4, x => x);
};

// Run all tests.

testSideEffectsOnLocals();
testGlobals();