@mikrojs/quickjs 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,985 @@
1
+ #ifdef NDEBUG
2
+ #undef NDEBUG
3
+ #endif
4
+ #include <assert.h>
5
+ #include <stdlib.h>
6
+ #include <string.h>
7
+ #include "quickjs.h"
8
+ #include "cutils.h"
9
+
10
+ static JSValue eval(JSContext *ctx, const char *code)
11
+ {
12
+ return JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_GLOBAL);
13
+ }
14
+
15
+ static JSValue cfunc_callback(JSContext *ctx, JSValueConst this_val,
16
+ int argc, JSValueConst *argv)
17
+ {
18
+ return JS_ThrowTypeError(ctx, "from cfunc");
19
+ }
20
+
21
+ static JSValue cfuncdata_callback(JSContext *ctx, JSValueConst this_val,
22
+ int argc, JSValueConst *argv,
23
+ int magic, JSValueConst *func_data)
24
+ {
25
+ return JS_ThrowTypeError(ctx, "from cfuncdata");
26
+ }
27
+
28
+ static JSValue cclosure_callback(JSContext *ctx, JSValueConst this_val,
29
+ int argc, JSValueConst *argv,
30
+ int magic, void *func_data)
31
+ {
32
+ return JS_ThrowTypeError(ctx, "from cclosure");
33
+ }
34
+
35
+ static bool closure_finalized = false;
36
+
37
+ static void cclosure_opaque_finalize(void *opaque)
38
+ {
39
+ if ((intptr_t)opaque == 12)
40
+ closure_finalized = true;
41
+ }
42
+
43
+ static void cfunctions(void)
44
+ {
45
+ uint32_t length;
46
+ const char *s;
47
+ JSValue ret, stack;
48
+
49
+ JSRuntime *rt = JS_NewRuntime();
50
+ JSContext *ctx = JS_NewContext(rt);
51
+ JSValue cfunc = JS_NewCFunction(ctx, cfunc_callback, "cfunc", 42);
52
+ JSValue cfuncdata =
53
+ JS_NewCFunctionData2(ctx, cfuncdata_callback, "cfuncdata",
54
+ /*length*/1337, /*magic*/0, /*data_len*/0, NULL);
55
+ JSValue cclosure =
56
+ JS_NewCClosure(ctx, cclosure_callback, "cclosure", cclosure_opaque_finalize,
57
+ /*length*/0xC0DE, /*magic*/11, /*opaque*/(void*)12);
58
+ JSValue global = JS_GetGlobalObject(ctx);
59
+ JS_SetPropertyStr(ctx, global, "cfunc", cfunc);
60
+ JS_SetPropertyStr(ctx, global, "cfuncdata", cfuncdata);
61
+ JS_SetPropertyStr(ctx, global, "cclosure", cclosure);
62
+ JS_FreeValue(ctx, global);
63
+
64
+ ret = eval(ctx, "cfunc.name");
65
+ assert(!JS_IsException(ret));
66
+ assert(JS_IsString(ret));
67
+ s = JS_ToCString(ctx, ret);
68
+ JS_FreeValue(ctx, ret);
69
+ assert(s);
70
+ assert(!strcmp(s, "cfunc"));
71
+ JS_FreeCString(ctx, s);
72
+ ret = eval(ctx, "cfunc.length");
73
+ assert(!JS_IsException(ret));
74
+ assert(JS_IsNumber(ret));
75
+ assert(0 == JS_ToUint32(ctx, &length, ret));
76
+ assert(length == 42);
77
+
78
+ ret = eval(ctx, "cfuncdata.name");
79
+ assert(!JS_IsException(ret));
80
+ assert(JS_IsString(ret));
81
+ s = JS_ToCString(ctx, ret);
82
+ JS_FreeValue(ctx, ret);
83
+ assert(s);
84
+ assert(!strcmp(s, "cfuncdata"));
85
+ JS_FreeCString(ctx, s);
86
+ ret = eval(ctx, "cfuncdata.length");
87
+ assert(!JS_IsException(ret));
88
+ assert(JS_IsNumber(ret));
89
+ assert(0 == JS_ToUint32(ctx, &length, ret));
90
+ assert(length == 1337);
91
+
92
+ ret = eval(ctx, "cclosure.name");
93
+ assert(!JS_IsException(ret));
94
+ assert(JS_IsString(ret));
95
+ s = JS_ToCString(ctx, ret);
96
+ JS_FreeValue(ctx, ret);
97
+ assert(s);
98
+ assert(!strcmp(s, "cclosure"));
99
+ JS_FreeCString(ctx, s);
100
+ ret = eval(ctx, "cclosure.length");
101
+ assert(!JS_IsException(ret));
102
+ assert(JS_IsNumber(ret));
103
+ assert(0 == JS_ToUint32(ctx, &length, ret));
104
+ assert(length == 0xC0DE);
105
+
106
+ ret = eval(ctx, "cfunc()");
107
+ assert(JS_IsException(ret));
108
+ ret = JS_GetException(ctx);
109
+ assert(JS_IsError(ret));
110
+ stack = JS_GetPropertyStr(ctx, ret, "stack");
111
+ assert(JS_IsString(stack));
112
+ s = JS_ToCString(ctx, stack);
113
+ JS_FreeValue(ctx, stack);
114
+ assert(s);
115
+ assert(!strcmp(s, " at cfunc (native)\n at <eval> (<input>:1:1)\n"));
116
+ JS_FreeCString(ctx, s);
117
+ s = JS_ToCString(ctx, ret);
118
+ JS_FreeValue(ctx, ret);
119
+ assert(s);
120
+ assert(!strcmp(s, "TypeError: from cfunc"));
121
+ JS_FreeCString(ctx, s);
122
+
123
+ ret = eval(ctx, "cfuncdata()");
124
+ assert(JS_IsException(ret));
125
+ ret = JS_GetException(ctx);
126
+ assert(JS_IsError(ret));
127
+ stack = JS_GetPropertyStr(ctx, ret, "stack");
128
+ assert(JS_IsString(stack));
129
+ s = JS_ToCString(ctx, stack);
130
+ JS_FreeValue(ctx, stack);
131
+ assert(s);
132
+ assert(!strcmp(s, " at cfuncdata (native)\n at <eval> (<input>:1:1)\n"));
133
+ JS_FreeCString(ctx, s);
134
+ s = JS_ToCString(ctx, ret);
135
+ JS_FreeValue(ctx, ret);
136
+ assert(s);
137
+ assert(!strcmp(s, "TypeError: from cfuncdata"));
138
+ JS_FreeCString(ctx, s);
139
+
140
+ ret = eval(ctx, "cclosure()");
141
+ assert(JS_IsException(ret));
142
+ ret = JS_GetException(ctx);
143
+ assert(JS_IsError(ret));
144
+ stack = JS_GetPropertyStr(ctx, ret, "stack");
145
+ assert(JS_IsString(stack));
146
+ s = JS_ToCString(ctx, stack);
147
+ JS_FreeValue(ctx, stack);
148
+ assert(s);
149
+ assert(!strcmp(s, " at cclosure (native)\n at <eval> (<input>:1:1)\n"));
150
+ JS_FreeCString(ctx, s);
151
+ s = JS_ToCString(ctx, ret);
152
+ JS_FreeValue(ctx, ret);
153
+ assert(s);
154
+ assert(!strcmp(s, "TypeError: from cclosure"));
155
+ JS_FreeCString(ctx, s);
156
+
157
+ JS_FreeContext(ctx);
158
+ JS_FreeRuntime(rt);
159
+
160
+ assert(closure_finalized);
161
+ }
162
+
163
+ #define MAX_TIME 10
164
+
165
+ static int timeout_interrupt_handler(JSRuntime *rt, void *opaque)
166
+ {
167
+ int *time = (int *)opaque;
168
+ if (*time <= MAX_TIME)
169
+ *time += 1;
170
+ return *time > MAX_TIME;
171
+ }
172
+
173
+ static void sync_call(void)
174
+ {
175
+ static const char code[] =
176
+ "(function() { \
177
+ try { \
178
+ while (true) {} \
179
+ } catch (e) {} \
180
+ })();";
181
+
182
+ JSRuntime *rt = JS_NewRuntime();
183
+ JSContext *ctx = JS_NewContext(rt);
184
+ int time = 0;
185
+ JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time);
186
+ JSValue ret = eval(ctx, code);
187
+ assert(time > MAX_TIME);
188
+ assert(JS_IsException(ret));
189
+ JS_FreeValue(ctx, ret);
190
+ assert(JS_HasException(ctx));
191
+ JSValue e = JS_GetException(ctx);
192
+ assert(JS_IsUncatchableError(e));
193
+ JS_FreeValue(ctx, e);
194
+ JS_FreeContext(ctx);
195
+ JS_FreeRuntime(rt);
196
+ }
197
+
198
+ static void async_call(void)
199
+ {
200
+ static const char code[] =
201
+ "(async function() { \
202
+ const loop = async () => { \
203
+ await Promise.resolve(); \
204
+ while (true) {} \
205
+ }; \
206
+ await loop().catch(() => {}); \
207
+ })();";
208
+
209
+ JSRuntime *rt = JS_NewRuntime();
210
+ JSContext *ctx = JS_NewContext(rt);
211
+ int time = 0;
212
+ JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time);
213
+ JSValue ret = eval(ctx, code);
214
+ assert(!JS_IsException(ret));
215
+ JS_FreeValue(ctx, ret);
216
+ assert(JS_IsJobPending(rt));
217
+ int r = 0;
218
+ while (JS_IsJobPending(rt)) {
219
+ r = JS_ExecutePendingJob(rt, &ctx);
220
+ }
221
+ assert(time > MAX_TIME);
222
+ assert(r == -1);
223
+ assert(JS_HasException(ctx));
224
+ JSValue e = JS_GetException(ctx);
225
+ assert(JS_IsUncatchableError(e));
226
+ JS_FreeValue(ctx, e);
227
+ JS_FreeContext(ctx);
228
+ JS_FreeRuntime(rt);
229
+ }
230
+
231
+ static JSValue save_value(JSContext *ctx, JSValueConst this_val,
232
+ int argc, JSValueConst *argv)
233
+ {
234
+ assert(argc == 1);
235
+ JSValue *p = (JSValue *)JS_GetContextOpaque(ctx);
236
+ *p = JS_DupValue(ctx, argv[0]);
237
+ return JS_UNDEFINED;
238
+ }
239
+
240
+ static void async_call_stack_overflow(void)
241
+ {
242
+ static const char code[] =
243
+ "(async function() { \
244
+ const f = () => f(); \
245
+ try { \
246
+ await Promise.resolve(); \
247
+ f(); \
248
+ } catch (e) { \
249
+ save_value(e); \
250
+ } \
251
+ })();";
252
+
253
+ JSRuntime *rt = JS_NewRuntime();
254
+ JSContext *ctx = JS_NewContext(rt);
255
+ JSValue value = JS_UNDEFINED;
256
+ JS_SetContextOpaque(ctx, &value);
257
+ JSValue global = JS_GetGlobalObject(ctx);
258
+ JS_SetPropertyStr(ctx, global, "save_value", JS_NewCFunction(ctx, save_value, "save_value", 1));
259
+ JS_FreeValue(ctx, global);
260
+ JSValue ret = eval(ctx, code);
261
+ assert(!JS_IsException(ret));
262
+ JS_FreeValue(ctx, ret);
263
+ assert(JS_IsJobPending(rt));
264
+ int r = 0;
265
+ while (JS_IsJobPending(rt)) {
266
+ r = JS_ExecutePendingJob(rt, &ctx);
267
+ }
268
+ assert(r == 1);
269
+ assert(!JS_HasException(ctx));
270
+ assert(JS_IsError(value)); // stack overflow should be caught
271
+ JS_FreeValue(ctx, value);
272
+ JS_FreeContext(ctx);
273
+ JS_FreeRuntime(rt);
274
+ }
275
+
276
+ // https://github.com/quickjs-ng/quickjs/issues/914
277
+ static void raw_context_global_var(void)
278
+ {
279
+ JSRuntime *rt = JS_NewRuntime();
280
+ JSContext *ctx = JS_NewContextRaw(rt);
281
+ JS_AddIntrinsicEval(ctx);
282
+ {
283
+ JSValue ret = eval(ctx, "globalThis");
284
+ assert(JS_IsException(ret));
285
+ JS_FreeValue(ctx, ret);
286
+ }
287
+ {
288
+ JSValue ret = eval(ctx, "var x = 42");
289
+ assert(JS_IsUndefined(ret));
290
+ JS_FreeValue(ctx, ret);
291
+ }
292
+ {
293
+ JSValue ret = eval(ctx, "function f() {}");
294
+ assert(JS_IsUndefined(ret));
295
+ JS_FreeValue(ctx, ret);
296
+ }
297
+ JS_FreeContext(ctx);
298
+ JS_FreeRuntime(rt);
299
+ }
300
+
301
+ static void is_array(void)
302
+ {
303
+ JSRuntime *rt = JS_NewRuntime();
304
+ JSContext *ctx = JS_NewContext(rt);
305
+ {
306
+ JSValue ret = eval(ctx, "[]");
307
+ assert(!JS_IsException(ret));
308
+ assert(JS_IsArray(ret));
309
+ JS_FreeValue(ctx, ret);
310
+ }
311
+ {
312
+ JSValue ret = eval(ctx, "new Proxy([], {})");
313
+ assert(!JS_IsException(ret));
314
+ assert(!JS_IsArray(ret));
315
+ assert(JS_IsProxy(ret));
316
+ JSValue handler = JS_GetProxyHandler(ctx, ret);
317
+ JSValue target = JS_GetProxyTarget(ctx, ret);
318
+ assert(!JS_IsException(handler));
319
+ assert(!JS_IsException(target));
320
+ assert(!JS_IsProxy(handler));
321
+ assert(!JS_IsProxy(target));
322
+ assert(JS_IsObject(handler));
323
+ assert(JS_IsArray(target));
324
+ JS_FreeValue(ctx, handler);
325
+ JS_FreeValue(ctx, target);
326
+ JS_FreeValue(ctx, ret);
327
+ }
328
+ JS_FreeContext(ctx);
329
+ JS_FreeRuntime(rt);
330
+ }
331
+
332
+ static int loader_calls;
333
+
334
+ static JSModuleDef *loader(JSContext *ctx, const char *name, void *opaque)
335
+ {
336
+ loader_calls++;
337
+ assert(!strcmp(name, "b"));
338
+ static const char code[] = "export function f(x){}";
339
+ JSValue ret = JS_Eval(ctx, code, strlen(code), "b",
340
+ JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY);
341
+ assert(!JS_IsException(ret));
342
+ JSModuleDef *m = JS_VALUE_GET_PTR(ret);
343
+ assert(m);
344
+ JS_FreeValue(ctx, ret);
345
+ return m;
346
+ }
347
+
348
+ static void module_serde(void)
349
+ {
350
+ JSRuntime *rt = JS_NewRuntime();
351
+ //JS_SetDumpFlags(rt, JS_DUMP_MODULE_RESOLVE);
352
+ JS_SetModuleLoaderFunc(rt, NULL, loader, NULL);
353
+ JSContext *ctx = JS_NewContext(rt);
354
+ static const char code[] = "import {f} from 'b'; f()";
355
+ assert(loader_calls == 0);
356
+ JSValue mod = JS_Eval(ctx, code, strlen(code), "a",
357
+ JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY);
358
+ assert(loader_calls == 1);
359
+ assert(!JS_IsException(mod));
360
+ assert(JS_IsModule(mod));
361
+ size_t len = 0;
362
+ uint8_t *buf = JS_WriteObject(ctx, &len, mod,
363
+ JS_WRITE_OBJ_BYTECODE|JS_WRITE_OBJ_REFERENCE);
364
+ assert(buf);
365
+ assert(len > 0);
366
+ JS_FreeValue(ctx, mod);
367
+ assert(loader_calls == 1);
368
+ mod = JS_ReadObject(ctx, buf, len, JS_READ_OBJ_BYTECODE);
369
+ js_free(ctx, buf);
370
+ assert(loader_calls == 1); // 'b' is returned from cache
371
+ assert(!JS_IsException(mod));
372
+ JSValue ret = JS_EvalFunction(ctx, mod);
373
+ assert(!JS_IsException(ret));
374
+ assert(JS_IsPromise(ret));
375
+ JSValue result = JS_PromiseResult(ctx, ret);
376
+ assert(!JS_IsException(result));
377
+ assert(JS_IsUndefined(result));
378
+ JS_FreeValue(ctx, result);
379
+ JS_FreeValue(ctx, ret);
380
+ JS_FreeValue(ctx, mod);
381
+ JS_FreeContext(ctx);
382
+ JS_FreeRuntime(rt);
383
+ }
384
+
385
+ static void runtime_cstring_free(void)
386
+ {
387
+ JSRuntime *rt = JS_NewRuntime();
388
+ JSContext *ctx = JS_NewContext(rt);
389
+ // string -> cstring + JS_FreeCStringRT
390
+ {
391
+ JSValue ret = eval(ctx, "\"testStringPleaseIgnore\"");
392
+ assert(JS_IsString(ret));
393
+ const char *s = JS_ToCString(ctx, ret);
394
+ assert(s);
395
+ assert(strcmp(s, "testStringPleaseIgnore") == 0);
396
+ JS_FreeCStringRT(rt, s);
397
+ JS_FreeValue(ctx, ret);
398
+ }
399
+ // string -> cstring + JS_FreeCStringRT, destroying the source value first
400
+ {
401
+ JSValue ret = eval(ctx, "\"testStringPleaseIgnore\"");
402
+ assert(JS_IsString(ret));
403
+ const char *s = JS_ToCString(ctx, ret);
404
+ assert(s);
405
+ JS_FreeValue(ctx, ret);
406
+ assert(strcmp(s, "testStringPleaseIgnore") == 0);
407
+ JS_FreeCStringRT(rt, s);
408
+ }
409
+ // number -> cstring + JS_FreeCStringRT
410
+ {
411
+ JSValue ret = eval(ctx, "123987");
412
+ assert(JS_IsNumber(ret));
413
+ const char *s = JS_ToCString(ctx, ret);
414
+ assert(s);
415
+ assert(strcmp(s, "123987") == 0);
416
+ JS_FreeCStringRT(rt, s);
417
+ JS_FreeValue(ctx, ret);
418
+ }
419
+ JS_FreeContext(ctx);
420
+ JS_FreeRuntime(rt);
421
+ }
422
+
423
+ static void utf16_string(void)
424
+ {
425
+ JSRuntime *rt = JS_NewRuntime();
426
+ JSContext *ctx = JS_NewContext(rt);
427
+ {
428
+ JSValue v = JS_NewStringUTF16(ctx, NULL, 0);
429
+ assert(!JS_IsException(v));
430
+ const char *s = JS_ToCString(ctx, v);
431
+ assert(s);
432
+ assert(!strcmp(s, ""));
433
+ JS_FreeCString(ctx, s);
434
+ JS_FreeValue(ctx, v);
435
+ }
436
+ {
437
+ JSValue v = JS_NewStringUTF16(ctx, (uint16_t[]){'o','k'}, 2);
438
+ assert(!JS_IsException(v));
439
+ const char *s = JS_ToCString(ctx, v);
440
+ assert(s);
441
+ assert(!strcmp(s, "ok"));
442
+ JS_FreeCString(ctx, s);
443
+ size_t n;
444
+ const uint16_t *u = JS_ToCStringLenUTF16(ctx, &n, v);
445
+ assert(u);
446
+ assert(n == 2);
447
+ assert(u[0] == 'o');
448
+ assert(u[1] == 'k');
449
+ JS_FreeCStringUTF16(ctx, u);
450
+ JS_FreeValue(ctx, v);
451
+ }
452
+ {
453
+ JSValue v = JS_NewStringUTF16(ctx, (uint16_t[]){0xD800}, 1);
454
+ assert(!JS_IsException(v));
455
+ const char *s = JS_ToCString(ctx, v);
456
+ assert(s);
457
+ // questionable but surrogates don't map to UTF-8 without WTF-8
458
+ assert(!strcmp(s, "\xED\xA0\x80"));
459
+ JS_FreeCString(ctx, s);
460
+ size_t n;
461
+ const uint16_t *u = JS_ToCStringLenUTF16(ctx, &n, v);
462
+ assert(u);
463
+ assert(n == 1);
464
+ assert(u[0] == 0xD800);
465
+ JS_FreeCStringUTF16(ctx, u);
466
+ JS_FreeValue(ctx, v);
467
+ }
468
+ {
469
+ JSValue v = JS_NewStringLen(ctx, "ok", 2); // ascii -> ucs
470
+ assert(!JS_IsException(v));
471
+ size_t n;
472
+ const uint16_t *u = JS_ToCStringLenUTF16(ctx, &n, v);
473
+ assert(u);
474
+ assert(n == 2);
475
+ assert(u[0] == 'o');
476
+ assert(u[1] == 'k');
477
+ JS_FreeCStringUTF16(ctx, u);
478
+ JS_FreeValue(ctx, v);
479
+ }
480
+ JS_FreeContext(ctx);
481
+ JS_FreeRuntime(rt);
482
+ }
483
+
484
+ static void weak_map_gc_check(void)
485
+ {
486
+ static const char init_code[] =
487
+ "const map = new WeakMap(); \
488
+ function addItem() { \
489
+ const k = { \
490
+ text: 'a', \
491
+ }; \
492
+ map.set(k, {k}); \
493
+ }";
494
+ static const char test_code[] = "addItem()";
495
+
496
+ JSRuntime *rt = JS_NewRuntime();
497
+ JSContext *ctx = JS_NewContext(rt);
498
+
499
+ JSValue ret = eval(ctx, init_code);
500
+ assert(!JS_IsException(ret));
501
+
502
+ JSValue ret_test = eval(ctx, test_code);
503
+ assert(!JS_IsException(ret_test));
504
+ JS_RunGC(rt);
505
+ JSMemoryUsage memory_usage;
506
+ JS_ComputeMemoryUsage(rt, &memory_usage);
507
+
508
+ for (int i = 0; i < 3; i++) {
509
+ JSValue ret_test2 = eval(ctx, test_code);
510
+ assert(!JS_IsException(ret_test2));
511
+ JS_RunGC(rt);
512
+ JSMemoryUsage memory_usage2;
513
+ JS_ComputeMemoryUsage(rt, &memory_usage2);
514
+
515
+ assert(memory_usage.memory_used_count == memory_usage2.memory_used_count);
516
+ assert(memory_usage.memory_used_size == memory_usage2.memory_used_size);
517
+ JS_FreeValue(ctx, ret_test2);
518
+ }
519
+
520
+ JS_FreeValue(ctx, ret);
521
+ JS_FreeValue(ctx, ret_test);
522
+ JS_FreeContext(ctx);
523
+ JS_FreeRuntime(rt);
524
+ }
525
+
526
+ struct {
527
+ int hook_type_call_count[4];
528
+ } promise_hook_state;
529
+
530
+ static void promise_hook_cb(JSContext *ctx, JSPromiseHookType type,
531
+ JSValueConst promise, JSValueConst parent_promise,
532
+ void *opaque)
533
+ {
534
+ assert(type == JS_PROMISE_HOOK_INIT ||
535
+ type == JS_PROMISE_HOOK_BEFORE ||
536
+ type == JS_PROMISE_HOOK_AFTER ||
537
+ type == JS_PROMISE_HOOK_RESOLVE);
538
+ promise_hook_state.hook_type_call_count[type]++;
539
+ assert(opaque == (void *)&promise_hook_state);
540
+ if (!JS_IsUndefined(parent_promise)) {
541
+ JSValue global_object = JS_GetGlobalObject(ctx);
542
+ JS_SetPropertyStr(ctx, global_object, "actual",
543
+ JS_DupValue(ctx, parent_promise));
544
+ JS_FreeValue(ctx, global_object);
545
+ }
546
+ }
547
+
548
+ static void promise_hook(void)
549
+ {
550
+ int *cc = promise_hook_state.hook_type_call_count;
551
+ JSContext *unused;
552
+ JSRuntime *rt = JS_NewRuntime();
553
+ //JS_SetDumpFlags(rt, JS_DUMP_PROMISE);
554
+ JS_SetPromiseHook(rt, promise_hook_cb, &promise_hook_state);
555
+ JSContext *ctx = JS_NewContext(rt);
556
+ JSValue global_object = JS_GetGlobalObject(ctx);
557
+ {
558
+ // empty module; creates an outer and inner module promise;
559
+ // JS_Eval returns the outer promise
560
+ JSValue ret = JS_Eval(ctx, "", 0, "<input>", JS_EVAL_TYPE_MODULE);
561
+ assert(!JS_IsException(ret));
562
+ assert(JS_IsPromise(ret));
563
+ assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret));
564
+ JS_FreeValue(ctx, ret);
565
+ assert(2 == cc[JS_PROMISE_HOOK_INIT]);
566
+ assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
567
+ assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
568
+ assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]);
569
+ assert(!JS_IsJobPending(rt));
570
+ }
571
+ memset(&promise_hook_state, 0, sizeof(promise_hook_state));
572
+ {
573
+ // module with unresolved promise; the outer and inner module promises
574
+ // are resolved but not the user's promise
575
+ static const char code[] = "new Promise(() => {})";
576
+ JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
577
+ assert(!JS_IsException(ret));
578
+ assert(JS_IsPromise(ret));
579
+ assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
580
+ JS_FreeValue(ctx, ret);
581
+ assert(3 == cc[JS_PROMISE_HOOK_INIT]);
582
+ assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
583
+ assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
584
+ assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); // outer and inner module promise
585
+ assert(!JS_IsJobPending(rt));
586
+ }
587
+ memset(&promise_hook_state, 0, sizeof(promise_hook_state));
588
+ {
589
+ // module with resolved promise
590
+ static const char code[] = "new Promise((resolve,reject) => resolve())";
591
+ JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
592
+ assert(!JS_IsException(ret));
593
+ assert(JS_IsPromise(ret));
594
+ assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
595
+ JS_FreeValue(ctx, ret);
596
+ assert(3 == cc[JS_PROMISE_HOOK_INIT]);
597
+ assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
598
+ assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
599
+ assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]);
600
+ assert(!JS_IsJobPending(rt));
601
+ }
602
+ memset(&promise_hook_state, 0, sizeof(promise_hook_state));
603
+ {
604
+ // module with rejected promise
605
+ static const char code[] = "new Promise((resolve,reject) => reject())";
606
+ JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
607
+ assert(!JS_IsException(ret));
608
+ assert(JS_IsPromise(ret));
609
+ assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
610
+ JS_FreeValue(ctx, ret);
611
+ assert(3 == cc[JS_PROMISE_HOOK_INIT]);
612
+ assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
613
+ assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
614
+ assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]);
615
+ assert(!JS_IsJobPending(rt));
616
+ }
617
+ memset(&promise_hook_state, 0, sizeof(promise_hook_state));
618
+ {
619
+ // module with promise chain
620
+ static const char code[] =
621
+ "globalThis.count = 0;"
622
+ "globalThis.actual = undefined;" // set by promise_hook_cb
623
+ "globalThis.expected = new Promise(resolve => resolve());"
624
+ "expected.then(_ => count++)";
625
+ JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
626
+ assert(!JS_IsException(ret));
627
+ assert(JS_IsPromise(ret));
628
+ assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
629
+ JS_FreeValue(ctx, ret);
630
+ assert(4 == cc[JS_PROMISE_HOOK_INIT]);
631
+ assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
632
+ assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
633
+ assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]);
634
+ JSValue v = JS_GetPropertyStr(ctx, global_object, "count");
635
+ assert(!JS_IsException(v));
636
+ int32_t count;
637
+ assert(0 == JS_ToInt32(ctx, &count, v));
638
+ assert(0 == count);
639
+ JS_FreeValue(ctx, v);
640
+ assert(JS_IsJobPending(rt));
641
+ assert(1 == JS_ExecutePendingJob(rt, &unused));
642
+ assert(!JS_HasException(ctx));
643
+ assert(4 == cc[JS_PROMISE_HOOK_INIT]);
644
+ assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
645
+ assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
646
+ assert(4 == cc[JS_PROMISE_HOOK_RESOLVE]);
647
+ assert(!JS_IsJobPending(rt));
648
+ v = JS_GetPropertyStr(ctx, global_object, "count");
649
+ assert(!JS_IsException(v));
650
+ assert(0 == JS_ToInt32(ctx, &count, v));
651
+ assert(1 == count);
652
+ JS_FreeValue(ctx, v);
653
+ JSValue actual = JS_GetPropertyStr(ctx, global_object, "actual");
654
+ JSValue expected = JS_GetPropertyStr(ctx, global_object, "expected");
655
+ assert(!JS_IsException(actual));
656
+ assert(!JS_IsException(expected));
657
+ assert(JS_IsSameValue(ctx, actual, expected));
658
+ JS_FreeValue(ctx, actual);
659
+ JS_FreeValue(ctx, expected);
660
+ }
661
+ memset(&promise_hook_state, 0, sizeof(promise_hook_state));
662
+ {
663
+ // module with thenable; fires before and after hooks
664
+ static const char code[] =
665
+ "new Promise(resolve => resolve({then(resolve){ resolve() }}))";
666
+ JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
667
+ assert(!JS_IsException(ret));
668
+ assert(JS_IsPromise(ret));
669
+ assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
670
+ JS_FreeValue(ctx, ret);
671
+ assert(3 == cc[JS_PROMISE_HOOK_INIT]);
672
+ assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
673
+ assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
674
+ assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]);
675
+ assert(JS_IsJobPending(rt));
676
+ assert(1 == JS_ExecutePendingJob(rt, &unused));
677
+ assert(!JS_HasException(ctx));
678
+ assert(3 == cc[JS_PROMISE_HOOK_INIT]);
679
+ assert(1 == cc[JS_PROMISE_HOOK_BEFORE]);
680
+ assert(1 == cc[JS_PROMISE_HOOK_AFTER]);
681
+ assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]);
682
+ assert(!JS_IsJobPending(rt));
683
+ }
684
+ JS_FreeValue(ctx, global_object);
685
+ JS_FreeContext(ctx);
686
+ JS_FreeRuntime(rt);
687
+ }
688
+
689
+ static void dump_memory_usage(void)
690
+ {
691
+ JSMemoryUsage stats;
692
+
693
+ JSRuntime *rt = NULL;
694
+ JSContext *ctx = NULL;
695
+
696
+ rt = JS_NewRuntime();
697
+ ctx = JS_NewContext(rt);
698
+
699
+ //JS_SetDumpFlags(rt, JS_DUMP_PROMISE);
700
+
701
+ static const char code[] =
702
+ "globalThis.count = 0;"
703
+ "globalThis.actual = undefined;" // set by promise_hook_cb
704
+ "globalThis.expected = new Promise(resolve => resolve());"
705
+ "expected.then(_ => count++)";
706
+
707
+ JSValue evalVal = JS_Eval(ctx, code, strlen(code), "<input>", 0);
708
+ JS_FreeValue(ctx, evalVal);
709
+
710
+ FILE *temp = tmpfile();
711
+ assert(temp != NULL);
712
+ JS_ComputeMemoryUsage(rt, &stats);
713
+ JS_DumpMemoryUsage(temp, &stats, rt);
714
+ // JS_DumpMemoryUsage(stdout, &stats, rt);
715
+ fclose(temp);
716
+
717
+ JS_FreeContext(ctx);
718
+ JS_FreeRuntime(rt);
719
+ }
720
+
721
+ static void new_errors(void)
722
+ {
723
+ typedef struct {
724
+ const char name[16];
725
+ JSValue (*func)(JSContext *, const char *, ...);
726
+ } Entry;
727
+ static const Entry entries[] = {
728
+ {"Error", JS_NewPlainError},
729
+ {"InternalError", JS_NewInternalError},
730
+ {"RangeError", JS_NewRangeError},
731
+ {"ReferenceError", JS_NewReferenceError},
732
+ {"SyntaxError", JS_NewSyntaxError},
733
+ {"TypeError", JS_NewTypeError},
734
+ };
735
+ const Entry *e;
736
+
737
+ JSRuntime *rt = JS_NewRuntime();
738
+ JSContext *ctx = JS_NewContext(rt);
739
+ for (e = entries; e < endof(entries); e++) {
740
+ JSValue obj = (*e->func)(ctx, "the %s", "needle");
741
+ assert(!JS_IsException(obj));
742
+ assert(JS_IsObject(obj));
743
+ assert(JS_IsError(obj));
744
+ const char *haystack = JS_ToCString(ctx, obj);
745
+ char needle[256];
746
+ snprintf(needle, sizeof(needle), "%s: the needle", e->name);
747
+ assert(strstr(haystack, needle));
748
+ JS_FreeCString(ctx, haystack);
749
+ JSValue stack = JS_GetPropertyStr(ctx, obj, "stack");
750
+ assert(!JS_IsException(stack));
751
+ assert(JS_IsString(stack));
752
+ JS_FreeValue(ctx, stack);
753
+ JS_FreeValue(ctx, obj);
754
+ }
755
+ JS_FreeContext(ctx);
756
+ JS_FreeRuntime(rt);
757
+ }
758
+
759
+ static int gop_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc,
760
+ JSValueConst obj, JSAtom prop)
761
+ {
762
+ const char *name;
763
+ int found;
764
+
765
+ found = 0;
766
+ name = JS_AtomToCString(ctx, prop);
767
+ if (!name)
768
+ return -1;
769
+ if (!strcmp(name, "answer")) {
770
+ found = 1;
771
+ *desc = (JSPropertyDescriptor){
772
+ .value = JS_NewInt32(ctx, 42),
773
+ .flags = JS_PROP_C_W_E | JS_PROP_HAS_VALUE,
774
+ };
775
+ }
776
+ JS_FreeCString(ctx, name);
777
+ return found;
778
+ }
779
+
780
+ static void global_object_prototype(void)
781
+ {
782
+ static const char code[] = "answer";
783
+ JSValue global_object, proto, ret;
784
+ JSClassID class_id;
785
+ JSRuntime *rt;
786
+ JSContext *ctx;
787
+ int32_t answer;
788
+ int res;
789
+
790
+ {
791
+ rt = JS_NewRuntime();
792
+ ctx = JS_NewContext(rt);
793
+ proto = JS_NewObject(ctx);
794
+ assert(JS_IsObject(proto));
795
+ JSCFunctionListEntry prop = JS_PROP_INT32_DEF("answer", 42, JS_PROP_C_W_E);
796
+ JS_SetPropertyFunctionList(ctx, proto, &prop, 1);
797
+ global_object = JS_GetGlobalObject(ctx);
798
+ res = JS_SetPrototype(ctx, global_object, proto);
799
+ assert(res == true);
800
+ JS_FreeValue(ctx, global_object);
801
+ JS_FreeValue(ctx, proto);
802
+ ret = eval(ctx, code);
803
+ assert(!JS_IsException(ret));
804
+ assert(JS_IsNumber(ret));
805
+ res = JS_ToInt32(ctx, &answer, ret);
806
+ assert(res == 0);
807
+ assert(answer == 42);
808
+ JS_FreeValue(ctx, ret);
809
+ JS_FreeContext(ctx);
810
+ JS_FreeRuntime(rt);
811
+ }
812
+ {
813
+ JSClassExoticMethods exotic = (JSClassExoticMethods){
814
+ .get_own_property = gop_get_own_property,
815
+ };
816
+ JSClassDef def = (JSClassDef){
817
+ .class_name = "Global Object",
818
+ .exotic = &exotic,
819
+ };
820
+ rt = JS_NewRuntime();
821
+ class_id = 0;
822
+ JS_NewClassID(rt, &class_id);
823
+ res = JS_NewClass(rt, class_id, &def);
824
+ assert(res == 0);
825
+ ctx = JS_NewContext(rt);
826
+ proto = JS_NewObjectClass(ctx, class_id);
827
+ assert(JS_IsObject(proto));
828
+ global_object = JS_GetGlobalObject(ctx);
829
+ res = JS_SetPrototype(ctx, global_object, proto);
830
+ assert(res == true);
831
+ JS_FreeValue(ctx, global_object);
832
+ JS_FreeValue(ctx, proto);
833
+ ret = eval(ctx, code);
834
+ assert(!JS_IsException(ret));
835
+ assert(JS_IsNumber(ret));
836
+ res = JS_ToInt32(ctx, &answer, ret);
837
+ assert(res == 0);
838
+ assert(answer == 42);
839
+ JS_FreeValue(ctx, ret);
840
+ JS_FreeContext(ctx);
841
+ JS_FreeRuntime(rt);
842
+ }
843
+ }
844
+
845
+ // https://github.com/quickjs-ng/quickjs/issues/1178
846
+ static void slice_string_tocstring(void)
847
+ {
848
+ JSRuntime *rt = JS_NewRuntime();
849
+ JSContext *ctx = JS_NewContext(rt);
850
+ JSValue ret = eval(ctx, "'.'.repeat(16384).slice(1, -1)");
851
+ assert(!JS_IsException(ret));
852
+ assert(JS_IsString(ret));
853
+ const char *str = JS_ToCString(ctx, ret);
854
+ assert(strlen(str) == 16382);
855
+ JS_FreeCString(ctx, str);
856
+ JS_FreeValue(ctx, ret);
857
+ JS_FreeContext(ctx);
858
+ JS_FreeRuntime(rt);
859
+ }
860
+
861
+ static void immutable_array_buffer(void)
862
+ {
863
+ JSValue obj, ret;
864
+ bool immutable;
865
+ char buf[96];
866
+ int i, v;
867
+
868
+ JSRuntime *rt = JS_NewRuntime();
869
+ JSContext *ctx = JS_NewContext(rt);
870
+ for (i = 0; i < 2; i++) {
871
+ obj = JS_NewObject(ctx);
872
+ immutable = (i == 0);
873
+ assert(-1 == JS_IsImmutableArrayBuffer(JS_NULL));
874
+ assert(-1 == JS_IsImmutableArrayBuffer(JS_UNDEFINED));
875
+ assert(-1 == JS_IsImmutableArrayBuffer(obj));
876
+ assert(-1 == JS_SetImmutableArrayBuffer(JS_NULL, immutable));
877
+ assert(-1 == JS_SetImmutableArrayBuffer(JS_UNDEFINED, immutable));
878
+ assert(-1 == JS_SetImmutableArrayBuffer(obj, immutable));
879
+ JS_FreeValue(ctx, obj);
880
+ }
881
+ obj = eval(ctx, "globalThis.ab = new ArrayBuffer(1)");
882
+ assert(!JS_IsException(obj));
883
+ assert(JS_IsArrayBuffer(obj));
884
+ assert(!JS_IsImmutableArrayBuffer(obj));
885
+ for (i = 1; i <= 3; i++) {
886
+ immutable = (i == 2);
887
+ if (i > 1)
888
+ JS_SetImmutableArrayBuffer(obj, immutable);
889
+ assert(immutable == JS_IsImmutableArrayBuffer(obj));
890
+ snprintf(buf, sizeof(buf),
891
+ "var ta = new Uint8Array(ab); ta[0] = %d; ta[0]", i);
892
+ ret = eval(ctx, buf);
893
+ assert(!JS_IsException(ret));
894
+ assert(JS_IsNumber(ret));
895
+ assert(0 == JS_ToInt32(ctx, &v, ret));
896
+ JS_FreeValue(ctx, ret);
897
+ if (immutable) {
898
+ assert(v != i);
899
+ } else {
900
+ assert(v == i);
901
+ }
902
+ }
903
+ JS_FreeValue(ctx, obj);
904
+ JS_FreeContext(ctx);
905
+ JS_FreeRuntime(rt);
906
+ }
907
+
908
+ static void get_uint8array(void)
909
+ {
910
+ JSRuntime *rt = JS_NewRuntime();
911
+ JSContext *ctx = JS_NewContext(rt);
912
+ JSValue val;
913
+ uint8_t *p;
914
+ size_t size;
915
+ uint8_t buf[3] = { 1, 2, 3 };
916
+
917
+ val = eval(ctx, "new Uint8Array(0)");
918
+ assert(!JS_IsException(val));
919
+ p = JS_GetUint8Array(ctx, &size, val);
920
+ assert(p != NULL);
921
+ assert(size == 0);
922
+ JS_FreeValue(ctx, val);
923
+
924
+ val = JS_NewUint8Array(ctx, NULL, 0, NULL, NULL, false);
925
+ assert(!JS_IsException(val));
926
+ p = JS_GetUint8Array(ctx, &size, val);
927
+ assert(p != NULL);
928
+ assert(size == 0);
929
+ JS_FreeValue(ctx, val);
930
+
931
+ val = JS_NewUint8ArrayCopy(ctx, NULL, 0);
932
+ assert(!JS_IsException(val));
933
+ p = JS_GetUint8Array(ctx, &size, val);
934
+ assert(p != NULL);
935
+ assert(size == 0);
936
+ JS_FreeValue(ctx, val);
937
+
938
+ val = JS_NewUint8ArrayCopy(ctx, buf, sizeof(buf));
939
+ assert(!JS_IsException(val));
940
+ p = JS_GetUint8Array(ctx, &size, val);
941
+ assert(p != NULL);
942
+ assert(size == 3);
943
+ assert(p[0] == 1 && p[1] == 2 && p[2] == 3);
944
+ JS_FreeValue(ctx, val);
945
+
946
+ val = eval(ctx, "new Uint8Array([4, 5, 6])");
947
+ assert(!JS_IsException(val));
948
+ p = JS_GetUint8Array(ctx, &size, val);
949
+ assert(p != NULL);
950
+ assert(size == 3);
951
+ assert(p[0] == 4 && p[1] == 5 && p[2] == 6);
952
+ JS_FreeValue(ctx, val);
953
+
954
+ val = eval(ctx, "new Int32Array(4)");
955
+ assert(!JS_IsException(val));
956
+ p = JS_GetUint8Array(ctx, &size, val);
957
+ assert(p == NULL);
958
+ assert(size == 0);
959
+ JS_FreeValue(ctx, val);
960
+
961
+ JS_FreeContext(ctx);
962
+ JS_FreeRuntime(rt);
963
+ }
964
+
965
+ int main(void)
966
+ {
967
+ cfunctions();
968
+ sync_call();
969
+ async_call();
970
+ async_call_stack_overflow();
971
+ raw_context_global_var();
972
+ is_array();
973
+ module_serde();
974
+ runtime_cstring_free();
975
+ utf16_string();
976
+ weak_map_gc_check();
977
+ promise_hook();
978
+ dump_memory_usage();
979
+ new_errors();
980
+ global_object_prototype();
981
+ slice_string_tocstring();
982
+ immutable_array_buffer();
983
+ get_uint8array();
984
+ return 0;
985
+ }