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
Global()276 JSVM_Value Global()
277 {
278 JSVM_Value js_global;
279 JSVMTEST_CALL(OH_JSVM_GetGlobal(jsvm_env, &js_global));
280 return js_global;
281 }
282
283 // object property
GetProperty(JSVM_Value object,JSVM_Value key)284 JSVM_Value GetProperty(JSVM_Value object, JSVM_Value key)
285 {
286 JSVM_Value result;
287 JSVMTEST_CALL(OH_JSVM_GetProperty(jsvm_env, object, key, &result));
288 return result;
289 }
290
GetProperty(JSVM_Value object,const char * name)291 JSVM_Value GetProperty(JSVM_Value object, const char *name)
292 {
293 return GetProperty(object, Str(name));
294 }
295
SetProperty(JSVM_Value object,JSVM_Value key,JSVM_Value value)296 void SetProperty(JSVM_Value object, JSVM_Value key, JSVM_Value value)
297 {
298 JSVMTEST_CALL(OH_JSVM_SetProperty(jsvm_env, object, key, value));
299 }
300
SetProperty(JSVM_Value object,const char * name,JSVM_Value value)301 void SetProperty(JSVM_Value object, const char *name, JSVM_Value value)
302 {
303 JSVMTEST_CALL(OH_JSVM_SetProperty(jsvm_env, object, Str(name), value));
304 }
305
306 // Get named property with name `name` from globalThis
Global(const char * name)307 JSVM_Value Global(const char *name)
308 {
309 return GetProperty(Global(), Str(name));
310 }
311
312 // Get property with key `key` from globalThis
Global(JSVM_Value key)313 JSVM_Value Global(JSVM_Value key)
314 {
315 return GetProperty(Global(), key);
316 }
317
318 // Call function with normal function with empty argument list
Call(JSVM_Value func)319 JSVM_Value Call(JSVM_Value func)
320 {
321 JSVM_Value result;
322 JSVMTEST_CALL(OH_JSVM_CallFunction(jsvm_env, Undefined(), func, 0, NULL, &result));
323 return result;
324 }
325
326 // Call function with normal function with specified argument list
Call(JSVM_Value func,JSVM_Value thisArg,std::initializer_list<JSVM_Value> args)327 JSVM_Value Call(JSVM_Value func, JSVM_Value thisArg, std::initializer_list<JSVM_Value> args)
328 {
329 JSVM_Value result;
330 int argc = args.size();
331 if (argc == 0) {
332 JSVMTEST_CALL(OH_JSVM_CallFunction(jsvm_env, thisArg, func, 0, NULL, &result));
333 return result;
334 }
335 JSVM_Value argv[argc];
336 size_t i = 0;
337 for (JSVM_Value a : args) {
338 argv[i++] = a;
339 }
340 JSVMTEST_CALL(OH_JSVM_CallFunction(jsvm_env, thisArg, func, argc, argv, &result));
341 return result;
342 }
343
344 // Call function as constructor with empty argument list
New(JSVM_Value constructor)345 JSVM_Value New(JSVM_Value constructor)
346 {
347 JSVM_Value result;
348 JSVMTEST_CALL(OH_JSVM_NewInstance(jsvm_env, constructor, 0, NULL, &result));
349 return result;
350 }
351
InstanceOf(JSVM_Value value,JSVM_Value constructor)352 bool InstanceOf(JSVM_Value value, JSVM_Value constructor)
353 {
354 bool result;
355 JSVMTEST_CALL(OH_JSVM_Instanceof(jsvm_env, value, constructor, &result));
356 return result;
357 }
358
IsNull(JSVM_Value value)359 bool IsNull(JSVM_Value value)
360 {
361 bool result;
362 JSVMTEST_CALL(OH_JSVM_IsNull(jsvm_env, value, &result));
363 return result;
364 }
365
IsUndefined(JSVM_Value value)366 bool IsUndefined(JSVM_Value value)
367 {
368 bool result;
369 JSVMTEST_CALL(OH_JSVM_IsUndefined(jsvm_env, value, &result));
370 return result;
371 }
372
IsNullOrUndefined(JSVM_Value value)373 bool IsNullOrUndefined(JSVM_Value value)
374 {
375 bool result;
376 JSVMTEST_CALL(OH_JSVM_IsNullOrUndefined(jsvm_env, value, &result));
377 return result;
378 }
379
IsBoolean(JSVM_Value value)380 bool IsBoolean(JSVM_Value value)
381 {
382 bool result;
383 JSVMTEST_CALL(OH_JSVM_IsBoolean(jsvm_env, value, &result));
384 return result;
385 }
386
IsTrue(JSVM_Value value)387 bool IsTrue(JSVM_Value value)
388 {
389 return IsBoolean(value) && ToBoolean(value) == true;
390 }
391
IsFalse(JSVM_Value value)392 bool IsFalse(JSVM_Value value)
393 {
394 return IsBoolean(value) && ToBoolean(value) == false;
395 }
396
IsNumber(JSVM_Value value)397 bool IsNumber(JSVM_Value value)
398 {
399 bool result;
400 JSVMTEST_CALL(OH_JSVM_IsNumber(jsvm_env, value, &result));
401 return result;
402 }
403
IsString(JSVM_Value value)404 bool IsString(JSVM_Value value)
405 {
406 bool result;
407 JSVMTEST_CALL(OH_JSVM_IsString(jsvm_env, value, &result));
408 return result;
409 }
410
IsObject(JSVM_Value value)411 bool IsObject(JSVM_Value value)
412 {
413 bool result;
414 JSVMTEST_CALL(OH_JSVM_IsObject(jsvm_env, value, &result));
415 return result;
416 }
417
IsBigInt(JSVM_Value value)418 bool IsBigInt(JSVM_Value value)
419 {
420 bool result;
421 JSVMTEST_CALL(OH_JSVM_IsBigInt(jsvm_env, value, &result));
422 return result;
423 }
424
IsSymbol(JSVM_Value value)425 bool IsSymbol(JSVM_Value value)
426 {
427 bool result;
428 JSVMTEST_CALL(OH_JSVM_IsSymbol(jsvm_env, value, &result));
429 return result;
430 }
431
IsFunction(JSVM_Value value)432 bool IsFunction(JSVM_Value value)
433 {
434 bool result;
435 JSVMTEST_CALL(OH_JSVM_IsFunction(jsvm_env, value, &result));
436 return result;
437 }
438
IsArray(JSVM_Value value)439 bool IsArray(JSVM_Value value)
440 {
441 bool result;
442 JSVMTEST_CALL(OH_JSVM_IsArray(jsvm_env, value, &result));
443 return result;
444 }
445
IsArraybuffer(JSVM_Value value)446 bool IsArraybuffer(JSVM_Value value)
447 {
448 bool result;
449 JSVMTEST_CALL(OH_JSVM_IsArraybuffer(jsvm_env, value, &result));
450 return result;
451 }
452
IsPromise(JSVM_Value value)453 bool IsPromise(JSVM_Value value)
454 {
455 bool result;
456 JSVMTEST_CALL(OH_JSVM_IsPromise(jsvm_env, value, &result));
457 return result;
458 }
459
IsWasmModuleObject(JSVM_Value value)460 bool IsWasmModuleObject(JSVM_Value value)
461 {
462 bool result;
463 #ifdef TEST_WASM
464 JSVMTEST_CALL(OH_JSVM_IsWasmModuleObject(jsvm_env, value, &result));
465 #else
466 auto Module = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Module");
467 result = jsvm::InstanceOf(value, Module);
468 #endif
469 return result;
470 }
471
IsWebAssemblyInstance(JSVM_Value value)472 bool IsWebAssemblyInstance(JSVM_Value value)
473 {
474 auto Instance = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Instance");
475 return jsvm::InstanceOf(value, Instance);
476 }
477
IsWebAssemblyMemory(JSVM_Value value)478 bool IsWebAssemblyMemory(JSVM_Value value)
479 {
480 auto Memory = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Memory");
481 return jsvm::InstanceOf(value, Memory);
482 }
483
IsWebAssemblyTable(JSVM_Value value)484 bool IsWebAssemblyTable(JSVM_Value value)
485 {
486 auto Table = jsvm::GetProperty(jsvm::Global("WebAssembly"), "Table");
487 return jsvm::InstanceOf(value, Table);
488 }
489
StringLength(JSVM_Value str)490 size_t StringLength(JSVM_Value str)
491 {
492 CHECK(IsString(str));
493 size_t length;
494 JSVMTEST_CALL(OH_JSVM_GetValueStringUtf8(jsvm_env, str, NULL, 0, &length));
495 return length;
496 }
497
ArrayLength(JSVM_Value array)498 uint32_t ArrayLength(JSVM_Value array)
499 {
500 CHECK(IsArray(array));
501 uint32_t length;
502 JSVMTEST_CALL(OH_JSVM_GetArrayLength(jsvm_env, array, &length));
503 return length;
504 }
505
ArrayAt(JSVM_Value array,uint32_t index)506 JSVM_Value ArrayAt(JSVM_Value array, uint32_t index)
507 {
508 JSVM_Value element;
509 JSVMTEST_CALL(OH_JSVM_GetElement(jsvm_env, array, index, &element));
510 return element;
511 }
512
JsonParse(JSVM_Value str)513 JSVM_Value JsonParse(JSVM_Value str)
514 {
515 JSVM_Value obj;
516 JSVMTEST_CALL(OH_JSVM_JsonParse(jsvm_env, str, &obj));
517 return obj;
518 }
519
JsonStringify(JSVM_Value obj)520 JSVM_Value JsonStringify(JSVM_Value obj)
521 {
522 JSVM_Value str;
523 JSVMTEST_CALL(OH_JSVM_JsonStringify(jsvm_env, obj, &str));
524 return str;
525 }
526
Equals(JSVM_Value lhs,JSVM_Value rhs)527 bool Equals(JSVM_Value lhs, JSVM_Value rhs)
528 {
529 bool result;
530 JSVMTEST_CALL(OH_JSVM_Equals(jsvm_env, lhs, rhs, &result));
531 return result;
532 }
533
StrictEquals(JSVM_Value lhs,JSVM_Value rhs)534 bool StrictEquals(JSVM_Value lhs, JSVM_Value rhs)
535 {
536 bool result;
537 JSVMTEST_CALL(OH_JSVM_StrictEquals(jsvm_env, lhs, rhs, &result));
538 return result;
539 }
540
541 // This is a simple log function
MyConsoleLog(JSVM_Env env,JSVM_CallbackInfo info)542 JSVM_Value MyConsoleLog(JSVM_Env env, JSVM_CallbackInfo info)
543 {
544 size_t argc = 1;
545 JSVM_Value argv[1];
546 JSVMTEST_CALL(OH_JSVM_GetCbInfo(env, info, &argc, argv, NULL, NULL));
547 JSVM_Value x = argv[0];
548 std::string str = jsvm::ToString(x);
549 Print(str.c_str());
550 return jsvm::Undefined();
551 }
552
InstallMyConsoleLog(JSVM_Env env)553 void InstallMyConsoleLog(JSVM_Env env)
554 {
555 static JSVM_CallbackStruct cb = {MyConsoleLog, NULL};
556 JSVM_Value log;
557 JSVMTEST_CALL(OH_JSVM_CreateFunction(env, "log", JSVM_AUTO_LENGTH, &cb, &log));
558 CHECK(jsvm::IsFunction(log));
559 auto console = jsvm::Global("console");
560 if (!jsvm::IsObject(console)) {
561 JSVMTEST_CALL(OH_JSVM_CreateObject(env, &console));
562 jsvm::SetProperty(jsvm::Global(), "console", console);
563 }
564 jsvm::SetProperty(console, "log", log);
565 }
566
567 } // namespace jsvm
568