1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/wasm/wasm-module-builder.h"
6 #include "test/cctest/cctest.h"
7 #include "test/cctest/wasm/wasm-run-utils.h"
8 #include "test/common/wasm/test-signatures.h"
9 #include "test/common/wasm/wasm-macro-gen.h"
10 
11 namespace v8 {
12 namespace internal {
13 namespace wasm {
14 namespace test_run_wasm_bulk_memory {
15 
16 namespace {
CheckMemoryEquals(TestingModuleBuilder * builder,size_t index,const std::vector<byte> & expected)17 void CheckMemoryEquals(TestingModuleBuilder* builder, size_t index,
18                        const std::vector<byte>& expected) {
19   const byte* mem_start = builder->raw_mem_start<byte>();
20   const byte* mem_end = builder->raw_mem_end<byte>();
21   size_t mem_size = mem_end - mem_start;
22   CHECK_LE(index, mem_size);
23   CHECK_LE(index + expected.size(), mem_size);
24   for (size_t i = 0; i < expected.size(); ++i) {
25     CHECK_EQ(expected[i], mem_start[index + i]);
26   }
27 }
28 
CheckMemoryEqualsZero(TestingModuleBuilder * builder,size_t index,size_t length)29 void CheckMemoryEqualsZero(TestingModuleBuilder* builder, size_t index,
30                            size_t length) {
31   const byte* mem_start = builder->raw_mem_start<byte>();
32   const byte* mem_end = builder->raw_mem_end<byte>();
33   size_t mem_size = mem_end - mem_start;
34   CHECK_LE(index, mem_size);
35   CHECK_LE(index + length, mem_size);
36   for (size_t i = 0; i < length; ++i) {
37     CHECK_EQ(0, mem_start[index + i]);
38   }
39 }
40 
CheckMemoryEqualsFollowedByZeroes(TestingModuleBuilder * builder,const std::vector<byte> & expected)41 void CheckMemoryEqualsFollowedByZeroes(TestingModuleBuilder* builder,
42                                        const std::vector<byte>& expected) {
43   CheckMemoryEquals(builder, 0, expected);
44   CheckMemoryEqualsZero(builder, expected.size(),
45                         builder->mem_size() - expected.size());
46 }
47 }  // namespace
48 
WASM_EXEC_TEST(MemoryInit)49 WASM_EXEC_TEST(MemoryInit) {
50   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
51   r.builder().AddMemory(kWasmPageSize);
52   const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
53   r.builder().AddPassiveDataSegment(base::ArrayVector(data));
54   BUILD(r,
55         WASM_MEMORY_INIT(0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
56                          WASM_LOCAL_GET(2)),
57         kExprI32Const, 0);
58 
59   // All zeroes.
60   CheckMemoryEqualsZero(&r.builder(), 0, kWasmPageSize);
61 
62   // Copy all bytes from data segment 0, to memory at [10, 20).
63   CHECK_EQ(0, r.Call(10, 0, 10));
64   CheckMemoryEqualsFollowedByZeroes(
65       &r.builder(),
66       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
67 
68   // Copy bytes in range [5, 10) from data segment 0, to memory at [0, 5).
69   CHECK_EQ(0, r.Call(0, 5, 5));
70   CheckMemoryEqualsFollowedByZeroes(
71       &r.builder(),
72       {5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
73 
74   // Copy 0 bytes does nothing.
75   CHECK_EQ(0, r.Call(10, 1, 0));
76   CheckMemoryEqualsFollowedByZeroes(
77       &r.builder(),
78       {5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
79 
80   // Copy 0 at end of memory region or data segment is OK.
81   CHECK_EQ(0, r.Call(kWasmPageSize, 0, 0));
82   CHECK_EQ(0, r.Call(0, sizeof(data), 0));
83 }
84 
WASM_EXEC_TEST(MemoryInitOutOfBoundsData)85 WASM_EXEC_TEST(MemoryInitOutOfBoundsData) {
86   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
87   r.builder().AddMemory(kWasmPageSize);
88   const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
89   r.builder().AddPassiveDataSegment(base::ArrayVector(data));
90   BUILD(r,
91         WASM_MEMORY_INIT(0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
92                          WASM_LOCAL_GET(2)),
93         kExprI32Const, 0);
94 
95   const uint32_t last_5_bytes = kWasmPageSize - 5;
96 
97   // Failing memory.init should not have any effect.
98   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, 0, 6));
99   CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
100 
101   r.builder().BlankMemory();
102   CHECK_EQ(0xDEADBEEF, r.Call(0, 5, 6));
103   CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
104 }
105 
WASM_EXEC_TEST(MemoryInitOutOfBounds)106 WASM_EXEC_TEST(MemoryInitOutOfBounds) {
107   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
108   r.builder().AddMemory(kWasmPageSize);
109   const byte data[kWasmPageSize] = {};
110   r.builder().AddPassiveDataSegment(base::ArrayVector(data));
111   BUILD(r,
112         WASM_MEMORY_INIT(0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
113                          WASM_LOCAL_GET(2)),
114         kExprI32Const, 0);
115 
116   // OK, copy the full data segment to memory.
117   r.Call(0, 0, kWasmPageSize);
118 
119   // Source range must not be out of bounds.
120   CHECK_EQ(0xDEADBEEF, r.Call(0, 1, kWasmPageSize));
121   CHECK_EQ(0xDEADBEEF, r.Call(0, 1000, kWasmPageSize));
122   CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize, 1));
123 
124   // Destination range must not be out of bounds.
125   CHECK_EQ(0xDEADBEEF, r.Call(1, 0, kWasmPageSize));
126   CHECK_EQ(0xDEADBEEF, r.Call(1000, 0, kWasmPageSize));
127   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, 0, 1));
128 
129   // Copy 0 out-of-bounds fails if target is invalid.
130   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, 0, 0));
131   CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize + 1, 0));
132 
133   // Make sure bounds aren't checked with 32-bit wrapping.
134   CHECK_EQ(0xDEADBEEF, r.Call(1, 1, 0xFFFFFFFF));
135 }
136 
WASM_EXEC_TEST(MemoryCopy)137 WASM_EXEC_TEST(MemoryCopy) {
138   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
139   byte* mem = r.builder().AddMemory(kWasmPageSize);
140   BUILD(
141       r,
142       WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
143       kExprI32Const, 0);
144 
145   const byte initial[] = {0, 11, 22, 33, 44, 55, 66, 77};
146   memcpy(mem, initial, sizeof(initial));
147 
148   // Copy from [1, 8] to [10, 16].
149   CHECK_EQ(0, r.Call(10, 1, 8));
150   CheckMemoryEqualsFollowedByZeroes(
151       &r.builder(),
152       {0, 11, 22, 33, 44, 55, 66, 77, 0, 0, 11, 22, 33, 44, 55, 66, 77});
153 
154   // Copy 0 bytes does nothing.
155   CHECK_EQ(0, r.Call(10, 2, 0));
156   CheckMemoryEqualsFollowedByZeroes(
157       &r.builder(),
158       {0, 11, 22, 33, 44, 55, 66, 77, 0, 0, 11, 22, 33, 44, 55, 66, 77});
159 
160   // Copy 0 at end of memory region is OK.
161   CHECK_EQ(0, r.Call(kWasmPageSize, 0, 0));
162   CHECK_EQ(0, r.Call(0, kWasmPageSize, 0));
163 }
164 
WASM_EXEC_TEST(MemoryCopyOverlapping)165 WASM_EXEC_TEST(MemoryCopyOverlapping) {
166   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
167   byte* mem = r.builder().AddMemory(kWasmPageSize);
168   BUILD(
169       r,
170       WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
171       kExprI32Const, 0);
172 
173   const byte initial[] = {10, 20, 30};
174   memcpy(mem, initial, sizeof(initial));
175 
176   // Copy from [0, 3] -> [2, 5]. The copy must not overwrite 30 before copying
177   // it (i.e. cannot copy forward in this case).
178   CHECK_EQ(0, r.Call(2, 0, 3));
179   CheckMemoryEqualsFollowedByZeroes(&r.builder(), {10, 20, 10, 20, 30});
180 
181   // Copy from [2, 5] -> [0, 3]. The copy must not write the first 10 (i.e.
182   // cannot copy backward in this case).
183   CHECK_EQ(0, r.Call(0, 2, 3));
184   CheckMemoryEqualsFollowedByZeroes(&r.builder(), {10, 20, 30, 20, 30});
185 }
186 
WASM_EXEC_TEST(MemoryCopyOutOfBoundsData)187 WASM_EXEC_TEST(MemoryCopyOutOfBoundsData) {
188   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
189   byte* mem = r.builder().AddMemory(kWasmPageSize);
190   BUILD(
191       r,
192       WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
193       kExprI32Const, 0);
194 
195   const byte data[] = {11, 22, 33, 44, 55, 66, 77, 88};
196   memcpy(mem, data, sizeof(data));
197 
198   const uint32_t last_5_bytes = kWasmPageSize - 5;
199 
200   CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
201   CHECK_EQ(0xDEADBEEF, r.Call(last_5_bytes, 0, 6));
202   CheckMemoryEquals(&r.builder(), last_5_bytes, {0, 0, 0, 0, 0});
203 
204   r.builder().BlankMemory();
205   memcpy(mem + last_5_bytes, data, 5);
206   CHECK_EQ(0xDEADBEEF, r.Call(0, last_5_bytes, kWasmPageSize));
207   CheckMemoryEquals(&r.builder(), last_5_bytes, {11, 22, 33, 44, 55});
208 
209   r.builder().BlankMemory();
210   memcpy(mem + last_5_bytes, data, 5);
211   CHECK_EQ(0xDEADBEEF, r.Call(last_5_bytes, 0, kWasmPageSize));
212   CheckMemoryEquals(&r.builder(), last_5_bytes, {11, 22, 33, 44, 55});
213 }
214 
WASM_EXEC_TEST(MemoryCopyOutOfBounds)215 WASM_EXEC_TEST(MemoryCopyOutOfBounds) {
216   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
217   r.builder().AddMemory(kWasmPageSize);
218   BUILD(
219       r,
220       WASM_MEMORY_COPY(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
221       kExprI32Const, 0);
222 
223   // Copy full range is OK.
224   CHECK_EQ(0, r.Call(0, 0, kWasmPageSize));
225 
226   // Source range must not be out of bounds.
227   CHECK_EQ(0xDEADBEEF, r.Call(0, 1, kWasmPageSize));
228   CHECK_EQ(0xDEADBEEF, r.Call(0, 1000, kWasmPageSize));
229   CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize, 1));
230 
231   // Destination range must not be out of bounds.
232   CHECK_EQ(0xDEADBEEF, r.Call(1, 0, kWasmPageSize));
233   CHECK_EQ(0xDEADBEEF, r.Call(1000, 0, kWasmPageSize));
234   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, 0, 1));
235 
236   // Copy 0 out-of-bounds fails if target is invalid.
237   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, 0, 0));
238   CHECK_EQ(0xDEADBEEF, r.Call(0, kWasmPageSize + 1, 0));
239 
240   // Make sure bounds aren't checked with 32-bit wrapping.
241   CHECK_EQ(0xDEADBEEF, r.Call(1, 1, 0xFFFFFFFF));
242 }
243 
WASM_EXEC_TEST(MemoryFill)244 WASM_EXEC_TEST(MemoryFill) {
245   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
246   r.builder().AddMemory(kWasmPageSize);
247   BUILD(
248       r,
249       WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
250       kExprI32Const, 0);
251   CHECK_EQ(0, r.Call(1, 33, 5));
252   CheckMemoryEqualsFollowedByZeroes(&r.builder(), {0, 33, 33, 33, 33, 33});
253 
254   CHECK_EQ(0, r.Call(4, 66, 4));
255   CheckMemoryEqualsFollowedByZeroes(&r.builder(),
256                                     {0, 33, 33, 33, 66, 66, 66, 66});
257 
258   // Fill 0 bytes does nothing.
259   CHECK_EQ(0, r.Call(4, 66, 0));
260   CheckMemoryEqualsFollowedByZeroes(&r.builder(),
261                                     {0, 33, 33, 33, 66, 66, 66, 66});
262 
263   // Fill 0 at end of memory region is OK.
264   CHECK_EQ(0, r.Call(kWasmPageSize, 66, 0));
265 }
266 
WASM_EXEC_TEST(MemoryFillValueWrapsToByte)267 WASM_EXEC_TEST(MemoryFillValueWrapsToByte) {
268   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
269   r.builder().AddMemory(kWasmPageSize);
270   BUILD(
271       r,
272       WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
273       kExprI32Const, 0);
274   CHECK_EQ(0, r.Call(0, 1000, 3));
275   const byte expected = 1000 & 255;
276   CheckMemoryEqualsFollowedByZeroes(&r.builder(),
277                                     {expected, expected, expected});
278 }
279 
WASM_EXEC_TEST(MemoryFillOutOfBoundsData)280 WASM_EXEC_TEST(MemoryFillOutOfBoundsData) {
281   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
282   r.builder().AddMemory(kWasmPageSize);
283   BUILD(
284       r,
285       WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
286       kExprI32Const, 0);
287   const byte v = 123;
288   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, v, 999));
289   CheckMemoryEquals(&r.builder(), kWasmPageSize - 6, {0, 0, 0, 0, 0, 0});
290 }
291 
WASM_EXEC_TEST(MemoryFillOutOfBounds)292 WASM_EXEC_TEST(MemoryFillOutOfBounds) {
293   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
294   r.builder().AddMemory(kWasmPageSize);
295   BUILD(
296       r,
297       WASM_MEMORY_FILL(WASM_LOCAL_GET(0), WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
298       kExprI32Const, 0);
299 
300   const byte v = 123;
301 
302   // Destination range must not be out of bounds.
303   CHECK_EQ(0xDEADBEEF, r.Call(1, v, kWasmPageSize));
304   CHECK_EQ(0xDEADBEEF, r.Call(1000, v, kWasmPageSize));
305   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize, v, 1));
306 
307   // Fill 0 out-of-bounds still fails.
308   CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize + 1, v, 0));
309 
310   // Make sure bounds aren't checked with 32-bit wrapping.
311   CHECK_EQ(0xDEADBEEF, r.Call(1, v, 0xFFFFFFFF));
312 }
313 
WASM_EXEC_TEST(DataDropTwice)314 WASM_EXEC_TEST(DataDropTwice) {
315   WasmRunner<uint32_t> r(execution_tier);
316   r.builder().AddMemory(kWasmPageSize);
317   const byte data[] = {0};
318   r.builder().AddPassiveDataSegment(base::ArrayVector(data));
319   BUILD(r, WASM_DATA_DROP(0), kExprI32Const, 0);
320 
321   CHECK_EQ(0, r.Call());
322   CHECK_EQ(0, r.Call());
323 }
324 
WASM_EXEC_TEST(DataDropThenMemoryInit)325 WASM_EXEC_TEST(DataDropThenMemoryInit) {
326   WasmRunner<uint32_t> r(execution_tier);
327   r.builder().AddMemory(kWasmPageSize);
328   const byte data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
329   r.builder().AddPassiveDataSegment(base::ArrayVector(data));
330   BUILD(r, WASM_DATA_DROP(0),
331         WASM_MEMORY_INIT(0, WASM_I32V_1(0), WASM_I32V_1(1), WASM_I32V_1(2)),
332         kExprI32Const, 0);
333 
334   CHECK_EQ(0xDEADBEEF, r.Call());
335 }
336 
TestTableCopyInbounds(TestExecutionTier execution_tier,int table_dst,int table_src)337 void TestTableCopyInbounds(TestExecutionTier execution_tier, int table_dst,
338                            int table_src) {
339   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
340   const uint32_t kTableSize = 5;
341   // Add 10 function tables, even though we only test one table.
342   for (int i = 0; i < 10; ++i) {
343     r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
344   }
345   BUILD(r,
346         WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
347                         WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
348         kExprI32Const, 0);
349 
350   for (uint32_t i = 0; i <= kTableSize; ++i) {
351     r.CheckCallViaJS(0, 0, 0, i);  // nop
352     r.CheckCallViaJS(0, 0, i, kTableSize - i);
353     r.CheckCallViaJS(0, i, 0, kTableSize - i);
354   }
355 }
356 
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom0To0)357 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom0To0) {
358   TestTableCopyInbounds(execution_tier, 0, 0);
359 }
360 
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom3To0)361 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom3To0) {
362   EXPERIMENTAL_FLAG_SCOPE(reftypes);
363   TestTableCopyInbounds(execution_tier, 3, 0);
364 }
365 
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom5To9)366 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom5To9) {
367   EXPERIMENTAL_FLAG_SCOPE(reftypes);
368   TestTableCopyInbounds(execution_tier, 5, 9);
369 }
370 
WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom6To6)371 WASM_COMPILED_EXEC_TEST(TableCopyInboundsFrom6To6) {
372   EXPERIMENTAL_FLAG_SCOPE(reftypes);
373   TestTableCopyInbounds(execution_tier, 6, 6);
374 }
375 
376 namespace {
377 template <typename... Args>
CheckTable(Isolate * isolate,Handle<WasmTableObject> table,Args...args)378 void CheckTable(Isolate* isolate, Handle<WasmTableObject> table, Args... args) {
379   uint32_t args_length = static_cast<uint32_t>(sizeof...(args));
380   CHECK_EQ(table->current_length(), args_length);
381   Handle<Object> handles[] = {args...};
382   for (uint32_t i = 0; i < args_length; ++i) {
383     CHECK(WasmTableObject::Get(isolate, table, i).is_identical_to(handles[i]));
384   }
385 }
386 
387 template <typename WasmRunner, typename... Args>
CheckTableCall(Isolate * isolate,Handle<WasmTableObject> table,WasmRunner * r,uint32_t function_index,Args...args)388 void CheckTableCall(Isolate* isolate, Handle<WasmTableObject> table,
389                     WasmRunner* r, uint32_t function_index, Args... args) {
390   uint32_t args_length = static_cast<uint32_t>(sizeof...(args));
391   CHECK_EQ(table->current_length(), args_length);
392   double expected[] = {args...};
393   for (uint32_t i = 0; i < args_length; ++i) {
394     Handle<Object> buffer[] = {isolate->factory()->NewNumber(i)};
395     r->CheckCallApplyViaJS(expected[i], function_index, buffer, 1);
396   }
397 }
398 }  // namespace
399 
TestTableInitElems(TestExecutionTier execution_tier,int table_index)400 void TestTableInitElems(TestExecutionTier execution_tier, int table_index) {
401   Isolate* isolate = CcTest::InitIsolateOnce();
402   HandleScope scope(isolate);
403   TestSignatures sigs;
404   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
405   const uint32_t kTableSize = 5;
406   std::vector<uint32_t> function_indexes;
407   const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
408 
409   for (uint32_t i = 0; i < kTableSize; ++i) {
410     WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
411     BUILD(fn, WASM_I32V_1(i));
412     fn.SetSigIndex(sig_index);
413     function_indexes.push_back(fn.function_index());
414   }
415 
416   // Passive element segment has [f0, f1, f2, f3, f4, null].
417   function_indexes.push_back(WasmModuleBuilder::kNullIndex);
418 
419   // Add 10 function tables, even though we only test one table.
420   for (int i = 0; i < 10; ++i) {
421     r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
422   }
423   r.builder().AddPassiveElementSegment(function_indexes);
424 
425   WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
426   BUILD(call,
427         WASM_CALL_INDIRECT_TABLE(table_index, sig_index, WASM_LOCAL_GET(0)));
428   const uint32_t call_index = call.function_index();
429 
430   BUILD(r,
431         WASM_TABLE_INIT(table_index, 0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
432                         WASM_LOCAL_GET(2)),
433         kExprI32Const, 0);
434 
435   auto table =
436       handle(WasmTableObject::cast(
437                  r.builder().instance_object()->tables().get(table_index)),
438              isolate);
439   const double null = 0xDEADBEEF;
440 
441   CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
442 
443   // 0 count is ok in bounds, and at end of regions.
444   r.CheckCallViaJS(0, 0, 0, 0);
445   r.CheckCallViaJS(0, kTableSize, 0, 0);
446   r.CheckCallViaJS(0, 0, kTableSize, 0);
447 
448   // Test actual writes.
449   r.CheckCallViaJS(0, 0, 0, 1);
450   CheckTableCall(isolate, table, &r, call_index, 0.0, null, null, null, null);
451   r.CheckCallViaJS(0, 0, 0, 2);
452   CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, null, null, null);
453   r.CheckCallViaJS(0, 0, 0, 3);
454   CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, null, null);
455   r.CheckCallViaJS(0, 3, 0, 2);
456   CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 0.0, 1.0);
457   r.CheckCallViaJS(0, 3, 1, 2);
458   CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 1.0, 2.0);
459   r.CheckCallViaJS(0, 3, 2, 2);
460   CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 2.0, 3.0);
461   r.CheckCallViaJS(0, 3, 3, 2);
462   CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 3.0, 4.0);
463 }
464 
WASM_COMPILED_EXEC_TEST(TableInitElems0)465 WASM_COMPILED_EXEC_TEST(TableInitElems0) {
466   TestTableInitElems(execution_tier, 0);
467 }
WASM_COMPILED_EXEC_TEST(TableInitElems7)468 WASM_COMPILED_EXEC_TEST(TableInitElems7) {
469   EXPERIMENTAL_FLAG_SCOPE(reftypes);
470   TestTableInitElems(execution_tier, 7);
471 }
WASM_COMPILED_EXEC_TEST(TableInitElems9)472 WASM_COMPILED_EXEC_TEST(TableInitElems9) {
473   EXPERIMENTAL_FLAG_SCOPE(reftypes);
474   TestTableInitElems(execution_tier, 9);
475 }
476 
TestTableInitOob(TestExecutionTier execution_tier,int table_index)477 void TestTableInitOob(TestExecutionTier execution_tier, int table_index) {
478   Isolate* isolate = CcTest::InitIsolateOnce();
479   HandleScope scope(isolate);
480   TestSignatures sigs;
481   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
482   const uint32_t kTableSize = 5;
483   std::vector<uint32_t> function_indexes;
484   const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
485 
486   for (uint32_t i = 0; i < kTableSize; ++i) {
487     WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
488     BUILD(fn, WASM_I32V_1(i));
489     fn.SetSigIndex(sig_index);
490     function_indexes.push_back(fn.function_index());
491   }
492 
493   for (int i = 0; i < 10; ++i) {
494     r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
495   }
496   r.builder().AddPassiveElementSegment(function_indexes);
497 
498   WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
499   BUILD(call,
500         WASM_CALL_INDIRECT_TABLE(table_index, sig_index, WASM_LOCAL_GET(0)));
501   const uint32_t call_index = call.function_index();
502 
503   BUILD(r,
504         WASM_TABLE_INIT(table_index, 0, WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
505                         WASM_LOCAL_GET(2)),
506         kExprI32Const, 0);
507 
508   auto table =
509       handle(WasmTableObject::cast(
510                  r.builder().instance_object()->tables().get(table_index)),
511              isolate);
512   const double null = 0xDEADBEEF;
513 
514   CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
515 
516   // Out-of-bounds table.init should not have any effect.
517   r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
518   CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
519 
520   r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
521   CheckTableCall(isolate, table, &r, call_index, null, null, null, null, null);
522 
523   // 0-count is still oob if target is invalid.
524   r.CheckCallViaJS(0xDEADBEEF, kTableSize + 1, 0, 0);
525   r.CheckCallViaJS(0xDEADBEEF, 0, kTableSize + 1, 0);
526 
527   r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
528   r.CheckCallViaJS(0xDEADBEEF, 0, 1, 5);
529   r.CheckCallViaJS(0xDEADBEEF, 0, 2, 4);
530   r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
531   r.CheckCallViaJS(0xDEADBEEF, 0, 4, 2);
532   r.CheckCallViaJS(0xDEADBEEF, 0, 5, 1);
533 
534   r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
535   r.CheckCallViaJS(0xDEADBEEF, 1, 0, 5);
536   r.CheckCallViaJS(0xDEADBEEF, 2, 0, 4);
537   r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
538   r.CheckCallViaJS(0xDEADBEEF, 4, 0, 2);
539   r.CheckCallViaJS(0xDEADBEEF, 5, 0, 1);
540 
541   r.CheckCallViaJS(0xDEADBEEF, 10, 0, 1);
542   r.CheckCallViaJS(0xDEADBEEF, 0, 10, 1);
543 }
544 
WASM_COMPILED_EXEC_TEST(TableInitOob0)545 WASM_COMPILED_EXEC_TEST(TableInitOob0) { TestTableInitOob(execution_tier, 0); }
WASM_COMPILED_EXEC_TEST(TableInitOob7)546 WASM_COMPILED_EXEC_TEST(TableInitOob7) {
547   EXPERIMENTAL_FLAG_SCOPE(reftypes);
548   TestTableInitOob(execution_tier, 7);
549 }
WASM_COMPILED_EXEC_TEST(TableInitOob9)550 WASM_COMPILED_EXEC_TEST(TableInitOob9) {
551   EXPERIMENTAL_FLAG_SCOPE(reftypes);
552   TestTableInitOob(execution_tier, 9);
553 }
554 
TestTableCopyElems(TestExecutionTier execution_tier,int table_dst,int table_src)555 void TestTableCopyElems(TestExecutionTier execution_tier, int table_dst,
556                         int table_src) {
557   Isolate* isolate = CcTest::InitIsolateOnce();
558   HandleScope scope(isolate);
559   TestSignatures sigs;
560   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
561   const uint32_t kTableSize = 5;
562   uint16_t function_indexes[kTableSize];
563   const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
564 
565   for (uint32_t i = 0; i < kTableSize; ++i) {
566     WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
567     BUILD(fn, WASM_I32V_1(i));
568     fn.SetSigIndex(sig_index);
569     function_indexes[i] = fn.function_index();
570   }
571 
572   for (int i = 0; i < 10; ++i) {
573     r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
574   }
575 
576   BUILD(r,
577         WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
578                         WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
579         kExprI32Const, 0);
580 
581   r.builder().FreezeSignatureMapAndInitializeWrapperCache();
582 
583   auto table =
584       handle(WasmTableObject::cast(
585                  r.builder().instance_object()->tables().get(table_dst)),
586              isolate);
587   r.CheckCallViaJS(0, 0, 0, kTableSize);
588   auto f0 = WasmTableObject::Get(isolate, table, 0);
589   auto f1 = WasmTableObject::Get(isolate, table, 1);
590   auto f2 = WasmTableObject::Get(isolate, table, 2);
591   auto f3 = WasmTableObject::Get(isolate, table, 3);
592   auto f4 = WasmTableObject::Get(isolate, table, 4);
593 
594   if (table_dst == table_src) {
595     CheckTable(isolate, table, f0, f1, f2, f3, f4);
596     r.CheckCallViaJS(0, 0, 1, 1);
597     CheckTable(isolate, table, f1, f1, f2, f3, f4);
598     r.CheckCallViaJS(0, 0, 1, 2);
599     CheckTable(isolate, table, f1, f2, f2, f3, f4);
600     r.CheckCallViaJS(0, 3, 0, 2);
601     CheckTable(isolate, table, f1, f2, f2, f1, f2);
602     r.CheckCallViaJS(0, 1, 0, 2);
603     CheckTable(isolate, table, f1, f1, f2, f1, f2);
604   } else {
605     CheckTable(isolate, table, f0, f1, f2, f3, f4);
606     r.CheckCallViaJS(0, 0, 1, 1);
607     CheckTable(isolate, table, f1, f1, f2, f3, f4);
608     r.CheckCallViaJS(0, 0, 1, 2);
609     CheckTable(isolate, table, f1, f2, f2, f3, f4);
610     r.CheckCallViaJS(0, 3, 0, 2);
611     CheckTable(isolate, table, f1, f2, f2, f0, f1);
612     r.CheckCallViaJS(0, 1, 0, 2);
613     CheckTable(isolate, table, f1, f0, f1, f0, f1);
614   }
615 }
616 
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom0To0)617 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom0To0) {
618   TestTableCopyElems(execution_tier, 0, 0);
619 }
620 
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom3To0)621 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom3To0) {
622   EXPERIMENTAL_FLAG_SCOPE(reftypes);
623   TestTableCopyElems(execution_tier, 3, 0);
624 }
625 
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom5To9)626 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom5To9) {
627   EXPERIMENTAL_FLAG_SCOPE(reftypes);
628   TestTableCopyElems(execution_tier, 5, 9);
629 }
630 
WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom6To6)631 WASM_COMPILED_EXEC_TEST(TableCopyElemsFrom6To6) {
632   EXPERIMENTAL_FLAG_SCOPE(reftypes);
633   TestTableCopyElems(execution_tier, 6, 6);
634 }
635 
TestTableCopyCalls(TestExecutionTier execution_tier,int table_dst,int table_src)636 void TestTableCopyCalls(TestExecutionTier execution_tier, int table_dst,
637                         int table_src) {
638   Isolate* isolate = CcTest::InitIsolateOnce();
639   HandleScope scope(isolate);
640   TestSignatures sigs;
641   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
642   const uint32_t kTableSize = 5;
643   uint16_t function_indexes[kTableSize];
644   const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
645 
646   for (uint32_t i = 0; i < kTableSize; ++i) {
647     WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
648     BUILD(fn, WASM_I32V_1(i));
649     fn.SetSigIndex(sig_index);
650     function_indexes[i] = fn.function_index();
651   }
652 
653   for (int i = 0; i < 10; ++i) {
654     r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
655   }
656 
657   WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
658   BUILD(call,
659         WASM_CALL_INDIRECT_TABLE(table_dst, sig_index, WASM_LOCAL_GET(0)));
660   const uint32_t call_index = call.function_index();
661 
662   BUILD(r,
663         WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
664                         WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
665         kExprI32Const, 0);
666 
667   auto table =
668       handle(WasmTableObject::cast(
669                  r.builder().instance_object()->tables().get(table_dst)),
670              isolate);
671 
672   if (table_dst == table_src) {
673     CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 3.0, 4.0);
674     r.CheckCallViaJS(0, 0, 1, 1);
675     CheckTableCall(isolate, table, &r, call_index, 1.0, 1.0, 2.0, 3.0, 4.0);
676     r.CheckCallViaJS(0, 0, 1, 2);
677     CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 3.0, 4.0);
678     r.CheckCallViaJS(0, 3, 0, 2);
679     CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 1.0, 2.0);
680   } else {
681     CheckTableCall(isolate, table, &r, call_index, 0.0, 1.0, 2.0, 3.0, 4.0);
682     r.CheckCallViaJS(0, 0, 1, 1);
683     CheckTableCall(isolate, table, &r, call_index, 1.0, 1.0, 2.0, 3.0, 4.0);
684     r.CheckCallViaJS(0, 0, 1, 2);
685     CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 3.0, 4.0);
686     r.CheckCallViaJS(0, 3, 0, 2);
687     CheckTableCall(isolate, table, &r, call_index, 1.0, 2.0, 2.0, 0.0, 1.0);
688   }
689 }
690 
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo0From0)691 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo0From0) {
692   TestTableCopyCalls(execution_tier, 0, 0);
693 }
694 
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo3From0)695 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo3From0) {
696   EXPERIMENTAL_FLAG_SCOPE(reftypes);
697   TestTableCopyCalls(execution_tier, 3, 0);
698 }
699 
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo5From9)700 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo5From9) {
701   EXPERIMENTAL_FLAG_SCOPE(reftypes);
702   TestTableCopyCalls(execution_tier, 5, 9);
703 }
704 
WASM_COMPILED_EXEC_TEST(TableCopyCallsTo6From6)705 WASM_COMPILED_EXEC_TEST(TableCopyCallsTo6From6) {
706   EXPERIMENTAL_FLAG_SCOPE(reftypes);
707   TestTableCopyCalls(execution_tier, 6, 6);
708 }
709 
TestTableCopyOobWrites(TestExecutionTier execution_tier,int table_dst,int table_src)710 void TestTableCopyOobWrites(TestExecutionTier execution_tier, int table_dst,
711                             int table_src) {
712   Isolate* isolate = CcTest::InitIsolateOnce();
713   HandleScope scope(isolate);
714   TestSignatures sigs;
715   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
716   const uint32_t kTableSize = 5;
717   uint16_t function_indexes[kTableSize];
718   const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
719 
720   for (uint32_t i = 0; i < kTableSize; ++i) {
721     WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
722     BUILD(fn, WASM_I32V_1(i));
723     fn.SetSigIndex(sig_index);
724     function_indexes[i] = fn.function_index();
725   }
726 
727   for (int i = 0; i < 10; ++i) {
728     r.builder().AddIndirectFunctionTable(function_indexes, kTableSize);
729   }
730 
731   BUILD(r,
732         WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
733                         WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
734         kExprI32Const, 0);
735 
736   r.builder().FreezeSignatureMapAndInitializeWrapperCache();
737 
738   auto table =
739       handle(WasmTableObject::cast(
740                  r.builder().instance_object()->tables().get(table_dst)),
741              isolate);
742   // Fill the dst table with values from the src table, to make checks easier.
743   r.CheckCallViaJS(0, 0, 0, kTableSize);
744   auto f0 = WasmTableObject::Get(isolate, table, 0);
745   auto f1 = WasmTableObject::Get(isolate, table, 1);
746   auto f2 = WasmTableObject::Get(isolate, table, 2);
747   auto f3 = WasmTableObject::Get(isolate, table, 3);
748   auto f4 = WasmTableObject::Get(isolate, table, 4);
749 
750   CheckTable(isolate, table, f0, f1, f2, f3, f4);
751 
752   // Failing table.copy should not have any effect.
753   r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
754   CheckTable(isolate, table, f0, f1, f2, f3, f4);
755 
756   r.CheckCallViaJS(0xDEADBEEF, 0, 4, 2);
757   CheckTable(isolate, table, f0, f1, f2, f3, f4);
758 
759   r.CheckCallViaJS(0xDEADBEEF, 3, 0, 99);
760   CheckTable(isolate, table, f0, f1, f2, f3, f4);
761 
762   r.CheckCallViaJS(0xDEADBEEF, 0, 1, 99);
763   CheckTable(isolate, table, f0, f1, f2, f3, f4);
764 }
765 
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom0To0)766 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom0To0) {
767   TestTableCopyOobWrites(execution_tier, 0, 0);
768 }
769 
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom3To0)770 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom3To0) {
771   EXPERIMENTAL_FLAG_SCOPE(reftypes);
772   TestTableCopyOobWrites(execution_tier, 3, 0);
773 }
774 
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom5To9)775 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom5To9) {
776   EXPERIMENTAL_FLAG_SCOPE(reftypes);
777   TestTableCopyOobWrites(execution_tier, 5, 9);
778 }
779 
WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom6To6)780 WASM_COMPILED_EXEC_TEST(TableCopyOobWritesFrom6To6) {
781   EXPERIMENTAL_FLAG_SCOPE(reftypes);
782   TestTableCopyOobWrites(execution_tier, 6, 6);
783 }
784 
TestTableCopyOob1(TestExecutionTier execution_tier,int table_dst,int table_src)785 void TestTableCopyOob1(TestExecutionTier execution_tier, int table_dst,
786                        int table_src) {
787   WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
788   const uint32_t kTableSize = 5;
789 
790   for (int i = 0; i < 10; ++i) {
791     r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
792   }
793 
794   BUILD(r,
795         WASM_TABLE_COPY(table_dst, table_src, WASM_LOCAL_GET(0),
796                         WASM_LOCAL_GET(1), WASM_LOCAL_GET(2)),
797         kExprI32Const, 0);
798 
799   r.CheckCallViaJS(0, 0, 0, 1);           // nop
800   r.CheckCallViaJS(0, 0, 0, kTableSize);  // nop
801   r.CheckCallViaJS(0xDEADBEEF, 0, 0, kTableSize + 1);
802   r.CheckCallViaJS(0xDEADBEEF, 1, 0, kTableSize);
803   r.CheckCallViaJS(0xDEADBEEF, 0, 1, kTableSize);
804 
805   {
806     const uint32_t big = 1000000;
807     r.CheckCallViaJS(0xDEADBEEF, big, 0, 0);
808     r.CheckCallViaJS(0xDEADBEEF, 0, big, 0);
809   }
810 
811   for (uint32_t big = 4294967295; big > 1000; big >>= 1) {
812     r.CheckCallViaJS(0xDEADBEEF, big, 0, 1);
813     r.CheckCallViaJS(0xDEADBEEF, 0, big, 1);
814     r.CheckCallViaJS(0xDEADBEEF, 0, 0, big);
815   }
816 
817   for (uint32_t big = -1000; big != 0; big <<= 1) {
818     r.CheckCallViaJS(0xDEADBEEF, big, 0, 1);
819     r.CheckCallViaJS(0xDEADBEEF, 0, big, 1);
820     r.CheckCallViaJS(0xDEADBEEF, 0, 0, big);
821   }
822 }
823 
WASM_COMPILED_EXEC_TEST(TableCopyOob1From0To0)824 WASM_COMPILED_EXEC_TEST(TableCopyOob1From0To0) {
825   TestTableCopyOob1(execution_tier, 0, 0);
826 }
827 
WASM_COMPILED_EXEC_TEST(TableCopyOob1From3To0)828 WASM_COMPILED_EXEC_TEST(TableCopyOob1From3To0) {
829   EXPERIMENTAL_FLAG_SCOPE(reftypes);
830   TestTableCopyOob1(execution_tier, 3, 0);
831 }
832 
WASM_COMPILED_EXEC_TEST(TableCopyOob1From5To9)833 WASM_COMPILED_EXEC_TEST(TableCopyOob1From5To9) {
834   EXPERIMENTAL_FLAG_SCOPE(reftypes);
835   TestTableCopyOob1(execution_tier, 5, 9);
836 }
837 
WASM_COMPILED_EXEC_TEST(TableCopyOob1From6To6)838 WASM_COMPILED_EXEC_TEST(TableCopyOob1From6To6) {
839   EXPERIMENTAL_FLAG_SCOPE(reftypes);
840   TestTableCopyOob1(execution_tier, 6, 6);
841 }
842 
WASM_COMPILED_EXEC_TEST(ElemDropTwice)843 WASM_COMPILED_EXEC_TEST(ElemDropTwice) {
844   WasmRunner<uint32_t> r(execution_tier);
845   r.builder().AddIndirectFunctionTable(nullptr, 1);
846   r.builder().AddPassiveElementSegment({});
847   BUILD(r, WASM_ELEM_DROP(0), kExprI32Const, 0);
848 
849   r.CheckCallViaJS(0);
850   r.CheckCallViaJS(0);
851 }
852 
WASM_COMPILED_EXEC_TEST(ElemDropThenTableInit)853 WASM_COMPILED_EXEC_TEST(ElemDropThenTableInit) {
854   WasmRunner<uint32_t, uint32_t> r(execution_tier);
855   r.builder().AddIndirectFunctionTable(nullptr, 1);
856   r.builder().AddPassiveElementSegment({});
857   BUILD(
858       r, WASM_ELEM_DROP(0),
859       WASM_TABLE_INIT(0, 0, WASM_I32V_1(0), WASM_I32V_1(0), WASM_LOCAL_GET(0)),
860       kExprI32Const, 0);
861 
862   r.CheckCallViaJS(0, 0);
863   r.CheckCallViaJS(0xDEADBEEF, 1);
864 }
865 
866 }  // namespace test_run_wasm_bulk_memory
867 }  // namespace wasm
868 }  // namespace internal
869 }  // namespace v8
870