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