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 #include "jsvm_utils.h"
16
17 #include <cstdint>
18 #include <cstdio>
19 #include <cstring>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23
24 #include "jsvm.h"
25
Print(const char * str)26 void Print(const char* str)
27 {
28 printf("%s\n", str);
29 }
30
PrintException(JSVM_Env env,JSVM_Value exception,const char * call)31 void PrintException(JSVM_Env env, JSVM_Value exception, const char* call)
32 {
33 bool isObject = false;
34 JSVM_Status status = OH_JSVM_IsObject(env, exception, &isObject);
35 ASSERT_EQ(status, JSVM_OK);
36 ASSERT_TRUE(isObject);
37
38 JSVM_Value stack;
39 status = OH_JSVM_GetNamedProperty(env, exception, "stack", &stack);
40 ASSERT_EQ(status, JSVM_OK);
41 char stackStr[BUF_SIZE];
42 OH_JSVM_GetValueStringUtf8(env, stack, stackStr, BUF_SIZE, nullptr);
43 printf("[PrintException] exception.stack: %s\n", stackStr);
44
45 status = OH_JSVM_GetNamedProperty(env, exception, "message", &stack);
46 ASSERT_EQ(status, JSVM_OK);
47 char messageStr[BUF_SIZE];
48 OH_JSVM_GetValueStringUtf8(env, stack, messageStr, BUF_SIZE, nullptr);
49 printf("[PrintException] exception.message: %s\n", messageStr);
50 }
51
CheckErrorAndException(JSVM_Env env,JSVM_Status returnStatus,const char * call)52 void CheckErrorAndException(JSVM_Env env, JSVM_Status returnStatus, const char* call)
53 {
54 const JSVM_ExtendedErrorInfo* errorInfo;
55 JSVM_Status status = OH_JSVM_GetLastErrorInfo(env, &errorInfo);
56 ASSERT_EQ(status, JSVM_OK);
57 ASSERT_EQ(returnStatus, errorInfo->errorCode);
58 bool isPending = false;
59 status = OH_JSVM_IsExceptionPending(env, &isPending);
60 ASSERT_EQ(status, JSVM_OK);
61 JSVM_Value exception;
62 status = OH_JSVM_GetAndClearLastException(env, &exception);
63 ASSERT_EQ(status, JSVM_OK);
64 bool isExceptionUndefined = false;
65 status = OH_JSVM_IsUndefined(env, exception, &isExceptionUndefined);
66 ASSERT_EQ(status, JSVM_OK);
67 bool hasException = !isExceptionUndefined;
68 if (hasException) {
69 ASSERT_TRUE(isPending);
70 ASSERT_NE(returnStatus, JSVM_OK);
71 PrintException(env, exception, call);
72 ASSERT_TRUE(false);
73 } else {
74 // no exception
75 ASSERT_FALSE(isPending);
76 }
77 ASSERT_EQ(returnStatus, JSVM_OK);
78 }
79
80 // jsvm utils
81 namespace jsvm {
82
Str(const char * s)83 JSVM_Value Str(const char* s)
84 {
85 JSVM_Value str;
86 JSVMTEST_CALL(OH_JSVM_CreateStringUtf8(jsvm_env, s, JSVM_AUTO_LENGTH, &str));
87 return str;
88 }
89
Str(const std::string & stdString)90 JSVM_Value Str(const std::string& stdString)
91 {
92 return Str(stdString.c_str());
93 }
94
Compile(JSVM_Value js_str,const uint8_t * cache,size_t size)95 JSVM_Script Compile(JSVM_Value js_str, const uint8_t* cache, size_t size)
96 {
97 JSVM_Script script;
98 JSVMTEST_CALL(OH_JSVM_CompileScript(jsvm_env, js_str, cache, size, false, nullptr, &script));
99 return script;
100 }
101
CompileWithName(JSVM_Value js_str,const char * name)102 JSVM_Script CompileWithName(JSVM_Value js_str, const char* name)
103 {
104 JSVM_Script script;
105 JSVM_ScriptOrigin origin = {
106 .sourceMapUrl = nullptr,
107 .resourceName = name,
108 };
109 JSVMTEST_CALL(
110 OH_JSVM_CompileScriptWithOrigin(jsvm_env, js_str, NULL, JSVM_AUTO_LENGTH, false, NULL, &origin, &script));
111 return script;
112 }
113
Compile(const char * s,const uint8_t * cache,size_t size)114 JSVM_Script Compile(const char* s, const uint8_t* cache, size_t size)
115 {
116 return Compile(Str(s), cache, size);
117 }
118
CompileWithName(const char * s,const char * name)119 JSVM_Script CompileWithName(const char* s, const char* name)
120 {
121 return CompileWithName(Str(s), name);
122 }
123
Run(JSVM_Script script)124 JSVM_Value Run(JSVM_Script script)
125 {
126 JSVM_Value result;
127 JSVMTEST_CALL(OH_JSVM_RunScript(jsvm_env, script, &result));
128 return result;
129 }
130
Run(const char * s)131 JSVM_Value Run(const char* s)
132 {
133 return Run(Compile(s));
134 }
135
ToBoolean(JSVM_Value val)136 bool ToBoolean(JSVM_Value val)
137 {
138 JSVM_Value js_boolean;
139 JSVMTEST_CALL(OH_JSVM_CoerceToBool(jsvm_env, val, &js_boolean));
140 bool res;
141 JSVMTEST_CALL(OH_JSVM_GetValueBool(jsvm_env, js_boolean, &res));
142 return res;
143 }
144
ToNumber(JSVM_Value val)145 double ToNumber(JSVM_Value val)
146 {
147 JSVM_Value js_number;
148 JSVMTEST_CALL(OH_JSVM_CoerceToNumber(jsvm_env, val, &js_number));
149 double res;
150 JSVMTEST_CALL(OH_JSVM_GetValueDouble(jsvm_env, js_number, &res));
151 return res;
152 }
153
ToString(JSVM_Value val)154 std::string ToString(JSVM_Value val)
155 {
156 JSVM_Value js_string;
157 JSVMTEST_CALL(OH_JSVM_CoerceToString(jsvm_env, val, &js_string));
158 size_t length = 0;
159 JSVMTEST_CALL(OH_JSVM_GetValueStringUtf8(jsvm_env, js_string, NULL, 0, &length));
160 size_t capacity = length + 1;
161 char* buffer = new char[capacity];
162 size_t copyLength = 0;
163 JSVMTEST_CALL(OH_JSVM_GetValueStringUtf8(jsvm_env, js_string, buffer, capacity, ©Length));
164 std::string str(buffer);
165 delete[] buffer;
166 return str;
167 }
168
True()169 JSVM_Value True()
170 {
171 JSVM_Value js_true;
172 JSVMTEST_CALL(OH_JSVM_GetBoolean(jsvm_env, true, &js_true));
173 return js_true;
174 }
175
False()176 JSVM_Value False()
177 {
178 JSVM_Value js_false;
179 JSVMTEST_CALL(OH_JSVM_GetBoolean(jsvm_env, false, &js_false));
180 return js_false;
181 }
182
Undefined()183 JSVM_Value Undefined()
184 {
185 JSVM_Value js_undefined;
186 JSVMTEST_CALL(OH_JSVM_GetUndefined(jsvm_env, &js_undefined));
187 return js_undefined;
188 }
189
Null()190 JSVM_Value Null()
191 {
192 JSVM_Value js_null;
193 JSVMTEST_CALL(OH_JSVM_GetNull(jsvm_env, &js_null));
194 return js_null;
195 }
196
Int32(int32_t v)197 JSVM_Value Int32(int32_t v)
198 {
199 JSVM_Value result;
200 JSVMTEST_CALL(OH_JSVM_CreateInt32(jsvm_env, v, &result));
201 return result;
202 }
203
Int64(int64_t v)204 JSVM_Value Int64(int64_t v)
205 {
206 JSVM_Value result;
207 JSVMTEST_CALL(OH_JSVM_CreateInt64(jsvm_env, v, &result));
208 return result;
209 }
210
Uint32(uint32_t v)211 JSVM_Value Uint32(uint32_t v)
212 {
213 JSVM_Value result;
214 JSVMTEST_CALL(OH_JSVM_CreateUint32(jsvm_env, v, &result));
215 return result;
216 }
217
Double(double v)218 JSVM_Value Double(double v)
219 {
220 JSVM_Value result;
221 JSVMTEST_CALL(OH_JSVM_CreateDouble(jsvm_env, v, &result));
222 return result;
223 }
224
Object()225 JSVM_Value Object()
226 {
227 JSVM_Value js_object;
228 JSVMTEST_CALL(OH_JSVM_CreateObject(jsvm_env, &js_object));
229 return js_object;
230 }
231
Global()232 JSVM_Value Global()
233 {
234 JSVM_Value js_global;
235 JSVMTEST_CALL(OH_JSVM_GetGlobal(jsvm_env, &js_global));
236 return js_global;
237 }
238
239 // object property
GetProperty(JSVM_Value object,JSVM_Value key)240 JSVM_Value GetProperty(JSVM_Value object, JSVM_Value key)
241 {
242 JSVM_Value result;
243 JSVMTEST_CALL(OH_JSVM_GetProperty(jsvm_env, object, key, &result));
244 return result;
245 }
246
GetProperty(JSVM_Value object,const char * name)247 JSVM_Value GetProperty(JSVM_Value object, const char* name)
248 {
249 return GetProperty(object, Str(name));
250 }
251
SetProperty(JSVM_Value object,JSVM_Value key,JSVM_Value value)252 void SetProperty(JSVM_Value object, JSVM_Value key, JSVM_Value value)
253 {
254 JSVMTEST_CALL(OH_JSVM_SetProperty(jsvm_env, object, key, value));
255 }
256
SetProperty(JSVM_Value object,const char * name,JSVM_Value value)257 void SetProperty(JSVM_Value object, const char* name, JSVM_Value value)
258 {
259 JSVMTEST_CALL(OH_JSVM_SetProperty(jsvm_env, object, Str(name), value));
260 }
261
262 // Get named property with name `name` from globalThis
Global(const char * name)263 JSVM_Value Global(const char* name)
264 {
265 return GetProperty(Global(), Str(name));
266 }
267
268 // Get property with key `key` from globalThis
Global(JSVM_Value key)269 JSVM_Value Global(JSVM_Value key)
270 {
271 return GetProperty(Global(), key);
272 }
273
274 // Call function with normal function with empty argument list
Call(JSVM_Value func)275 JSVM_Value Call(JSVM_Value func)
276 {
277 JSVM_Value result;
278 JSVMTEST_CALL(OH_JSVM_CallFunction(jsvm_env, Undefined(), func, 0, NULL, &result));
279 return result;
280 }
281
282 // Call function with normal function with specified argument list
Call(JSVM_Value func,JSVM_Value thisArg,std::initializer_list<JSVM_Value> args)283 JSVM_Value Call(JSVM_Value func, JSVM_Value thisArg, std::initializer_list<JSVM_Value> args)
284 {
285 JSVM_Value result;
286 int argc = args.size();
287 if (argc == 0) {
288 JSVMTEST_CALL(OH_JSVM_CallFunction(jsvm_env, thisArg, func, 0, NULL, &result));
289 return result;
290 }
291 JSVM_Value argv[argc];
292 size_t i = 0;
293 for (JSVM_Value a : args) {
294 argv[i++] = a;
295 }
296 JSVMTEST_CALL(OH_JSVM_CallFunction(jsvm_env, thisArg, func, argc, argv, &result));
297 return result;
298 }
299
300 // Call function as constructor with empty argument list
New(JSVM_Value constructor)301 JSVM_Value New(JSVM_Value constructor)
302 {
303 JSVM_Value result;
304 JSVMTEST_CALL(OH_JSVM_NewInstance(jsvm_env, constructor, 0, NULL, &result));
305 return result;
306 }
307
InstanceOf(JSVM_Value value,JSVM_Value constructor)308 bool InstanceOf(JSVM_Value value, JSVM_Value constructor)
309 {
310 bool result;
311 JSVMTEST_CALL(OH_JSVM_Instanceof(jsvm_env, value, constructor, &result));
312 return result;
313 }
314
IsNull(JSVM_Value value)315 bool IsNull(JSVM_Value value)
316 {
317 bool result;
318 JSVMTEST_CALL(OH_JSVM_IsNull(jsvm_env, value, &result));
319 return result;
320 }
321
IsUndefined(JSVM_Value value)322 bool IsUndefined(JSVM_Value value)
323 {
324 bool result;
325 JSVMTEST_CALL(OH_JSVM_IsUndefined(jsvm_env, value, &result));
326 return result;
327 }
328
IsNullOrUndefined(JSVM_Value value)329 bool IsNullOrUndefined(JSVM_Value value)
330 {
331 bool result;
332 JSVMTEST_CALL(OH_JSVM_IsNullOrUndefined(jsvm_env, value, &result));
333 return result;
334 }
335
IsBoolean(JSVM_Value value)336 bool IsBoolean(JSVM_Value value)
337 {
338 bool result;
339 JSVMTEST_CALL(OH_JSVM_IsBoolean(jsvm_env, value, &result));
340 return result;
341 }
342
IsTrue(JSVM_Value value)343 bool IsTrue(JSVM_Value value)
344 {
345 return IsBoolean(value) && ToBoolean(value) == true;
346 }
347
IsFalse(JSVM_Value value)348 bool IsFalse(JSVM_Value value)
349 {
350 return IsBoolean(value) && ToBoolean(value) == false;
351 }
352
IsNumber(JSVM_Value value)353 bool IsNumber(JSVM_Value value)
354 {
355 bool result;
356 JSVMTEST_CALL(OH_JSVM_IsNumber(jsvm_env, value, &result));
357 return result;
358 }
359
IsString(JSVM_Value value)360 bool IsString(JSVM_Value value)
361 {
362 bool result;
363 JSVMTEST_CALL(OH_JSVM_IsString(jsvm_env, value, &result));
364 return result;
365 }
366
IsObject(JSVM_Value value)367 bool IsObject(JSVM_Value value)
368 {
369 bool result;
370 JSVMTEST_CALL(OH_JSVM_IsObject(jsvm_env, value, &result));
371 return result;
372 }
373
IsBigInt(JSVM_Value value)374 bool IsBigInt(JSVM_Value value)
375 {
376 bool result;
377 JSVMTEST_CALL(OH_JSVM_IsBigInt(jsvm_env, value, &result));
378 return result;
379 }
380
IsSymbol(JSVM_Value value)381 bool IsSymbol(JSVM_Value value)
382 {
383 bool result;
384 JSVMTEST_CALL(OH_JSVM_IsSymbol(jsvm_env, value, &result));
385 return result;
386 }
387
IsFunction(JSVM_Value value)388 bool IsFunction(JSVM_Value value)
389 {
390 bool result;
391 JSVMTEST_CALL(OH_JSVM_IsFunction(jsvm_env, value, &result));
392 return result;
393 }
394
IsArray(JSVM_Value value)395 bool IsArray(JSVM_Value value)
396 {
397 bool result;
398 JSVMTEST_CALL(OH_JSVM_IsArray(jsvm_env, value, &result));
399 return result;
400 }
401
IsArraybuffer(JSVM_Value value)402 bool IsArraybuffer(JSVM_Value value)
403 {
404 bool result;
405 JSVMTEST_CALL(OH_JSVM_IsArraybuffer(jsvm_env, value, &result));
406 return result;
407 }
408
IsPromise(JSVM_Value value)409 bool IsPromise(JSVM_Value value)
410 {
411 bool result;
412 JSVMTEST_CALL(OH_JSVM_IsPromise(jsvm_env, value, &result));
413 return result;
414 }
415
IsWasmModuleObject(JSVM_Value value)416 bool IsWasmModuleObject(JSVM_Value value)
417 {
418 bool result;
419 #ifdef TEST_WASM
420 JSVMTEST_CALL(OH_JSVM_IsWasmModuleObject(jsvm_env, value, &result));
421 #else
422 auto Module = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Module");
423 result = jsvm::InstanceOf(value, Module);
424 #endif
425 return result;
426 }
427
IsWebAssemblyInstance(JSVM_Value value)428 bool IsWebAssemblyInstance(JSVM_Value value)
429 {
430 auto Instance = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Instance");
431 return jsvm::InstanceOf(value, Instance);
432 }
433
IsWebAssemblyMemory(JSVM_Value value)434 bool IsWebAssemblyMemory(JSVM_Value value)
435 {
436 auto Memory = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Memory");
437 return jsvm::InstanceOf(value, Memory);
438 }
439
IsWebAssemblyTable(JSVM_Value value)440 bool IsWebAssemblyTable(JSVM_Value value)
441 {
442 auto Table = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Table");
443 return jsvm::InstanceOf(value, Table);
444 }
445
ArrayAt(JSVM_Value array,uint32_t index)446 JSVM_Value ArrayAt(JSVM_Value array, uint32_t index)
447 {
448 JSVM_Value element;
449 JSVMTEST_CALL(OH_JSVM_GetElement(jsvm_env, array, index, &element));
450 return element;
451 }
452
JsonParse(JSVM_Value str)453 JSVM_Value JsonParse(JSVM_Value str)
454 {
455 JSVM_Value obj;
456 JSVMTEST_CALL(OH_JSVM_JsonParse(jsvm_env, str, &obj));
457 return obj;
458 }
459
JsonStringify(JSVM_Value obj)460 JSVM_Value JsonStringify(JSVM_Value obj)
461 {
462 JSVM_Value str;
463 JSVMTEST_CALL(OH_JSVM_JsonStringify(jsvm_env, obj, &str));
464 return str;
465 }
466
Equals(JSVM_Value lhs,JSVM_Value rhs)467 bool Equals(JSVM_Value lhs, JSVM_Value rhs)
468 {
469 bool result;
470 JSVMTEST_CALL(OH_JSVM_Equals(jsvm_env, lhs, rhs, &result));
471 return result;
472 }
473
StrictEquals(JSVM_Value lhs,JSVM_Value rhs)474 bool StrictEquals(JSVM_Value lhs, JSVM_Value rhs)
475 {
476 bool result;
477 JSVMTEST_CALL(OH_JSVM_StrictEquals(jsvm_env, lhs, rhs, &result));
478 return result;
479 }
480
481 // This is a simple log function
MyConsoleLog(JSVM_Env env,JSVM_CallbackInfo info)482 JSVM_Value MyConsoleLog(JSVM_Env env, JSVM_CallbackInfo info)
483 {
484 size_t argc = 1;
485 JSVM_Value argv[1];
486 JSVMTEST_CALL(OH_JSVM_GetCbInfo(env, info, &argc, argv, NULL, NULL));
487 JSVM_Value x = argv[0];
488 std::string str = jsvm::ToString(x);
489 Print(str.c_str());
490 return jsvm::Undefined();
491 }
492
InstallMyConsoleLog(JSVM_Env env)493 void InstallMyConsoleLog(JSVM_Env env)
494 {
495 static JSVM_CallbackStruct cb = { MyConsoleLog, NULL };
496 JSVM_Value log;
497 JSVMTEST_CALL(OH_JSVM_CreateFunction(env, "log", JSVM_AUTO_LENGTH, &cb, &log));
498 ASSERT_TRUE(jsvm::IsFunction(log));
499 auto console = jsvm::Global("console");
500 if (!jsvm::IsObject(console)) {
501 JSVMTEST_CALL(OH_JSVM_CreateObject(env, &console));
502 jsvm::SetProperty(jsvm::Global(), "console", console);
503 }
504 jsvm::SetProperty(console, "log", log);
505 }
506
TryTriggerOOM()507 void TryTriggerOOM()
508 {
509 static std::vector<JSVM_Value> arrayVec;
510 int loopCount = 10000;
511 for (int i = 0; i < loopCount; i++) {
512 JSVM_Value array;
513 JSVMTEST_CALL(OH_JSVM_CreateArrayWithLength(jsvm_env, 0xffffff, &array));
514 arrayVec.push_back(array);
515 }
516 }
517
TryTriggerFatalError(JSVM_VM vm)518 void TryTriggerFatalError(JSVM_VM vm)
519 {
520 OH_JSVM_DestroyVM(vm);
521 }
522
TryTriggerGC()523 void TryTriggerGC()
524 {
525 JSVMTEST_CALL(OH_JSVM_MemoryPressureNotification(jsvm_env, JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL));
526 }
527 } // namespace jsvm
528