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