• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #ifdef TEST_WASM
16 #include <fstream>
17 #include <iostream>
18 
19 #if defined(OHOS_JSVMTEST_XTS)
20 #define USE_HEADER_WASM
21 #endif
22 
23 #include "jsvmtest.h"
24 #include "securec.h"
25 #ifdef USE_HEADER_WASM
26 #include <unordered_map>
27 
28 #include "add.wasm.def"
29 #include "ammo.wasm.def"
30 #include "bfs.wasm.def"
31 #include "cJSON.wasm.def"
32 #include "global.wasm.def"
33 #include "illegal.wasm.def"
34 #include "illegal2.wasm.def"
35 #include "imports.wasm.def"
36 #include "laya.physics3D.wasm.def"
37 #include "math.wasm.def"
38 #include "memory.wasm.def"
39 #include "newton.wasm.def"
40 #include "reloc_info.wasm.def"
41 #include "simple.wasm.def"
42 #include "table.wasm.def"
43 #include "table2.wasm.def"
44 #endif
45 
46 #if defined(USE_HEADER_WASM)
47 static const std::string wasmPath = "";
48 
49 using WasmMap = std::unordered_map<std::string, std::pair<unsigned char *, unsigned int>>;
InitWasmMap()50 const WasmMap &InitWasmMap()
51 {
52     static WasmMap wasmMap{};
53     if (wasmMap.size() == 0) {
54         wasmMap["add.wasm"] = std::pair<unsigned char *, unsigned int>(add_wasm, add_wasm_len);
55         wasmMap["ammo.wasm.wasm"] = std::pair<unsigned char *, unsigned int>(ammo_wasm_wasm, ammo_wasm_wasm_len);
56         wasmMap["bfs.wasm"] = std::pair<unsigned char *, unsigned int>(bfs_wasm, bfs_wasm_len);
57         wasmMap["cJSON.wasm"] = std::pair<unsigned char *, unsigned int>(cJSON_wasm, cJSON_wasm_len);
58         wasmMap["global.wasm"] = std::pair<unsigned char *, unsigned int>(global_wasm, global_wasm_len);
59         wasmMap["illegal2.wasm"] = std::pair<unsigned char *, unsigned int>(illegal2_wasm, illegal2_wasm_len);
60         wasmMap["illegal.wasm"] = std::pair<unsigned char *, unsigned int>(illegal_wasm, illegal_wasm_len);
61         wasmMap["imports.wasm"] = std::pair<unsigned char *, unsigned int>(imports_wasm, imports_wasm_len);
62         wasmMap["laya.physics3D.wasm.wasm"] =
63             std::pair<unsigned char *, unsigned int>(laya_physics3D_wasm_wasm, laya_physics3D_wasm_wasm_len);
64         wasmMap["math.wasm"] = std::pair<unsigned char *, unsigned int>(math_wasm, math_wasm_len);
65         wasmMap["memory.wasm"] = std::pair<unsigned char *, unsigned int>(memory_wasm, memory_wasm_len);
66         wasmMap["newton.wasm"] = std::pair<unsigned char *, unsigned int>(newton_wasm, newton_wasm_len);
67         wasmMap["reloc_info.wasm"] = std::pair<unsigned char *, unsigned int>(reloc_info_wasm, reloc_info_wasm_len);
68         wasmMap["simple.wasm"] = std::pair<unsigned char *, unsigned int>(simple_wasm, simple_wasm_len);
69         wasmMap["table2.wasm"] = std::pair<unsigned char *, unsigned int>(table2_wasm, table2_wasm_len);
70         wasmMap["table.wasm"] = std::pair<unsigned char *, unsigned int>(table_wasm, table_wasm_len);
71     }
72     return wasmMap;
73 }
74 
75 #elif defined(OHOS_JSVM_HAP)
76 static const std::string wasmPath = "/data/storage/el1/base/wasm/";
77 #else
78 static const std::string wasmPath = "./unittests/wasm/";
79 #endif
80 
GetInstanceExports(JSVM_Value wasmInstance)81 static JSVM_Value GetInstanceExports(JSVM_Value wasmInstance)
82 {
83     CHECK(jsvm::IsWebAssemblyInstance(wasmInstance));
84     auto exports = jsvm::GetProperty(wasmInstance, "exports");
85     CHECK(jsvm::IsObject(exports));
86     return exports;
87 }
88 
ReadBinaryFile(const char * path,std::vector<uint8_t> & buffer)89 static void ReadBinaryFile(const char *path, std::vector<uint8_t> &buffer)
90 {
91 #if !defined(USE_HEADER_WASM)
92     std::ifstream infile(path, std::ifstream::binary);
93     CHECK_FATAL(infile.good(), "can not access file: %s\n", path);
94     infile.seekg(0, std::ifstream::end);
95     size_t size = infile.tellg();
96     infile.seekg(0);
97     buffer.resize(size);
98     infile.read(reinterpret_cast<char *>(&buffer[0]), size);
99     infile.close();
100 #else
101     const auto &wasmMap = InitWasmMap();
102     auto it = wasmMap.find(path);
103     if (it == wasmMap.end()) {
104         CHECK_FATAL(false, "can not find file: %s", path);
105     }
106     auto p = it->second;
107     std::vector<uint8_t> newBuffer((uint8_t *)p.first, p.first + p.second);
108     buffer.swap(newBuffer);
109 #endif
110 }
111 
ReadBinaryFile(const std::string & path,std::vector<uint8_t> & buffer)112 static void ReadBinaryFile(const std::string &path, std::vector<uint8_t> &buffer)
113 {
114     ReadBinaryFile(path.c_str(), buffer);
115 }
116 
WriteBinaryFile(const char * path,const uint8_t * data,size_t byteLength)117 static void WriteBinaryFile(const char *path, const uint8_t *data, size_t byteLength)
118 {
119     std::ofstream outfile(path, std::ofstream::binary);
120     outfile.write(reinterpret_cast<const char *>(data), byteLength);
121 }
122 
ReadTextFile(const char * path,std::string & content)123 static void ReadTextFile(const char *path, std::string &content)
124 {
125     std::ifstream infile(path);
126     CHECK_FATAL(infile.good(), "can not access file: %s\n", path);
127     std::ostringstream oss;
128     oss << infile.rdbuf();
129     content = oss.str();
130 }
131 
ReadTextFile(const std::string & path,std::string & content)132 [[maybe_unused]] static void ReadTextFile(const std::string &path, std::string &content)
133 {
134     ReadTextFile(path.c_str(), content);
135 }
136 
CompileWasmModule(const char * path)137 static JSVM_Value CompileWasmModule(const char *path)
138 {
139     std::vector<uint8_t> buffer;
140     ReadBinaryFile(path, buffer);
141     CHECK(buffer.size() > 0);
142     JSVM_Value wasmModule;
143     JSVMTEST_CALL(OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), NULL, 0, NULL, &wasmModule));
144     CHECK(jsvm::IsWasmModuleObject(wasmModule));
145     return wasmModule;
146 }
147 
CompileWasmModule(const std::string & path)148 static JSVM_Value CompileWasmModule(const std::string &path)
149 {
150     return CompileWasmModule(path.c_str());
151 }
152 
InstantiateWasmModule(JSVM_Value wasmModule,JSVM_Value importedObject)153 static JSVM_Value InstantiateWasmModule(JSVM_Value wasmModule, JSVM_Value importedObject)
154 {
155     auto globalThis = jsvm::Global();
156     auto WebAssembly = jsvm::GetProperty(globalThis, "WebAssembly");
157     CHECK(jsvm::IsObject(WebAssembly));
158     auto WebAssemblyInstance = jsvm::GetProperty(WebAssembly, "Instance");
159     CHECK(jsvm::IsFunction(WebAssemblyInstance));
160     JSVM_Value wasmInstance;
161     JSVM_Value argv[] = {wasmModule, importedObject};
162     constexpr size_t argc = 2;
163     JSVMTEST_CALL(OH_JSVM_NewInstance(jsvm_env, WebAssemblyInstance, argc, argv, &wasmInstance));
164     CHECK(jsvm::IsWebAssemblyInstance(wasmInstance));
165     return wasmInstance;
166 }
167 
168 // [begin, end)
BatchCompileWasmFunctions(JSVM_Value wasmModule,size_t begin,size_t end)169 static void BatchCompileWasmFunctions(JSVM_Value wasmModule, size_t begin, size_t end)
170 {
171     CHECK(jsvm::IsWasmModuleObject(wasmModule));
172     for (size_t i = begin; i < end; ++i) {
173         JSVM_WasmOptLevel level = (i % 2 == 0 ? JSVM_WASM_OPT_BASELINE : JSVM_WASM_OPT_HIGH);
174         JSVMTEST_CALL(OH_JSVM_CompileWasmFunction(jsvm_env, wasmModule, i, level));
175     }
176 }
177 
178 // 验证 wasm instance 功能 (add 模块)
VerifyAddWasmInstance(JSVM_Value instance)179 static void VerifyAddWasmInstance(JSVM_Value instance)
180 {
181     CHECK(jsvm::IsWebAssemblyInstance(instance));
182     // 实例化之后,就能使用 WebAssembly module 定义的函数了
183     auto exports = jsvm::GetProperty(instance, jsvm::Str("exports"));
184     CHECK(jsvm::IsObject(exports));
185     auto add = jsvm::GetProperty(exports, jsvm::Str("add"));
186     CHECK(jsvm::IsFunction(add));
187     auto result = jsvm::Call(add, jsvm::Undefined(), {jsvm::Run("1"), jsvm::Run("2")});
188     CHECK(jsvm::IsNumber(result));
189     constexpr int kExpectedResult = 3;
190     CHECK(jsvm::ToNumber(result) == kExpectedResult);
191 }
192 
WebAssemblyDemo()193 static void WebAssemblyDemo()
194 {
195     std::string path = wasmPath + "add.wasm";
196     std::vector<uint8_t> buffer;
197     // 从文件中读取 wasm 字节码
198     ReadBinaryFile(path, buffer);
199     JSVM_Status status = JSVM_OK;
200 
201     JSVM_Value wasmModule;
202     // 将 wasm 字节码编译为 WebAssembly module
203     status = OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), NULL, 0, NULL, &wasmModule);
204     CHECK(status == JSVM_OK);
205 
206     bool isWasmModule;
207     // 检查编译结果确实是一个 WebAssembly module
208     status = OH_JSVM_IsWasmModuleObject(jsvm_env, wasmModule, &isWasmModule);
209     CHECK(status == JSVM_OK);
210     CHECK(isWasmModule);
211 
212     // 对 WebAssembly module 指定编号的函数进行 high optimization level 优化
213     status = OH_JSVM_CompileWasmFunction(jsvm_env, wasmModule, 0, JSVM_WASM_OPT_HIGH);
214     CHECK(status == JSVM_OK);
215 
216     // 实例化 WebAssembly module
217     auto instance = InstantiateWasmModule(wasmModule, jsvm::Run("{}"));
218     VerifyAddWasmInstance(instance);
219 
220     // 对编译后的 WebAssembly moudle 进行序列化,创建 wasm cache
221     const uint8_t *cacheData = NULL;
222     size_t cacheLength = 0;
223     status = OH_JSVM_CreateWasmCache(jsvm_env, wasmModule, &cacheData, &cacheLength);
224     CHECK(status == JSVM_OK);
225     CHECK(cacheData != NULL);
226     CHECK(cacheLength > 0);
227 
228     // 对序列化得到的 cache data 进行持久化存储
229     const std::string wasmCachePath = path + ".cache";
230     WriteBinaryFile(wasmCachePath.c_str(), cacheData, cacheLength);
231     status = OH_JSVM_ReleaseCache(jsvm_env, cacheData, JSVM_CACHE_TYPE_WASM);
232     CHECK(status == JSVM_OK);
233 
234     // 下次使用时,直接使用 wasm cache 反序列化得到 WebAssembly module,避免编译开销
235     std::vector<uint8_t> cacheBuffer;
236     ReadBinaryFile(wasmCachePath, cacheBuffer);
237     CHECK(cacheBuffer.size() == cacheLength);
238 
239     bool cacheRejected;
240     JSVM_Value wasmModuleSerialized;
241     status = OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), cacheBuffer.data(), cacheBuffer.size(),
242                                        &cacheRejected, &wasmModuleSerialized);
243     CHECK(status == JSVM_OK);
244 
245     CHECK(cacheRejected == false);
246     status = OH_JSVM_IsWasmModuleObject(jsvm_env, wasmModuleSerialized, &isWasmModule);
247     CHECK(status == JSVM_OK);
248     CHECK(isWasmModule);
249 
250     // 测试反序列化而来的 WebAssembly module 的功能
251     // 实例化 WebAssembly module
252     auto instance2 = InstantiateWasmModule(wasmModuleSerialized, jsvm::Run("var x = {}; x"));
253     VerifyAddWasmInstance(instance2);
254 }
255 
256 // test for Wasm Is Wasm Module Object
TEST(Wasm_IsWasmModuleObject)257 TEST(Wasm_IsWasmModuleObject)
258 {
259     auto number = jsvm::Run("42");
260     CHECK(!jsvm::IsWasmModuleObject(number));
261     CHECK(!jsvm::IsWasmModuleObject(jsvm::Undefined()));
262     CHECK(!jsvm::IsWasmModuleObject(jsvm::Null()));
263     CHECK(!jsvm::IsWasmModuleObject(jsvm::Str("hello")));
264 }
265 
266 #ifdef OHOS_JSVMTEST_XTS
TEST_DISABLE(Wasm_WebAssemblyDemo)267 TEST_DISABLE(Wasm_WebAssemblyDemo)
268 {
269 #else
270 TEST(Wasm_WebAssemblyDemo)
271 {
272 #endif
273     WebAssemblyDemo();
274 }
275 
276 // test for Wasm Compile Wasm Module
277 TEST(Wasm_CompileWasmModule)
278 {
279     JSVM_Value wasmModule = CompileWasmModule(wasmPath + "add.wasm");
280     JSVM_Value wasmInstance = InstantiateWasmModule(wasmModule, jsvm::Run("{}"));
281     JSVM_Value exports = jsvm::GetProperty(wasmInstance, jsvm::Str("exports"));
282     CHECK(jsvm::IsObject(exports));
283     JSVM_Value addFunc = jsvm::GetProperty(exports, jsvm::Str("add"));
284     CHECK(jsvm::IsFunction(addFunc));
285     JSVM_Value one = jsvm::Run("1");
286     JSVM_Value two = jsvm::Run("2");
287     JSVM_Value result = jsvm::Call(addFunc, jsvm::Undefined(), {one, two});
288     CHECK(jsvm::ToNumber(result) == 3);
289 
290     JSVMTEST_CALL(OH_JSVM_CompileWasmFunction(env, wasmModule, 0, JSVM_WASM_OPT_HIGH));
291 
292     const uint8_t *cacheData = nullptr;
293     size_t cacheSize = 0;
294     JSVMTEST_CALL(OH_JSVM_CreateWasmCache(env, wasmModule, &cacheData, &cacheSize));
295     CHECK(cacheData != nullptr);
296     CHECK(cacheSize > 0);
297 }
298 
299 [[maybe_unused]] static void RandomDeserialize(JSVM_Env env, const std::string &filename)
300 {
301     static int cnt = 0;
302     std::string wasmFile = wasmPath + filename;
303     std::string cacheFile = wasmPath + filename + ".test.cache";
304     std::ifstream infile(cacheFile);
305     if (!infile.good()) {
306         // CreateCodeCache
307         auto wasmModule = CompileWasmModule(wasmFile);
308         InstantiateWasmModule(wasmModule, jsvm::Undefined());
309         JSVMTEST_CALL(OH_JSVM_CompileWasmFunction(env, wasmModule, 0, JSVM_WASM_OPT_HIGH));
310         const uint8_t *data = NULL;
311         size_t length = 0;
312         JSVMTEST_CALL(OH_JSVM_CreateWasmCache(env, wasmModule, &data, &length));
313         CHECK(data != NULL);
314         CHECK(length > 0);
315         WriteBinaryFile(cacheFile.c_str(), data, length);
316         JSVMTEST_CALL(OH_JSVM_ReleaseCache(env, data, JSVM_CACHE_TYPE_WASM));
317     } else {
318         std::vector<uint8_t> buffer;
319         ReadBinaryFile(wasmFile, buffer);
320         std::vector<uint8_t> cache;
321         ReadBinaryFile(cacheFile, cache);
322         JSVM_Value wasmModule;
323         bool cacheRejected;
324         JSVMTEST_CALL(OH_JSVM_CompileWasmModule(env, buffer.data(), buffer.size(), cache.data(), cache.size(),
325                                                 &cacheRejected, &wasmModule));
326         CHECK(cacheRejected == false);
327         CHECK(jsvm::IsWasmModuleObject(wasmModule));
328         // 运行一定次数后,删除缓存
329         constexpr int kPeriodToRemoveCache = 3;
330         if (++cnt % kPeriodToRemoveCache == 0) {
331             int result = remove(cacheFile.c_str());
332             CHECK(result == 0);
333         }
334     }
335 }
336 
337 // test for Wasm Random Cache Test
338 #ifdef OHOS_JSVMTEST_XTS
339 TEST_DISABLE(Wasm_RandomCacheTest)
340 {
341 #else
342 TEST(Wasm_RandomCacheTest)
343 {
344 #endif
345     static std::vector<std::string> allFiles = {
346         "add.wasm",
347         "bfs.wasm",
348         "cJSON.wasm",
349         "math.wasm",
350     };
351     for (const std::string &filename : allFiles) {
352         for (int i = 0; i < 100; ++i) {
353             RandomDeserialize(env, filename);
354         }
355     }
356 }
357 
358 // test for Wasm Compile Wasm Function Baseline
359 TEST(Wasm_CompileWasmFunctionBaseline)
360 {
361     auto wasmModule = CompileWasmModule(wasmPath + "add.wasm");
362     InstantiateWasmModule(wasmModule, jsvm::Undefined());
363     JSVMTEST_CALL(OH_JSVM_CompileWasmFunction(env, wasmModule, 0, JSVM_WASM_OPT_BASELINE));
364     JSVMTEST_CALL(OH_JSVM_CompileWasmFunction(env, wasmModule, 0, JSVM_WASM_OPT_HIGH));
365     auto status = OH_JSVM_CompileWasmFunction(env, wasmModule, 1, JSVM_WASM_OPT_HIGH);
366     CHECK(status != JSVM_OK);
367 }
368 
369 // test for Wasm Compile Illegal Wasm Module
370 TEST(Wasm_CompileIllegalWasmModule)
371 {
372     std::vector<uint8_t> buffer;
373     std::vector<std::string> pathVec = {
374         wasmPath + "illegal.wasm",
375         wasmPath + "illegal2.wasm",
376     };
377     for (const std::string &path : pathVec) {
378         ReadBinaryFile(path, buffer);
379         CHECK(buffer.size() > 0);
380         JSVM_Value wasmModule;
381         JSVM_Status status =
382             OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), NULL, 0, NULL, &wasmModule);
383         CHECK(status == JSVM_PENDING_EXCEPTION);
384     }
385 }
386 
387 // Test import global
388 static void TestImportGlobal(JSVM_Env env, JSVM_Value exports)
389 {
390     JSVM_Value testFunc;
391     JSVM_Value result;
392     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_global_i32"));
393     result = jsvm::Call(testFunc, jsvm::Undefined(), {});
394     CHECK(jsvm::Equals(result, jsvm::Run("100")));
395 
396     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_global_i64"));
397     result = jsvm::Call(testFunc, jsvm::Undefined(), {});
398     CHECK(jsvm::Equals(result, jsvm::Run("1000000000000000n")));
399 
400     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_global_f32"));
401     result = jsvm::Call(testFunc, jsvm::Undefined(), {});
402 
403     JSVM_ValueType type;
404     OH_JSVM_Typeof(env, result, &type);
405     CHECK(jsvm::Equals(result, jsvm::Run("new Float32Array([1.3])[0]")));
406 
407     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_global_f64"));
408     result = jsvm::Call(testFunc, jsvm::Undefined(), {});
409     CHECK(jsvm::Equals(result, jsvm::Run("2.7")));
410 }
411 
412 // Test import func
413 static void TestImportFunc(JSVM_Env env, JSVM_Value exports)
414 {
415     (void)env;
416     JSVM_Value testFunc;
417     JSVM_Value result;
418     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_sub"));
419     result = jsvm::Call(testFunc, jsvm::Undefined(), {jsvm::Run("1"), jsvm::Run("2")});
420     CHECK(jsvm::Equals(result, jsvm::Run("-1")));
421 }
422 
423 // Test import memory
424 static void TestImportMemory(JSVM_Env env, JSVM_Value exports)
425 {
426     JSVM_Value testFunc;
427     JSVM_Value result;
428     JSVM_Status status = JSVM_OK;
429     JSVM_Value exception;
430     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_memory"));
431     result = jsvm::Call(testFunc, jsvm::Undefined(), {jsvm::Run("0")});
432     CHECK(jsvm::Equals(result, jsvm::Run("0")));
433 
434     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_memory"));
435     result = jsvm::Call(testFunc, jsvm::Undefined(), {jsvm::Run("10")});
436     CHECK(jsvm::Equals(result, jsvm::Run("16")));
437 
438     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_memory"));
439     result = jsvm::Call(testFunc, jsvm::Undefined(), {jsvm::Run("8")});
440     CHECK(jsvm::Equals(result, jsvm::Run("1048576")));
441 
442     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_memory"));
443     JSVM_Value argv[1] = {jsvm::Run("1000000")};
444     status = OH_JSVM_CallFunction(jsvm_env, jsvm::Undefined(), testFunc, 1, argv, &result);
445     CHECK(status == JSVM_PENDING_EXCEPTION);
446     status = OH_JSVM_GetAndClearLastException(env, &exception);
447 }
448 
449 // Test import table
450 static void TestImportTable(JSVM_Env env, JSVM_Value exports)
451 {
452     JSVM_Value testFunc;
453     JSVM_Value result;
454     JSVM_Status status = JSVM_OK;
455     JSVM_Value exception;
456     {
457         testFunc = jsvm::GetProperty(exports, jsvm::Str("test_table"));
458         JSVM_Value argv[1] = {jsvm::Run("0")};
459         status = OH_JSVM_CallFunction(jsvm_env, jsvm::Undefined(), testFunc, 1, argv, &result);
460         CHECK(status == JSVM_PENDING_EXCEPTION);
461         status = OH_JSVM_GetAndClearLastException(env, &exception);
462     }
463 
464     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_table"));
465     result = jsvm::Call(testFunc, jsvm::Undefined(), {jsvm::Run("1")});
466     CHECK(jsvm::Equals(result, jsvm::Run("11")));
467 
468     testFunc = jsvm::GetProperty(exports, jsvm::Str("test_table"));
469     result = jsvm::Call(testFunc, jsvm::Undefined(), {jsvm::Run("2")});
470     CHECK(jsvm::Equals(result, jsvm::Run("22")));
471 
472     {
473         testFunc = jsvm::GetProperty(exports, jsvm::Str("test_table"));
474         JSVM_Value argv[1] = {jsvm::Run("3")};
475         status = OH_JSVM_CallFunction(jsvm_env, jsvm::Undefined(), testFunc, 1, argv, &result);
476         CHECK(status == JSVM_PENDING_EXCEPTION);
477         status = OH_JSVM_GetAndClearLastException(env, &exception);
478     }
479 
480     {
481         testFunc = jsvm::GetProperty(exports, jsvm::Str("test_table"));
482         JSVM_Value argv[1] = {jsvm::Run("100")};
483         status = OH_JSVM_CallFunction(jsvm_env, jsvm::Undefined(), testFunc, 1, argv, &result);
484         CHECK(status == JSVM_PENDING_EXCEPTION);
485         status = OH_JSVM_GetAndClearLastException(env, &exception);
486     }
487 }
488 
489 // test for Wasm Compile Wasm Module With Imports
490 TEST(Wasm_CompileWasmModuleWithImports)
491 {
492     JSVM_Value wasmModule = CompileWasmModule(wasmPath + "imports.wasm");
493     const char *importJs = R"JS(
494         var x = {
495             test: {
496                 global_i32: 100,
497                 global_i64: 1000000000000000n,
498                 global_f32: 1.3,
499                 global_f64: 2.7,
500                 sub: (a, b) => { return a - b; },
501                 memory: new WebAssembly.Memory({initial: 1, maximum: 2}),
502                 table: new WebAssembly.Table({initial: 10, maximum: 20, element: 'anyfunc'}),
503             }
504         };
505         x
506     )JS";
507 
508     JSVM_Value importedObj = jsvm::Run(importJs);
509 
510     CHECK(jsvm::IsObject(importedObj));
511     JSVM_Value wasmInstance = InstantiateWasmModule(wasmModule, importedObj);
512     JSVM_Value exports = jsvm::GetProperty(wasmInstance, jsvm::Str("exports"));
513     CHECK(jsvm::IsObject(exports));
514 
515     TestImportGlobal(env, exports);
516     TestImportFunc(env, exports);
517     TestImportMemory(env, exports);
518     TestImportTable(env, exports);
519 }
520 
521 static void CompileWasmFunctions(JSVM_Env env, JSVM_Value wasmModule,
522                                  const std::initializer_list<uint32_t> &funcIndexList, JSVM_WasmOptLevel optLevel)
523 {
524     for (uint32_t funcIndex : funcIndexList) {
525         JSVM_Status status = OH_JSVM_CompileWasmFunction(jsvm_env, wasmModule, funcIndex, optLevel);
526         CHECK(status == JSVM_OK);
527     }
528 }
529 
530 static JSVM_Value TestCacheAndRelocInfoStep1(JSVM_Env env, const std::vector<uint8_t> &buffer,
531                                              const uint8_t *&cacheData, size_t &cacheSize)
532 {
533     JSVM_Value wasmModule;
534     JSVMTEST_CALL(OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), NULL, 0, NULL, &wasmModule));
535     CHECK(jsvm::IsWasmModuleObject(wasmModule));
536 
537     JSVM_Value wasmInstance =
538         InstantiateWasmModule(wasmModule, jsvm::Run("var x = { test: {func: () => { return 1; }}}; x"));
539     JSVM_Value exports = jsvm::GetProperty(wasmInstance, jsvm::Str("exports"));
540     CHECK(jsvm::IsObject(exports));
541 
542     CompileWasmFunctions(env, wasmModule, {1, 2}, JSVM_WASM_OPT_HIGH);
543 
544     JSVM_Value testFunc;
545     JSVM_Value result;
546     {
547         testFunc = jsvm::GetProperty(exports, jsvm::Str("test_wasm_call_reloc_info"));
548         CHECK(jsvm::IsFunction(testFunc));
549         result = jsvm::Call(testFunc, jsvm::Undefined(), {});
550         CHECK(jsvm::Equals(result, jsvm::Run("1")));
551 
552         testFunc = jsvm::GetProperty(exports, jsvm::Str("test_wasm_stub_call_reloc_info"));
553         CHECK(jsvm::IsFunction(testFunc));
554         result = jsvm::Call(testFunc, jsvm::Undefined(), {});
555         CHECK(jsvm::Equals(result, jsvm::Run("1")));
556     }
557     JSVMTEST_CALL(OH_JSVM_CreateWasmCache(env, wasmModule, &cacheData, &cacheSize));
558     CHECK(cacheData != nullptr);
559     CHECK(cacheSize > 0);
560     return exports;
561 }
562 
563 static void TestCacheAndRelocInfoStep2(JSVM_Env env, const std::vector<uint8_t> &buffer, const uint8_t *cacheData,
564                                        size_t cacheSize, JSVM_Value exports)
565 {
566     bool cacheRejected;
567     JSVM_Value wasmModule;
568     JSVM_Status status = OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), cacheData, cacheSize,
569                                                    &cacheRejected, &wasmModule);
570     CHECK(status == JSVM_OK);
571     CHECK(cacheRejected == false);
572 
573     JSVM_Value wasmInstance =
574         InstantiateWasmModule(wasmModule, jsvm::Run("var x = { test: {func: () => { return 2; }}}; x"));
575 
576     JSVM_Value newExports = jsvm::GetProperty(wasmInstance, jsvm::Str("exports"));
577     JSVM_Value testFunc;
578     JSVM_Value result;
579 
580     {
581         testFunc = jsvm::GetProperty(newExports, jsvm::Str("test_wasm_call_reloc_info"));
582         CHECK(jsvm::IsFunction(testFunc));
583         result = jsvm::Call(testFunc, jsvm::Undefined(), {});
584         CHECK(jsvm::Equals(result, jsvm::Run("1")));
585 
586         testFunc = jsvm::GetProperty(newExports, jsvm::Str("test_wasm_stub_call_reloc_info"));
587         CHECK(jsvm::IsFunction(testFunc));
588         result = jsvm::Call(testFunc, jsvm::Undefined(), {});
589         CHECK(jsvm::Equals(result, jsvm::Run("1")));
590     }
591 
592     testFunc = jsvm::GetProperty(exports, jsvm::Str("call_import"));
593     CHECK(jsvm::IsFunction(testFunc));
594     result = jsvm::Call(testFunc, jsvm::Undefined(), {});
595     CHECK(jsvm::Equals(result, jsvm::Run("1")));
596 
597     testFunc = jsvm::GetProperty(newExports, jsvm::Str("call_import"));
598     CHECK(jsvm::IsFunction(testFunc));
599     result = jsvm::Call(testFunc, jsvm::Undefined(), {});
600     CHECK(jsvm::Equals(result, jsvm::Run("2")));
601 
602     CompileWasmFunctions(env, wasmModule, {1, 2}, JSVM_WASM_OPT_HIGH);
603     CompileWasmFunctions(env, wasmModule, {1, 2}, JSVM_WASM_OPT_HIGH);
604 
605     status = OH_JSVM_ReleaseCache(jsvm_env, cacheData, JSVM_CACHE_TYPE_WASM);
606     CHECK(status == JSVM_OK);
607 }
608 
609 // test for Wasm Compile Wasm Module With Cache And Reloc Info
610 TEST(Wasm_CompileWasmModuleWithCacheAndRelocInfo)
611 {
612     std::vector<uint8_t> buffer;
613     ReadBinaryFile(wasmPath + "reloc_info.wasm", buffer);
614     CHECK(buffer.size() > 0);
615 
616     const uint8_t *cacheData = NULL;
617     size_t cacheSize = 0;
618     JSVM_Value exports = TestCacheAndRelocInfoStep1(env, buffer, cacheData, cacheSize);
619     TestCacheAndRelocInfoStep2(env, buffer, cacheData, cacheSize, exports);
620 }
621 
622 // test for Wasm Multiple Instances
623 TEST(Wasm_MultipleInstances)
624 {
625     auto wasmModule = CompileWasmModule(wasmPath + "add.wasm");
626     {
627         auto obj = jsvm::Run("var x = { foo : 'bar' }; x");
628         CHECK(jsvm::IsObject(obj));
629         auto instance = InstantiateWasmModule(wasmModule, obj);
630         CHECK(jsvm::IsWebAssemblyInstance(instance));
631     }
632     {
633         auto obj = jsvm::Run("var x = { a : 1 }; x");
634         CHECK(jsvm::IsObject(obj));
635         auto instance = InstantiateWasmModule(wasmModule, obj);
636         CHECK(jsvm::IsWebAssemblyInstance(instance));
637     }
638     {
639         auto obj = jsvm::Run("var x = { b : 2 }; x");
640         CHECK(jsvm::IsObject(obj));
641         auto instance = InstantiateWasmModule(wasmModule, obj);
642         CHECK(jsvm::IsWebAssemblyInstance(instance));
643     }
644 }
645 
646 static std::string fakelog = "";
647 
648 static JSVM_Value FakeConsoleLog(JSVM_Env env, JSVM_CallbackInfo info)
649 {
650     size_t argc = 1;
651     JSVM_Value argv[1];
652     JSVMTEST_CALL(OH_JSVM_GetCbInfo(env, info, &argc, argv, NULL, NULL));
653     JSVM_Value x = argv[0];
654     fakelog = jsvm::ToString(x);
655     return jsvm::Undefined();
656 }
657 
658 static void InstallFakeConsoleLog(JSVM_Env env)
659 {
660     static JSVM_CallbackStruct cb = {FakeConsoleLog, NULL};
661     JSVM_Value log;
662     JSVMTEST_CALL(OH_JSVM_CreateFunction(env, "log", JSVM_AUTO_LENGTH, &cb, &log));
663     CHECK(jsvm::IsFunction(log));
664     auto console = jsvm::Global("console");
665     if (!jsvm::IsObject(console)) {
666         JSVMTEST_CALL(OH_JSVM_CreateObject(env, &console));
667         jsvm::SetProperty(jsvm::Global(), "console", console);
668     }
669     jsvm::SetProperty(console, "log", log);
670 }
671 
672 // test for Wasm MDN index
673 TEST(Wasm_MDN_index)
674 {
675     fakelog = "";
676     InstallFakeConsoleLog(env);
677     auto wasmModule = CompileWasmModule(wasmPath + "simple.wasm");
678     auto importObj = jsvm::Run(R"JS(
679         var importObject = {
680             my_namespace: {
681                 imported_func: arg => {
682                     console.log(arg);
683                 }
684             }
685         };
686         importObject;
687      )JS");
688     CHECK(jsvm::IsObject(importObj));
689     auto instance = InstantiateWasmModule(wasmModule, importObj);
690     auto exports = jsvm::GetProperty(instance, jsvm::Str("exports"));
691     auto exported_func = jsvm::GetProperty(exports, jsvm::Str("exported_func"));
692     CHECK(fakelog == "");
693     jsvm::Call(exported_func);  // expected console.log 42
694     CHECK(fakelog == "42");
695 }
696 
697 // test for Wasm MDN imports
698 TEST(Wasm_MDN_imports)
699 {
700     auto wasmModule = CompileWasmModule(wasmPath + "simple.wasm");
701     auto WebAssembly = jsvm::GetProperty(jsvm::Global(), jsvm::Str("WebAssembly"));
702     auto Module = jsvm::GetProperty(WebAssembly, jsvm::Str("Module"));
703     auto ModuleImports = jsvm::GetProperty(Module, jsvm::Str("imports"));
704     CHECK(jsvm::IsFunction(ModuleImports));
705     auto imports = jsvm::Call(ModuleImports, Module, {wasmModule});
706     CHECK(jsvm::IsArray(imports));
707     JSVM_Value element;
708     JSVMTEST_CALL(OH_JSVM_GetElement(env, imports, 0, &element));
709     CHECK(jsvm::IsObject(element));
710 }
711 
712 // test for Wasm MDN validate
713 TEST(Wasm_MDN_validate)
714 {
715     std::vector<uint8_t> buffer;
716     ReadBinaryFile(wasmPath + "simple.wasm", buffer);
717     CHECK(buffer.size() > 0);
718     void *backingStoreData = NULL;
719     JSVM_Value arraybuffer;
720     JSVMTEST_CALL(OH_JSVM_CreateArraybuffer(env, buffer.size(), &backingStoreData, &arraybuffer));
721     memcpy_s(backingStoreData, buffer.size(), buffer.data(), buffer.size());
722     CHECK(jsvm::IsArraybuffer(arraybuffer));
723 
724     auto WebAssembly = jsvm::Global("WebAssembly");
725     auto validate = jsvm::GetProperty(WebAssembly, "validate");
726     auto isValid = jsvm::Call(validate, WebAssembly, {arraybuffer});
727     CHECK(jsvm::IsBoolean(isValid));
728     CHECK(jsvm::StrictEquals(isValid, jsvm::True()));
729 }
730 
731 // test for Wasm MDN memory
732 TEST(Wasm_MDN_memory)
733 {
734     (void)jsvm::Run(R"JS(
735         var memory = new WebAssembly.Memory({
736             initial: 10,
737             maximum: 100,
738         });
739         memory
740     )JS");
741     auto x = jsvm::GetProperty(jsvm::Global(), jsvm::Str("memory"));
742     CHECK(jsvm::IsObject(x));
743     auto importObject = jsvm::Run(R"JS(
744         var importObject = {
745             js: {
746                 mem: memory
747             }
748         };
749         importObject
750     )JS");
751     auto wasmModule = CompileWasmModule(wasmPath + "memory.wasm");
752     auto instance = InstantiateWasmModule(wasmModule, importObject);
753 
754     jsvm::Run(R"JS(
755         var summands = new DataView(memory.buffer);
756         for (var i = 0; i < 10; ++i) {
757             summands.setUint32(i * 4, i, true);  // WebAssembly is little endian
758         }
759     )JS");
760 
761     auto exports = GetInstanceExports(instance);
762     auto accumulate = jsvm::GetProperty(exports, jsvm::Str("accumulate"));
763     CHECK(jsvm::IsFunction(accumulate));
764     auto result = jsvm::Call(accumulate, exports, {jsvm::Int32(0), jsvm::Int32(10)});
765     CHECK(jsvm::IsNumber(result));
766     CHECK(jsvm::ToNumber(result) == 45);
767 }
768 
769 // test for Wasm MDN table
770 TEST(Wasm_MDN_table)
771 {
772     auto table = jsvm::Run(R"JS(
773         var table = new WebAssembly.Table({
774             element: "anyfunc",
775             initial: 1,
776             maximum: 10
777         });
778         table.grow(1);
779         table;
780     )JS");
781     CHECK(jsvm::IsWebAssemblyTable(table));
782     auto wasmModule = CompileWasmModule(wasmPath + "table.wasm");
783     auto instance = InstantiateWasmModule(wasmModule, jsvm::Undefined());
784     auto exports = GetInstanceExports(instance);
785     auto tbl = jsvm::GetProperty(exports, jsvm::Str("tbl"));
786     jsvm::SetProperty(jsvm::Global(), jsvm::Str("tbl"), tbl);
787     auto r0 = jsvm::Run("tbl.get(0)()");
788     CHECK(jsvm::ToNumber(r0) == 13);
789     auto r1 = jsvm::Run("tbl.get(1)()");
790     CHECK(jsvm::ToNumber(r1) == 42);
791 }
792 
793 // test for Wasm MDN table2
794 TEST(Wasm_MDN_table2)
795 {
796     auto tbl = jsvm::Run(R"JS(
797         var tbl = new WebAssembly.Table({
798             initial: 2,
799             element: "anyfunc"
800         });
801         tbl;
802     )JS");
803     CHECK(jsvm::IsWebAssemblyTable(tbl));
804 
805     auto importObject = jsvm::Run(R"JS(
806         var importObject = {
807             js: { tbl }
808         };
809         importObject;
810     )JS");
811     CHECK(jsvm::IsObject(importObject));
812 
813     auto wasmModule = CompileWasmModule(wasmPath + "table2.wasm");
814     (void)InstantiateWasmModule(wasmModule, importObject);
815     auto r0 = jsvm::Run("tbl.get(0)()");
816     CHECK(jsvm::ToNumber(r0) == 42);
817     auto r1 = jsvm::Run("tbl.get(1)()");
818     CHECK(jsvm::ToNumber(r1) == 83);
819 }
820 
821 // test for Wasm MDN global
822 TEST(Wasm_MDN_global)
823 {
824     (void)jsvm::Run("var global = new WebAssembly.Global({ value: 'i32', mutable: true }, 0); global");
825     auto wasmModule = CompileWasmModule(wasmPath + "global.wasm");
826     auto importObject = jsvm::Run(R"JS(
827         var importObject = {
828             js: { global }
829         };
830         importObject
831     )JS");
832     auto instance = InstantiateWasmModule(wasmModule, importObject);
833     auto exports = GetInstanceExports(instance);
834     auto getGlobal = jsvm::GetProperty(exports, "getGlobal");
835     CHECK(jsvm::IsFunction(getGlobal));
836     auto r0 = jsvm::Call(getGlobal, exports, {});
837     CHECK(jsvm::ToNumber(r0) == 0);
838     jsvm::Run("global.value = 42");
839     auto r1 = jsvm::Call(getGlobal, exports, {});
840     CHECK(jsvm::ToNumber(r1) == 42);
841     auto incGlobal = jsvm::GetProperty(exports, "incGlobal");
842     jsvm::Call(incGlobal, exports, {});
843     auto r2 = jsvm::Call(getGlobal, exports, {});
844     CHECK(jsvm::ToNumber(r2) == 43);
845 }
846 
847 // test for Wasm Multiple Modules
848 TEST(Wasm_MultipleModules)
849 {
850     auto mathModule = CompileWasmModule(wasmPath + "math.wasm");
851     auto newtonModule = CompileWasmModule(wasmPath + "newton.wasm");
852     auto mathInstance = InstantiateWasmModule(mathModule, jsvm::Undefined());
853     auto mathExports = GetInstanceExports(mathInstance);
854     auto add = jsvm::GetProperty(mathExports, "add");
855     CHECK(jsvm::IsFunction(add));
856     auto sub = jsvm::GetProperty(mathExports, "sub");
857     CHECK(jsvm::IsFunction(sub));
858     jsvm::SetProperty(jsvm::Global(), jsvm::Str("add"), add);
859     jsvm::SetProperty(jsvm::Global(), jsvm::Str("sub"), sub);
860     auto mathObj = jsvm::Run(R"JS(
861         var mathObj = {
862             math: {
863                 add: add,
864                 sub, sub
865             }
866         };
867         mathObj
868     )JS");
869     CHECK(jsvm::IsObject(mathObj));
870     auto newtonInstance = InstantiateWasmModule(newtonModule, mathObj);
871     auto newtonExports = GetInstanceExports(newtonInstance);
872     auto calculate = jsvm::GetProperty(newtonExports, "calculate");
873     CHECK(jsvm::IsFunction(calculate));
874     auto result = jsvm::Call(calculate);
875     CHECK(jsvm::IsNumber(result));
876     CHECK(jsvm::ToNumber(result) == 42);
877 }
878 
879 void runCToWasm(JSVM_Env env, const std::string& filePath)
880 {
881     std::vector<uint8_t> buffer;
882     ReadBinaryFile(filePath, buffer);
883     CHECK(buffer.size() > 0);
884     JSVM_Value wasmModule;
885     JSVMTEST_CALL(OH_JSVM_CompileWasmModule(jsvm_env, buffer.data(), buffer.size(), NULL, 0, NULL, &wasmModule));
886     CHECK(jsvm::IsWasmModuleObject(wasmModule));
887 
888     JSVM_Value wasmInstance =
889         InstantiateWasmModule(wasmModule, jsvm::Run("var x = { test: {func: () => { return 1; }}}; x"));
890     JSVM_Value exports = jsvm::GetProperty(wasmInstance, jsvm::Str("exports"));
891     CHECK(jsvm::IsObject(exports));
892 
893     CompileWasmFunctions(env, wasmModule, {1, 2}, JSVM_WASM_OPT_HIGH);
894 
895     JSVM_Value testFunc;
896     JSVM_Value result;
897     {
898         testFunc = jsvm::GetProperty(exports, jsvm::Str("_start"));
899         CHECK(jsvm::IsFunction(testFunc));
900         result = jsvm::Call(testFunc, jsvm::Undefined(), {});
901         CHECK(jsvm::Equals(result, jsvm::Undefined()));
902     }
903 }
904 
905 // test for Wasm Run CTo Wasm Test
906 TEST(Wasm_RunCToWasmTest)
907 {
908     std::vector<std::string> pathVec = {
909         wasmPath + "bfs.wasm",
910         wasmPath + "cJSON.wasm",
911     };
912 
913     for (std::string &wasm : pathVec) {
914         runCToWasm(env, wasm);
915     }
916 }
917 
918 // test for Wasm Game Engine Ammo
919 TEST(Wasm_GameEngineAmmo)
920 {
921     std::string wasmFile = wasmPath + "ammo.wasm.wasm";
922     auto wasmModule = CompileWasmModule(wasmFile);
923     const char *str = R"JS(
924         var obj = {
925             f: () => {},
926             c: () => {},
927             d: () => {},
928             e: () => {},
929             b: () => {},
930             g: () => {},
931             a: () => {},
932             memory: new WebAssembly.Memory({initial: 1024, maximum: 1024}),
933             table: new WebAssembly.Table({ initial: 758, element: "anyfunc" })
934         };
935         var importObj = {
936             env: obj,
937             wasi_unstable: obj
938         };
939         importObj;
940     )JS";
941     auto importObj = jsvm::Run(str);
942     auto instance = InstantiateWasmModule(wasmModule, importObj);
943     CHECK(jsvm::IsWebAssemblyInstance(instance));
944     BatchCompileWasmFunctions(wasmModule, 7, 20);
945     BatchCompileWasmFunctions(wasmModule, 400, 420);
946     BatchCompileWasmFunctions(wasmModule, 1480, 1502);
947     auto exports = GetInstanceExports(instance);
948     CHECK(jsvm::IsObject(exports));
949 }
950 
951 // test for Wasm Game Engine Laya
952 TEST(Wasm_GameEngineLaya)
953 {
954     std::string wasmFile = wasmPath + "laya.physics3D.wasm.wasm";
955     auto wasmModule = CompileWasmModule(wasmFile);
956     const char *str = R"JS(
957         var importObj = {
958             env: {
959                 memory: new WebAssembly.Memory({initial: 2}),
960             },
961             wasi_unstable: {
962                 fd_close: () => {},
963                 fd_write: () => {},
964                 fd_seek: () => {},
965             },
966             LayaAirInteractive: {
967                 getWorldTransform: () => {},
968                 setWorldTransform: () => {},
969             }
970         };
971         importObj;
972     )JS";
973     auto importObj = jsvm::Run(str);
974     auto instance = InstantiateWasmModule(wasmModule, importObj);
975     CHECK(jsvm::IsWebAssemblyInstance(instance));
976     BatchCompileWasmFunctions(wasmModule, 5, 20);
977     BatchCompileWasmFunctions(wasmModule, 400, 420);
978     BatchCompileWasmFunctions(wasmModule, 1320, 1333);
979     auto exports = GetInstanceExports(instance);
980     CHECK(jsvm::IsObject(exports));
981 }
982 
983 #endif  // TEST_WASM
984