mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-16 05:00:59 +08:00
1196 lines
37 KiB
C
1196 lines
37 KiB
C
#ifdef NDEBUG
|
|
#undef NDEBUG
|
|
#endif
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "quickjs.h"
|
|
#include "cutils.h"
|
|
|
|
static JSRuntime *new_runtime(void)
|
|
{
|
|
JSRuntime *rt = JS_NewRuntime();
|
|
|
|
if (rt)
|
|
JS_SetDumpFlags(rt, JS_ABORT_ON_LEAKS);
|
|
return rt;
|
|
}
|
|
|
|
static JSValue eval(JSContext *ctx, const char *code)
|
|
{
|
|
return JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_GLOBAL);
|
|
}
|
|
|
|
static JSValue cfunc_callback(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
return JS_ThrowTypeError(ctx, "from cfunc");
|
|
}
|
|
|
|
static JSValue cfuncdata_callback(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic, JSValueConst *func_data)
|
|
{
|
|
return JS_ThrowTypeError(ctx, "from cfuncdata");
|
|
}
|
|
|
|
static JSValue cclosure_callback(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv,
|
|
int magic, void *func_data)
|
|
{
|
|
return JS_ThrowTypeError(ctx, "from cclosure");
|
|
}
|
|
|
|
static bool closure_finalized = false;
|
|
|
|
static void cclosure_opaque_finalize(void *opaque)
|
|
{
|
|
if ((intptr_t)opaque == 12)
|
|
closure_finalized = true;
|
|
}
|
|
|
|
static void cfunctions(void)
|
|
{
|
|
uint32_t length;
|
|
const char *s;
|
|
JSValue ret, stack;
|
|
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
JSValue cfunc = JS_NewCFunction(ctx, cfunc_callback, "cfunc", 42);
|
|
JSValue cfuncdata =
|
|
JS_NewCFunctionData2(ctx, cfuncdata_callback, "cfuncdata",
|
|
/*length*/1337, /*magic*/0, /*data_len*/0, NULL);
|
|
JSValue cclosure =
|
|
JS_NewCClosure(ctx, cclosure_callback, "cclosure", cclosure_opaque_finalize,
|
|
/*length*/0xC0DE, /*magic*/11, /*opaque*/(void*)12);
|
|
JSValue global = JS_GetGlobalObject(ctx);
|
|
JS_SetPropertyStr(ctx, global, "cfunc", cfunc);
|
|
JS_SetPropertyStr(ctx, global, "cfuncdata", cfuncdata);
|
|
JS_SetPropertyStr(ctx, global, "cclosure", cclosure);
|
|
JS_FreeValue(ctx, global);
|
|
|
|
ret = eval(ctx, "cfunc.name");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsString(ret));
|
|
s = JS_ToCString(ctx, ret);
|
|
JS_FreeValue(ctx, ret);
|
|
assert(s);
|
|
assert(!strcmp(s, "cfunc"));
|
|
JS_FreeCString(ctx, s);
|
|
ret = eval(ctx, "cfunc.length");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsNumber(ret));
|
|
assert(0 == JS_ToUint32(ctx, &length, ret));
|
|
assert(length == 42);
|
|
|
|
ret = eval(ctx, "cfuncdata.name");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsString(ret));
|
|
s = JS_ToCString(ctx, ret);
|
|
JS_FreeValue(ctx, ret);
|
|
assert(s);
|
|
assert(!strcmp(s, "cfuncdata"));
|
|
JS_FreeCString(ctx, s);
|
|
ret = eval(ctx, "cfuncdata.length");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsNumber(ret));
|
|
assert(0 == JS_ToUint32(ctx, &length, ret));
|
|
assert(length == 1337);
|
|
|
|
ret = eval(ctx, "cclosure.name");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsString(ret));
|
|
s = JS_ToCString(ctx, ret);
|
|
JS_FreeValue(ctx, ret);
|
|
assert(s);
|
|
assert(!strcmp(s, "cclosure"));
|
|
JS_FreeCString(ctx, s);
|
|
ret = eval(ctx, "cclosure.length");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsNumber(ret));
|
|
assert(0 == JS_ToUint32(ctx, &length, ret));
|
|
assert(length == 0xC0DE);
|
|
|
|
ret = eval(ctx, "cfunc()");
|
|
assert(JS_IsException(ret));
|
|
ret = JS_GetException(ctx);
|
|
assert(JS_IsError(ret));
|
|
stack = JS_GetPropertyStr(ctx, ret, "stack");
|
|
assert(JS_IsString(stack));
|
|
s = JS_ToCString(ctx, stack);
|
|
JS_FreeValue(ctx, stack);
|
|
assert(s);
|
|
assert(!strcmp(s, " at cfunc (native)\n at <eval> (<input>:1:1)\n"));
|
|
JS_FreeCString(ctx, s);
|
|
s = JS_ToCString(ctx, ret);
|
|
JS_FreeValue(ctx, ret);
|
|
assert(s);
|
|
assert(!strcmp(s, "TypeError: from cfunc"));
|
|
JS_FreeCString(ctx, s);
|
|
|
|
ret = eval(ctx, "cfuncdata()");
|
|
assert(JS_IsException(ret));
|
|
ret = JS_GetException(ctx);
|
|
assert(JS_IsError(ret));
|
|
stack = JS_GetPropertyStr(ctx, ret, "stack");
|
|
assert(JS_IsString(stack));
|
|
s = JS_ToCString(ctx, stack);
|
|
JS_FreeValue(ctx, stack);
|
|
assert(s);
|
|
assert(!strcmp(s, " at cfuncdata (native)\n at <eval> (<input>:1:1)\n"));
|
|
JS_FreeCString(ctx, s);
|
|
s = JS_ToCString(ctx, ret);
|
|
JS_FreeValue(ctx, ret);
|
|
assert(s);
|
|
assert(!strcmp(s, "TypeError: from cfuncdata"));
|
|
JS_FreeCString(ctx, s);
|
|
|
|
ret = eval(ctx, "cclosure()");
|
|
assert(JS_IsException(ret));
|
|
ret = JS_GetException(ctx);
|
|
assert(JS_IsError(ret));
|
|
stack = JS_GetPropertyStr(ctx, ret, "stack");
|
|
assert(JS_IsString(stack));
|
|
s = JS_ToCString(ctx, stack);
|
|
JS_FreeValue(ctx, stack);
|
|
assert(s);
|
|
assert(!strcmp(s, " at cclosure (native)\n at <eval> (<input>:1:1)\n"));
|
|
JS_FreeCString(ctx, s);
|
|
s = JS_ToCString(ctx, ret);
|
|
JS_FreeValue(ctx, ret);
|
|
assert(s);
|
|
assert(!strcmp(s, "TypeError: from cclosure"));
|
|
JS_FreeCString(ctx, s);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
|
|
assert(closure_finalized);
|
|
}
|
|
|
|
#define MAX_TIME 10
|
|
|
|
static int timeout_interrupt_handler(JSRuntime *rt, void *opaque)
|
|
{
|
|
int *time = (int *)opaque;
|
|
if (*time <= MAX_TIME)
|
|
*time += 1;
|
|
return *time > MAX_TIME;
|
|
}
|
|
|
|
static void sync_call(void)
|
|
{
|
|
static const char code[] =
|
|
"(function() { \
|
|
try { \
|
|
while (true) {} \
|
|
} catch (e) {} \
|
|
})();";
|
|
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
int time = 0;
|
|
JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time);
|
|
JSValue ret = eval(ctx, code);
|
|
assert(time > MAX_TIME);
|
|
assert(JS_IsException(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
assert(JS_HasException(ctx));
|
|
JSValue e = JS_GetException(ctx);
|
|
assert(JS_IsUncatchableError(e));
|
|
JS_FreeValue(ctx, e);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void async_call(void)
|
|
{
|
|
static const char code[] =
|
|
"(async function() { \
|
|
const loop = async () => { \
|
|
await Promise.resolve(); \
|
|
while (true) {} \
|
|
}; \
|
|
await loop().catch(() => {}); \
|
|
})();";
|
|
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
int time = 0;
|
|
JS_SetInterruptHandler(rt, timeout_interrupt_handler, &time);
|
|
JSValue ret = eval(ctx, code);
|
|
assert(!JS_IsException(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
assert(JS_IsJobPending(rt));
|
|
int r = 0;
|
|
while (JS_IsJobPending(rt)) {
|
|
r = JS_ExecutePendingJob(rt, &ctx);
|
|
}
|
|
assert(time > MAX_TIME);
|
|
assert(r == -1);
|
|
assert(JS_HasException(ctx));
|
|
JSValue e = JS_GetException(ctx);
|
|
assert(JS_IsUncatchableError(e));
|
|
JS_FreeValue(ctx, e);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static JSValue save_value(JSContext *ctx, JSValueConst this_val,
|
|
int argc, JSValueConst *argv)
|
|
{
|
|
assert(argc == 1);
|
|
JSValue *p = (JSValue *)JS_GetContextOpaque(ctx);
|
|
*p = JS_DupValue(ctx, argv[0]);
|
|
return JS_UNDEFINED;
|
|
}
|
|
|
|
static void async_call_stack_overflow(void)
|
|
{
|
|
static const char code[] =
|
|
"(async function() { \
|
|
const f = () => f(); \
|
|
try { \
|
|
await Promise.resolve(); \
|
|
f(); \
|
|
} catch (e) { \
|
|
save_value(e); \
|
|
} \
|
|
})();";
|
|
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
JSValue value = JS_UNDEFINED;
|
|
JS_SetContextOpaque(ctx, &value);
|
|
JSValue global = JS_GetGlobalObject(ctx);
|
|
JS_SetPropertyStr(ctx, global, "save_value", JS_NewCFunction(ctx, save_value, "save_value", 1));
|
|
JS_FreeValue(ctx, global);
|
|
JSValue ret = eval(ctx, code);
|
|
assert(!JS_IsException(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
assert(JS_IsJobPending(rt));
|
|
int r = 0;
|
|
while (JS_IsJobPending(rt)) {
|
|
r = JS_ExecutePendingJob(rt, &ctx);
|
|
}
|
|
assert(r == 1);
|
|
assert(!JS_HasException(ctx));
|
|
assert(JS_IsError(value)); // stack overflow should be caught
|
|
JS_FreeValue(ctx, value);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
// https://github.com/quickjs-ng/quickjs/issues/914
|
|
static void raw_context_global_var(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContextRaw(rt);
|
|
JS_AddIntrinsicEval(ctx);
|
|
{
|
|
JSValue ret = eval(ctx, "globalThis");
|
|
assert(JS_IsException(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
{
|
|
JSValue ret = eval(ctx, "var x = 42");
|
|
assert(JS_IsUndefined(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
{
|
|
JSValue ret = eval(ctx, "function f() {}");
|
|
assert(JS_IsUndefined(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void is_array(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
{
|
|
JSValue ret = eval(ctx, "[]");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsArray(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
{
|
|
JSValue ret = eval(ctx, "new Proxy([], {})");
|
|
assert(!JS_IsException(ret));
|
|
assert(!JS_IsArray(ret));
|
|
assert(JS_IsProxy(ret));
|
|
JSValue handler = JS_GetProxyHandler(ctx, ret);
|
|
JSValue target = JS_GetProxyTarget(ctx, ret);
|
|
assert(!JS_IsException(handler));
|
|
assert(!JS_IsException(target));
|
|
assert(!JS_IsProxy(handler));
|
|
assert(!JS_IsProxy(target));
|
|
assert(JS_IsObject(handler));
|
|
assert(JS_IsArray(target));
|
|
JS_FreeValue(ctx, handler);
|
|
JS_FreeValue(ctx, target);
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static int loader_calls;
|
|
|
|
static JSModuleDef *loader(JSContext *ctx, const char *name, void *opaque)
|
|
{
|
|
loader_calls++;
|
|
assert(!strcmp(name, "b"));
|
|
static const char code[] = "export function f(x){}";
|
|
JSValue ret = JS_Eval(ctx, code, strlen(code), "b",
|
|
JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY);
|
|
assert(!JS_IsException(ret));
|
|
JSModuleDef *m = JS_VALUE_GET_PTR(ret);
|
|
assert(m);
|
|
JS_FreeValue(ctx, ret);
|
|
return m;
|
|
}
|
|
|
|
static void module_serde(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
//JS_SetDumpFlags(rt, JS_DUMP_MODULE_RESOLVE);
|
|
JS_SetModuleLoaderFunc(rt, NULL, loader, NULL);
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
static const char code[] = "import {f} from 'b'; f()";
|
|
assert(loader_calls == 0);
|
|
JSValue mod = JS_Eval(ctx, code, strlen(code), "a",
|
|
JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY);
|
|
assert(loader_calls == 1);
|
|
assert(!JS_IsException(mod));
|
|
assert(JS_IsModule(mod));
|
|
size_t len = 0;
|
|
uint8_t *buf = JS_WriteObject(ctx, &len, mod,
|
|
JS_WRITE_OBJ_BYTECODE|JS_WRITE_OBJ_REFERENCE);
|
|
assert(buf);
|
|
assert(len > 0);
|
|
JS_FreeValue(ctx, mod);
|
|
assert(loader_calls == 1);
|
|
mod = JS_ReadObject(ctx, buf, len, JS_READ_OBJ_BYTECODE);
|
|
js_free(ctx, buf);
|
|
assert(loader_calls == 1); // 'b' is returned from cache
|
|
assert(!JS_IsException(mod));
|
|
JSValue ret = JS_EvalFunction(ctx, mod);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsPromise(ret));
|
|
JSValue result = JS_PromiseResult(ctx, ret);
|
|
assert(!JS_IsException(result));
|
|
assert(JS_IsUndefined(result));
|
|
JS_FreeValue(ctx, result);
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeValue(ctx, mod);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void runtime_cstring_free(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
// string -> cstring + JS_FreeCStringRT
|
|
{
|
|
JSValue ret = eval(ctx, "\"testStringPleaseIgnore\"");
|
|
assert(JS_IsString(ret));
|
|
const char *s = JS_ToCString(ctx, ret);
|
|
assert(s);
|
|
assert(strcmp(s, "testStringPleaseIgnore") == 0);
|
|
JS_FreeCStringRT(rt, s);
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
// string -> cstring + JS_FreeCStringRT, destroying the source value first
|
|
{
|
|
JSValue ret = eval(ctx, "\"testStringPleaseIgnore\"");
|
|
assert(JS_IsString(ret));
|
|
const char *s = JS_ToCString(ctx, ret);
|
|
assert(s);
|
|
JS_FreeValue(ctx, ret);
|
|
assert(strcmp(s, "testStringPleaseIgnore") == 0);
|
|
JS_FreeCStringRT(rt, s);
|
|
}
|
|
// number -> cstring + JS_FreeCStringRT
|
|
{
|
|
JSValue ret = eval(ctx, "123987");
|
|
assert(JS_IsNumber(ret));
|
|
const char *s = JS_ToCString(ctx, ret);
|
|
assert(s);
|
|
assert(strcmp(s, "123987") == 0);
|
|
JS_FreeCStringRT(rt, s);
|
|
JS_FreeValue(ctx, ret);
|
|
}
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void utf16_string(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
{
|
|
JSValue v = JS_NewStringUTF16(ctx, NULL, 0);
|
|
assert(!JS_IsException(v));
|
|
const char *s = JS_ToCString(ctx, v);
|
|
assert(s);
|
|
assert(!strcmp(s, ""));
|
|
JS_FreeCString(ctx, s);
|
|
JS_FreeValue(ctx, v);
|
|
}
|
|
{
|
|
JSValue v = JS_NewStringUTF16(ctx, (uint16_t[]){'o','k'}, 2);
|
|
assert(!JS_IsException(v));
|
|
const char *s = JS_ToCString(ctx, v);
|
|
assert(s);
|
|
assert(!strcmp(s, "ok"));
|
|
JS_FreeCString(ctx, s);
|
|
size_t n;
|
|
const uint16_t *u = JS_ToCStringLenUTF16(ctx, &n, v);
|
|
assert(u);
|
|
assert(n == 2);
|
|
assert(u[0] == 'o');
|
|
assert(u[1] == 'k');
|
|
JS_FreeCStringUTF16(ctx, u);
|
|
JS_FreeValue(ctx, v);
|
|
}
|
|
{
|
|
JSValue v = JS_NewStringUTF16(ctx, (uint16_t[]){0xD800}, 1);
|
|
assert(!JS_IsException(v));
|
|
const char *s = JS_ToCString(ctx, v);
|
|
assert(s);
|
|
// questionable but surrogates don't map to UTF-8 without WTF-8
|
|
assert(!strcmp(s, "\xED\xA0\x80"));
|
|
JS_FreeCString(ctx, s);
|
|
size_t n;
|
|
const uint16_t *u = JS_ToCStringLenUTF16(ctx, &n, v);
|
|
assert(u);
|
|
assert(n == 1);
|
|
assert(u[0] == 0xD800);
|
|
JS_FreeCStringUTF16(ctx, u);
|
|
JS_FreeValue(ctx, v);
|
|
}
|
|
{
|
|
JSValue v = JS_NewStringLen(ctx, "ok", 2); // ascii -> ucs
|
|
assert(!JS_IsException(v));
|
|
size_t n;
|
|
const uint16_t *u = JS_ToCStringLenUTF16(ctx, &n, v);
|
|
assert(u);
|
|
assert(n == 2);
|
|
assert(u[0] == 'o');
|
|
assert(u[1] == 'k');
|
|
JS_FreeCStringUTF16(ctx, u);
|
|
JS_FreeValue(ctx, v);
|
|
}
|
|
{
|
|
JSValue v = JS_NewStringUTF16(ctx, NULL, (size_t)INT_MAX + 1);
|
|
assert(JS_IsException(v));
|
|
JSValue e = JS_GetException(ctx);
|
|
assert(JS_IsError(e));
|
|
const char *s = JS_ToCString(ctx, e);
|
|
assert(s);
|
|
assert(strstr(s, "invalid string length") != NULL);
|
|
JS_FreeCString(ctx, s);
|
|
JS_FreeValue(ctx, e);
|
|
}
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void weak_map_gc_check(void)
|
|
{
|
|
static const char init_code[] =
|
|
"const map = new WeakMap(); \
|
|
function addItem() { \
|
|
const k = { \
|
|
text: 'a', \
|
|
}; \
|
|
map.set(k, {k}); \
|
|
}";
|
|
static const char test_code[] = "addItem()";
|
|
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
|
|
JSValue ret = eval(ctx, init_code);
|
|
assert(!JS_IsException(ret));
|
|
|
|
JSValue ret_test = eval(ctx, test_code);
|
|
assert(!JS_IsException(ret_test));
|
|
JS_RunGC(rt);
|
|
JSMemoryUsage memory_usage;
|
|
JS_ComputeMemoryUsage(rt, &memory_usage);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
JSValue ret_test2 = eval(ctx, test_code);
|
|
assert(!JS_IsException(ret_test2));
|
|
JS_RunGC(rt);
|
|
JSMemoryUsage memory_usage2;
|
|
JS_ComputeMemoryUsage(rt, &memory_usage2);
|
|
|
|
assert(memory_usage.memory_used_count == memory_usage2.memory_used_count);
|
|
assert(memory_usage.memory_used_size == memory_usage2.memory_used_size);
|
|
JS_FreeValue(ctx, ret_test2);
|
|
}
|
|
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeValue(ctx, ret_test);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
struct {
|
|
int hook_type_call_count[4];
|
|
} promise_hook_state;
|
|
|
|
static void promise_hook_cb(JSContext *ctx, JSPromiseHookType type,
|
|
JSValueConst promise, JSValueConst parent_promise,
|
|
void *opaque)
|
|
{
|
|
assert(type == JS_PROMISE_HOOK_INIT ||
|
|
type == JS_PROMISE_HOOK_BEFORE ||
|
|
type == JS_PROMISE_HOOK_AFTER ||
|
|
type == JS_PROMISE_HOOK_RESOLVE);
|
|
promise_hook_state.hook_type_call_count[type]++;
|
|
assert(opaque == (void *)&promise_hook_state);
|
|
if (!JS_IsUndefined(parent_promise)) {
|
|
JSValue global_object = JS_GetGlobalObject(ctx);
|
|
JS_SetPropertyStr(ctx, global_object, "actual",
|
|
JS_DupValue(ctx, parent_promise));
|
|
JS_FreeValue(ctx, global_object);
|
|
}
|
|
}
|
|
|
|
static void promise_hook(void)
|
|
{
|
|
int *cc = promise_hook_state.hook_type_call_count;
|
|
JSContext *unused;
|
|
JSRuntime *rt = new_runtime();
|
|
//JS_SetDumpFlags(rt, JS_DUMP_PROMISE);
|
|
JS_SetPromiseHook(rt, promise_hook_cb, &promise_hook_state);
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
JSValue global_object = JS_GetGlobalObject(ctx);
|
|
{
|
|
// empty module; creates an outer and inner module promise;
|
|
// JS_Eval returns the outer promise
|
|
JSValue ret = JS_Eval(ctx, "", 0, "<input>", JS_EVAL_TYPE_MODULE);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsPromise(ret));
|
|
assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret));
|
|
JS_FreeValue(ctx, ret);
|
|
assert(2 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]);
|
|
assert(!JS_IsJobPending(rt));
|
|
}
|
|
memset(&promise_hook_state, 0, sizeof(promise_hook_state));
|
|
{
|
|
// module with unresolved promise; the outer and inner module promises
|
|
// are resolved but not the user's promise
|
|
static const char code[] = "new Promise(() => {})";
|
|
JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsPromise(ret));
|
|
assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
|
|
JS_FreeValue(ctx, ret);
|
|
assert(3 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]); // outer and inner module promise
|
|
assert(!JS_IsJobPending(rt));
|
|
}
|
|
memset(&promise_hook_state, 0, sizeof(promise_hook_state));
|
|
{
|
|
// module with resolved promise
|
|
static const char code[] = "new Promise((resolve,reject) => resolve())";
|
|
JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsPromise(ret));
|
|
assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
|
|
JS_FreeValue(ctx, ret);
|
|
assert(3 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]);
|
|
assert(!JS_IsJobPending(rt));
|
|
}
|
|
memset(&promise_hook_state, 0, sizeof(promise_hook_state));
|
|
{
|
|
// module with rejected promise
|
|
static const char code[] = "new Promise((resolve,reject) => reject())";
|
|
JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsPromise(ret));
|
|
assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
|
|
JS_FreeValue(ctx, ret);
|
|
assert(3 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]);
|
|
assert(!JS_IsJobPending(rt));
|
|
}
|
|
memset(&promise_hook_state, 0, sizeof(promise_hook_state));
|
|
{
|
|
// module with promise chain
|
|
static const char code[] =
|
|
"globalThis.count = 0;"
|
|
"globalThis.actual = undefined;" // set by promise_hook_cb
|
|
"globalThis.expected = new Promise(resolve => resolve());"
|
|
"expected.then(_ => count++)";
|
|
JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsPromise(ret));
|
|
assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
|
|
JS_FreeValue(ctx, ret);
|
|
assert(4 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]);
|
|
JSValue v = JS_GetPropertyStr(ctx, global_object, "count");
|
|
assert(!JS_IsException(v));
|
|
int32_t count;
|
|
assert(0 == JS_ToInt32(ctx, &count, v));
|
|
assert(0 == count);
|
|
JS_FreeValue(ctx, v);
|
|
assert(JS_IsJobPending(rt));
|
|
assert(1 == JS_ExecutePendingJob(rt, &unused));
|
|
assert(!JS_HasException(ctx));
|
|
assert(4 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(4 == cc[JS_PROMISE_HOOK_RESOLVE]);
|
|
assert(!JS_IsJobPending(rt));
|
|
v = JS_GetPropertyStr(ctx, global_object, "count");
|
|
assert(!JS_IsException(v));
|
|
assert(0 == JS_ToInt32(ctx, &count, v));
|
|
assert(1 == count);
|
|
JS_FreeValue(ctx, v);
|
|
JSValue actual = JS_GetPropertyStr(ctx, global_object, "actual");
|
|
JSValue expected = JS_GetPropertyStr(ctx, global_object, "expected");
|
|
assert(!JS_IsException(actual));
|
|
assert(!JS_IsException(expected));
|
|
assert(JS_IsSameValue(ctx, actual, expected));
|
|
JS_FreeValue(ctx, actual);
|
|
JS_FreeValue(ctx, expected);
|
|
}
|
|
memset(&promise_hook_state, 0, sizeof(promise_hook_state));
|
|
{
|
|
// module with thenable; fires before and after hooks
|
|
static const char code[] =
|
|
"new Promise(resolve => resolve({then(resolve){ resolve() }}))";
|
|
JSValue ret = JS_Eval(ctx, code, strlen(code), "<input>", JS_EVAL_TYPE_MODULE);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsPromise(ret));
|
|
assert(JS_PROMISE_FULFILLED == JS_PromiseState(ctx, ret)); // outer module promise
|
|
JS_FreeValue(ctx, ret);
|
|
assert(3 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(0 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(2 == cc[JS_PROMISE_HOOK_RESOLVE]);
|
|
assert(JS_IsJobPending(rt));
|
|
assert(1 == JS_ExecutePendingJob(rt, &unused));
|
|
assert(!JS_HasException(ctx));
|
|
assert(3 == cc[JS_PROMISE_HOOK_INIT]);
|
|
assert(1 == cc[JS_PROMISE_HOOK_BEFORE]);
|
|
assert(1 == cc[JS_PROMISE_HOOK_AFTER]);
|
|
assert(3 == cc[JS_PROMISE_HOOK_RESOLVE]);
|
|
assert(!JS_IsJobPending(rt));
|
|
}
|
|
JS_FreeValue(ctx, global_object);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void dump_memory_usage(void)
|
|
{
|
|
JSMemoryUsage stats;
|
|
|
|
JSRuntime *rt = NULL;
|
|
JSContext *ctx = NULL;
|
|
|
|
rt = new_runtime();
|
|
ctx = JS_NewContext(rt);
|
|
|
|
//JS_SetDumpFlags(rt, JS_DUMP_PROMISE);
|
|
|
|
static const char code[] =
|
|
"globalThis.count = 0;"
|
|
"globalThis.actual = undefined;" // set by promise_hook_cb
|
|
"globalThis.expected = new Promise(resolve => resolve());"
|
|
"expected.then(_ => count++)";
|
|
|
|
JSValue evalVal = JS_Eval(ctx, code, strlen(code), "<input>", 0);
|
|
JS_FreeValue(ctx, evalVal);
|
|
|
|
FILE *temp = tmpfile();
|
|
assert(temp != NULL);
|
|
JS_ComputeMemoryUsage(rt, &stats);
|
|
JS_DumpMemoryUsage(temp, &stats, rt);
|
|
// JS_DumpMemoryUsage(stdout, &stats, rt);
|
|
fclose(temp);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void new_errors(void)
|
|
{
|
|
typedef struct {
|
|
const char name[16];
|
|
JSValue (*func)(JSContext *, const char *, ...);
|
|
} Entry;
|
|
static const Entry entries[] = {
|
|
{"Error", JS_NewPlainError},
|
|
{"InternalError", JS_NewInternalError},
|
|
{"RangeError", JS_NewRangeError},
|
|
{"ReferenceError", JS_NewReferenceError},
|
|
{"SyntaxError", JS_NewSyntaxError},
|
|
{"TypeError", JS_NewTypeError},
|
|
};
|
|
const Entry *e;
|
|
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
for (e = entries; e < endof(entries); e++) {
|
|
JSValue obj = (*e->func)(ctx, "the %s", "needle");
|
|
assert(!JS_IsException(obj));
|
|
assert(JS_IsObject(obj));
|
|
assert(JS_IsError(obj));
|
|
const char *haystack = JS_ToCString(ctx, obj);
|
|
char needle[256];
|
|
snprintf(needle, sizeof(needle), "%s: the needle", e->name);
|
|
assert(strstr(haystack, needle));
|
|
JS_FreeCString(ctx, haystack);
|
|
JSValue stack = JS_GetPropertyStr(ctx, obj, "stack");
|
|
assert(!JS_IsException(stack));
|
|
assert(JS_IsString(stack));
|
|
JS_FreeValue(ctx, stack);
|
|
JS_FreeValue(ctx, obj);
|
|
}
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void backtrace_oom_current_exception(void)
|
|
{
|
|
static const char setup_code[] =
|
|
"globalThis.f = function() { missing; };\n"
|
|
"Object.defineProperty(f, 'name', { value: 'x'.repeat(2 * 1024 * 1024) });";
|
|
JSMemoryUsage stats;
|
|
JSValue ret, exception;
|
|
JSRuntime *rt;
|
|
JSContext *ctx;
|
|
|
|
rt = new_runtime();
|
|
ctx = JS_NewContext(rt);
|
|
|
|
ret = eval(ctx, setup_code);
|
|
assert(!JS_IsException(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
|
|
JS_ComputeMemoryUsage(rt, &stats);
|
|
JS_SetMemoryLimit(rt, (size_t)stats.malloc_size + 128 * 1024);
|
|
|
|
ret = eval(ctx, "f()");
|
|
assert(JS_IsException(ret));
|
|
assert(JS_HasException(ctx));
|
|
exception = JS_GetException(ctx);
|
|
JS_FreeValue(ctx, exception);
|
|
JS_SetMemoryLimit(rt, 0);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static int gop_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc,
|
|
JSValueConst obj, JSAtom prop)
|
|
{
|
|
const char *name;
|
|
int found;
|
|
|
|
found = 0;
|
|
name = JS_AtomToCString(ctx, prop);
|
|
if (!name)
|
|
return -1;
|
|
if (!strcmp(name, "answer")) {
|
|
found = 1;
|
|
*desc = (JSPropertyDescriptor){
|
|
.value = JS_NewInt32(ctx, 42),
|
|
.flags = JS_PROP_C_W_E | JS_PROP_HAS_VALUE,
|
|
};
|
|
}
|
|
JS_FreeCString(ctx, name);
|
|
return found;
|
|
}
|
|
|
|
static void global_object_prototype(void)
|
|
{
|
|
static const char code[] = "answer";
|
|
JSValue global_object, proto, ret;
|
|
JSClassID class_id;
|
|
JSRuntime *rt;
|
|
JSContext *ctx;
|
|
int32_t answer;
|
|
int res;
|
|
|
|
{
|
|
rt = new_runtime();
|
|
ctx = JS_NewContext(rt);
|
|
proto = JS_NewObject(ctx);
|
|
assert(JS_IsObject(proto));
|
|
JSCFunctionListEntry prop = JS_PROP_INT32_DEF("answer", 42, JS_PROP_C_W_E);
|
|
JS_SetPropertyFunctionList(ctx, proto, &prop, 1);
|
|
global_object = JS_GetGlobalObject(ctx);
|
|
res = JS_SetPrototype(ctx, global_object, proto);
|
|
assert(res == true);
|
|
JS_FreeValue(ctx, global_object);
|
|
JS_FreeValue(ctx, proto);
|
|
ret = eval(ctx, code);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsNumber(ret));
|
|
res = JS_ToInt32(ctx, &answer, ret);
|
|
assert(res == 0);
|
|
assert(answer == 42);
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
{
|
|
JSClassExoticMethods exotic = (JSClassExoticMethods){
|
|
.get_own_property = gop_get_own_property,
|
|
};
|
|
JSClassDef def = (JSClassDef){
|
|
.class_name = "Global Object",
|
|
.exotic = &exotic,
|
|
};
|
|
rt = new_runtime();
|
|
class_id = 0;
|
|
JS_NewClassID(rt, &class_id);
|
|
res = JS_NewClass(rt, class_id, &def);
|
|
assert(res == 0);
|
|
ctx = JS_NewContext(rt);
|
|
proto = JS_NewObjectClass(ctx, class_id);
|
|
assert(JS_IsObject(proto));
|
|
global_object = JS_GetGlobalObject(ctx);
|
|
res = JS_SetPrototype(ctx, global_object, proto);
|
|
assert(res == true);
|
|
JS_FreeValue(ctx, global_object);
|
|
JS_FreeValue(ctx, proto);
|
|
ret = eval(ctx, code);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsNumber(ret));
|
|
res = JS_ToInt32(ctx, &answer, ret);
|
|
assert(res == 0);
|
|
assert(answer == 42);
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
}
|
|
|
|
// https://github.com/quickjs-ng/quickjs/issues/1178
|
|
static void slice_string_tocstring(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
JSValue ret = eval(ctx, "'.'.repeat(16384).slice(1, -1)");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsString(ret));
|
|
const char *str = JS_ToCString(ctx, ret);
|
|
assert(strlen(str) == 16382);
|
|
JS_FreeCString(ctx, str);
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void immutable_array_buffer(void)
|
|
{
|
|
JSValue obj, ret;
|
|
bool immutable;
|
|
char buf[96];
|
|
int i, v;
|
|
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
for (i = 0; i < 2; i++) {
|
|
obj = JS_NewObject(ctx);
|
|
immutable = (i == 0);
|
|
assert(-1 == JS_IsImmutableArrayBuffer(JS_NULL));
|
|
assert(-1 == JS_IsImmutableArrayBuffer(JS_UNDEFINED));
|
|
assert(-1 == JS_IsImmutableArrayBuffer(obj));
|
|
assert(-1 == JS_SetImmutableArrayBuffer(JS_NULL, immutable));
|
|
assert(-1 == JS_SetImmutableArrayBuffer(JS_UNDEFINED, immutable));
|
|
assert(-1 == JS_SetImmutableArrayBuffer(obj, immutable));
|
|
JS_FreeValue(ctx, obj);
|
|
}
|
|
obj = eval(ctx, "globalThis.ab = new ArrayBuffer(1)");
|
|
assert(!JS_IsException(obj));
|
|
assert(JS_IsArrayBuffer(obj));
|
|
assert(!JS_IsImmutableArrayBuffer(obj));
|
|
for (i = 1; i <= 3; i++) {
|
|
immutable = (i == 2);
|
|
if (i > 1)
|
|
JS_SetImmutableArrayBuffer(obj, immutable);
|
|
assert(immutable == JS_IsImmutableArrayBuffer(obj));
|
|
snprintf(buf, sizeof(buf),
|
|
"var ta = new Uint8Array(ab); ta[0] = %d; ta[0]", i);
|
|
ret = eval(ctx, buf);
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsNumber(ret));
|
|
assert(0 == JS_ToInt32(ctx, &v, ret));
|
|
JS_FreeValue(ctx, ret);
|
|
if (immutable) {
|
|
assert(v != i);
|
|
} else {
|
|
assert(v == i);
|
|
}
|
|
}
|
|
JS_FreeValue(ctx, obj);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void *sab_test_alloc(void *opaque, size_t size)
|
|
{
|
|
return malloc(size);
|
|
}
|
|
|
|
static void sab_test_free(void *opaque, void *ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
static void shared_array_buffer_growth(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
JSValue ret, exception;
|
|
|
|
ret = eval(ctx, "new SharedArrayBuffer(16)");
|
|
assert(!JS_IsException(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
|
|
ret = eval(ctx,
|
|
"const sab = new SharedArrayBuffer(16, { maxByteLength: 16 });"
|
|
"sab.grow(16);"
|
|
"sab.byteLength === 16 && sab.maxByteLength === 16");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsBool(ret));
|
|
assert(JS_VALUE_GET_BOOL(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
|
|
ret = eval(ctx, "new SharedArrayBuffer(16, { maxByteLength: 16384 })");
|
|
assert(JS_IsException(ret));
|
|
assert(JS_HasException(ctx));
|
|
exception = JS_GetException(ctx);
|
|
assert(JS_IsError(exception));
|
|
JS_FreeValue(ctx, exception);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
|
|
JSSharedArrayBufferFunctions funcs = {
|
|
.sab_alloc = sab_test_alloc,
|
|
.sab_free = sab_test_free,
|
|
.sab_dup = NULL,
|
|
.sab_opaque = NULL,
|
|
};
|
|
|
|
rt = new_runtime();
|
|
JS_SetSharedArrayBufferFunctions(rt, &funcs);
|
|
ctx = JS_NewContext(rt);
|
|
ret = eval(ctx,
|
|
"const sab = new SharedArrayBuffer(16, { maxByteLength: 16384 });"
|
|
"const u8 = new Uint8Array(sab);"
|
|
"sab.grow(16384);"
|
|
"u8[1024] === 0 && u8.byteLength === 16384");
|
|
assert(!JS_IsException(ret));
|
|
assert(JS_IsBool(ret));
|
|
assert(JS_VALUE_GET_BOOL(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void get_uint8array(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
JSValue val;
|
|
uint8_t *p;
|
|
size_t size;
|
|
uint8_t buf[3] = { 1, 2, 3 };
|
|
|
|
val = eval(ctx, "new Uint8Array(0)");
|
|
assert(!JS_IsException(val));
|
|
p = JS_GetUint8Array(ctx, &size, val);
|
|
assert(p != NULL);
|
|
assert(size == 0);
|
|
JS_FreeValue(ctx, val);
|
|
|
|
val = JS_NewUint8Array(ctx, NULL, 0, NULL, NULL, false);
|
|
assert(!JS_IsException(val));
|
|
p = JS_GetUint8Array(ctx, &size, val);
|
|
assert(p != NULL);
|
|
assert(size == 0);
|
|
JS_FreeValue(ctx, val);
|
|
|
|
val = JS_NewUint8ArrayCopy(ctx, NULL, 0);
|
|
assert(!JS_IsException(val));
|
|
p = JS_GetUint8Array(ctx, &size, val);
|
|
assert(p != NULL);
|
|
assert(size == 0);
|
|
JS_FreeValue(ctx, val);
|
|
|
|
val = JS_NewUint8ArrayCopy(ctx, buf, sizeof(buf));
|
|
assert(!JS_IsException(val));
|
|
p = JS_GetUint8Array(ctx, &size, val);
|
|
assert(p != NULL);
|
|
assert(size == 3);
|
|
assert(p[0] == 1 && p[1] == 2 && p[2] == 3);
|
|
JS_FreeValue(ctx, val);
|
|
|
|
val = eval(ctx, "new Uint8Array([4, 5, 6])");
|
|
assert(!JS_IsException(val));
|
|
p = JS_GetUint8Array(ctx, &size, val);
|
|
assert(p != NULL);
|
|
assert(size == 3);
|
|
assert(p[0] == 4 && p[1] == 5 && p[2] == 6);
|
|
JS_FreeValue(ctx, val);
|
|
|
|
val = eval(ctx, "new Int32Array(4)");
|
|
assert(!JS_IsException(val));
|
|
p = JS_GetUint8Array(ctx, &size, val);
|
|
assert(p == NULL);
|
|
assert(size == 0);
|
|
JS_FreeValue(ctx, val);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void new_symbol(void)
|
|
{
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
JSValue global = JS_GetGlobalObject(ctx);
|
|
JSValue sym, ret;
|
|
|
|
/* Local symbol with NULL description -> Symbol() */
|
|
sym = JS_NewSymbol(ctx, NULL, false);
|
|
assert(!JS_IsException(sym));
|
|
assert(JS_IsSymbol(sym));
|
|
JS_SetPropertyStr(ctx, global, "sym_local_null", sym);
|
|
|
|
ret = eval(ctx, "typeof sym_local_null === 'symbol' && sym_local_null.description === undefined && Symbol.keyFor(sym_local_null) === undefined");
|
|
assert(JS_IsBool(ret));
|
|
assert(JS_VALUE_GET_BOOL(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
|
|
/* Global symbol with NULL description -> Symbol.for() -> Symbol.for('undefined') */
|
|
sym = JS_NewSymbol(ctx, NULL, true);
|
|
assert(!JS_IsException(sym));
|
|
assert(JS_IsSymbol(sym));
|
|
JS_SetPropertyStr(ctx, global, "sym_global_null", sym);
|
|
|
|
ret = eval(ctx, "typeof sym_global_null === 'symbol' && sym_global_null.description === 'undefined' && Symbol.keyFor(sym_global_null) === 'undefined'");
|
|
assert(JS_IsBool(ret));
|
|
assert(JS_VALUE_GET_BOOL(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
|
|
/* Local symbol with description -> Symbol('test_local') */
|
|
sym = JS_NewSymbol(ctx, "test_local", false);
|
|
assert(!JS_IsException(sym));
|
|
assert(JS_IsSymbol(sym));
|
|
JS_SetPropertyStr(ctx, global, "sym_local_str", sym);
|
|
|
|
ret = eval(ctx, "sym_local_str.description === 'test_local' && Symbol.keyFor(sym_local_str) === undefined");
|
|
assert(JS_IsBool(ret));
|
|
assert(JS_VALUE_GET_BOOL(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
|
|
/* Global symbol with description -> Symbol.for('test_global') */
|
|
sym = JS_NewSymbol(ctx, "test_global", true);
|
|
assert(!JS_IsException(sym));
|
|
assert(JS_IsSymbol(sym));
|
|
JS_SetPropertyStr(ctx, global, "sym_global_str", sym);
|
|
|
|
ret = eval(ctx, "sym_global_str.description === 'test_global' && Symbol.keyFor(sym_global_str) === 'test_global'");
|
|
assert(JS_IsBool(ret));
|
|
assert(JS_VALUE_GET_BOOL(ret));
|
|
JS_FreeValue(ctx, ret);
|
|
|
|
JS_FreeValue(ctx, global);
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
static void bulk_free_macros(void) {
|
|
JSRuntime *rt = new_runtime();
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
|
|
JSValue val0 = JS_NewObject(ctx);
|
|
JSValue val1 = JS_NewArray(ctx);
|
|
JSValue val2 = JS_NewDate(ctx, 0.0);
|
|
|
|
JSMemoryUsage mem_usage;
|
|
JS_ComputeMemoryUsage(rt, &mem_usage);
|
|
int obj_count = mem_usage.obj_count;
|
|
|
|
JS_FreeValues(ctx, val0, val1);
|
|
JS_FreeValuesRT(rt, val2);
|
|
|
|
// silly atoms to ensure qjs doesn't find built-ins that match
|
|
JSAtom atom0 = JS_NewAtom(ctx, "ALL!!");
|
|
JSAtom atom1 = JS_NewAtom(ctx, "YOUR!!");
|
|
JSAtom atom2 = JS_NewAtom(ctx, "ATOMS!!");
|
|
JSAtom atom3 = JS_NewAtom(ctx, "ARE!!");
|
|
JSAtom atom4 = JS_NewAtom(ctx, "BELONG!!");
|
|
JSAtom atom5 = JS_NewAtom(ctx, "TO US!!");
|
|
|
|
JS_ComputeMemoryUsage(rt, &mem_usage);
|
|
assert((obj_count - 3) == mem_usage.obj_count);
|
|
int atom_count = mem_usage.atom_count;
|
|
|
|
JS_FreeAtoms(ctx, atom1, atom3, atom4);
|
|
JS_FreeAtomsRT(rt, atom0, atom2, atom5);
|
|
|
|
JS_ComputeMemoryUsage(rt, &mem_usage);
|
|
assert((atom_count - 6) == mem_usage.atom_count);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
cfunctions();
|
|
sync_call();
|
|
async_call();
|
|
async_call_stack_overflow();
|
|
raw_context_global_var();
|
|
is_array();
|
|
module_serde();
|
|
runtime_cstring_free();
|
|
utf16_string();
|
|
weak_map_gc_check();
|
|
promise_hook();
|
|
dump_memory_usage();
|
|
new_errors();
|
|
backtrace_oom_current_exception();
|
|
global_object_prototype();
|
|
slice_string_tocstring();
|
|
immutable_array_buffer();
|
|
shared_array_buffer_growth();
|
|
get_uint8array();
|
|
new_symbol();
|
|
bulk_free_macros();
|
|
return 0;
|
|
}
|