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