Merge branch 'fix_gc' into mcode_streamline

This commit is contained in:
2026-02-12 19:15:13 -06:00
4 changed files with 349 additions and 204 deletions

View File

@@ -547,7 +547,7 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
} }
/* String concat for ADD */ /* String concat for ADD */
if (op == MACH_ADD && JS_IsText(a) && JS_IsText(b)) if (op == MACH_ADD && mist_is_text(a) && mist_is_text(b))
return JS_ConcatString(ctx, a, b); return JS_ConcatString(ctx, a, b);
/* Comparison ops allow mixed types — return false for mismatches */ /* Comparison ops allow mixed types — return false for mismatches */
@@ -576,7 +576,7 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
} }
} }
/* String comparisons */ /* String comparisons */
if (JS_IsText(a) && JS_IsText(b)) { if (mist_is_text(a) && mist_is_text(b)) {
int cmp = js_string_compare_value(ctx, a, b, FALSE); int cmp = js_string_compare_value(ctx, a, b, FALSE);
switch (op) { switch (op) {
case MACH_EQ: return JS_NewBool(ctx, cmp == 0); case MACH_EQ: return JS_NewBool(ctx, cmp == 0);
@@ -696,7 +696,7 @@ void __asan_on_error(void) {
fprintf(stderr, "\n=== ASAN error: VM stack trace ===\n"); fprintf(stderr, "\n=== ASAN error: VM stack trace ===\n");
int is_first = 1; int is_first = 1;
while (frame) { while (frame) {
if (!JS_IsFunction(frame->function)) break; if (!mist_is_function(frame->function)) break;
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
const char *func_name = NULL; const char *func_name = NULL;
const char *file = NULL; const char *file = NULL;
@@ -882,7 +882,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JS_ToFloat64(ctx, &dt, tol); JS_ToFloat64(ctx, &dt, tol);
BOOL eq = fabs(da - db) <= dt; BOOL eq = fabs(da - db) <= dt;
frame->slots[a] = JS_NewBool(ctx, is_eq_op ? eq : !eq); frame->slots[a] = JS_NewBool(ctx, is_eq_op ? eq : !eq);
} else if (JS_IsText(left) && JS_IsText(right) && JS_VALUE_GET_TAG(tol) == JS_TAG_BOOL && JS_VALUE_GET_BOOL(tol)) { } else if (mist_is_text(left) && mist_is_text(right) && JS_VALUE_GET_TAG(tol) == JS_TAG_BOOL && JS_VALUE_GET_BOOL(tol)) {
BOOL eq = js_string_compare_value_nocase(ctx, left, right) == 0; BOOL eq = js_string_compare_value_nocase(ctx, left, right) == 0;
frame->slots[a] = JS_NewBool(ctx, is_eq_op ? eq : !eq); frame->slots[a] = JS_NewBool(ctx, is_eq_op ? eq : !eq);
} else { } else {
@@ -960,7 +960,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JSValue obj = frame->slots[b]; JSValue obj = frame->slots[b];
JSValue key = code->cpool[c]; JSValue key = code->cpool[c];
/* Non-proxy functions (arity != 2) can't have properties read */ /* Non-proxy functions (arity != 2) can't have properties read */
if (JS_IsFunction(obj)) { if (mist_is_function(obj)) {
JSFunction *fn_chk = JS_VALUE_GET_FUNCTION(obj); JSFunction *fn_chk = JS_VALUE_GET_FUNCTION(obj);
if (fn_chk->length != 2) { if (fn_chk->length != 2) {
JS_ThrowTypeError(ctx, "cannot read property of non-proxy function"); JS_ThrowTypeError(ctx, "cannot read property of non-proxy function");
@@ -1009,10 +1009,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
if (JS_IsInt(idx)) { if (JS_IsInt(idx)) {
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(idx), val); JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(idx), val);
ret = JS_IsException(r) ? -1 : 0; ret = JS_IsException(r) ? -1 : 0;
} else if (JS_IsArray(obj)) { } else if (mist_is_array(obj)) {
JS_ThrowTypeError(ctx, "array index must be a number"); JS_ThrowTypeError(ctx, "array index must be a number");
ret = -1; ret = -1;
} else if (JS_IsRecord(obj) && !JS_IsText(idx) && !JS_IsRecord(idx)) { } else if (mist_is_record(obj) && !mist_is_text(idx) && !mist_is_record(idx)) {
JS_ThrowTypeError(ctx, "object key must be a string or object"); JS_ThrowTypeError(ctx, "object key must be a string or object");
ret = -1; ret = -1;
} else { } else {
@@ -1219,7 +1219,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
/* push R(B) onto array R(A) */ /* push R(B) onto array R(A) */
JSValue arr = frame->slots[a]; JSValue arr = frame->slots[a];
JSValue val = frame->slots[b]; JSValue val = frame->slots[b];
if (!JS_IsArray(arr)) { if (!mist_is_array(arr)) {
JS_ThrowTypeError(ctx, "cannot push to non-array"); JS_ThrowTypeError(ctx, "cannot push to non-array");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt; goto disrupt;
@@ -1238,7 +1238,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
case MACH_POP: { case MACH_POP: {
/* R(A) = pop last element from array R(B) */ /* R(A) = pop last element from array R(B) */
JSValue arr = frame->slots[b]; JSValue arr = frame->slots[b];
if (!JS_IsArray(arr)) { if (!mist_is_array(arr)) {
JS_ThrowTypeError(ctx, "cannot pop from non-array"); JS_ThrowTypeError(ctx, "cannot pop from non-array");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt; goto disrupt;
@@ -1482,7 +1482,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
frame->slots[a] = JS_NewBool(ctx, JS_IsNumber(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, JS_IsNumber(frame->slots[b]));
break; break;
case MACH_IS_TEXT: case MACH_IS_TEXT:
frame->slots[a] = JS_NewBool(ctx, JS_IsText(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, mist_is_text(frame->slots[b]));
break; break;
case MACH_IS_BOOL: case MACH_IS_BOOL:
frame->slots[a] = JS_NewBool(ctx, JS_IsBool(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, JS_IsBool(frame->slots[b]));
@@ -1491,49 +1491,42 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
frame->slots[a] = JS_NewBool(ctx, JS_IsNull(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, JS_IsNull(frame->slots[b]));
break; break;
case MACH_IS_ARRAY: case MACH_IS_ARRAY:
frame->slots[a] = JS_NewBool(ctx, JS_IsArray(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, mist_is_array(frame->slots[b]));
break; break;
case MACH_IS_FUNC: case MACH_IS_FUNC:
frame->slots[a] = JS_NewBool(ctx, JS_IsFunction(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, mist_is_function(frame->slots[b]));
break; break;
case MACH_IS_RECORD: case MACH_IS_RECORD:
frame->slots[a] = JS_NewBool(ctx, JS_IsRecord(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, mist_is_record(frame->slots[b]));
break; break;
case MACH_IS_STONE: case MACH_IS_STONE:
frame->slots[a] = JS_NewBool(ctx, JS_IsStone(frame->slots[b])); frame->slots[a] = JS_NewBool(ctx, mist_is_stone(frame->slots[b]));
break; break;
case MACH_LENGTH: { case MACH_LENGTH: {
JSValue res = JS_CellLength(ctx, frame->slots[b]); JSValue v = frame->slots[b];
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (mist_is_array(v)) {
frame->slots[a] = res; JSArray *arr = JS_VALUE_GET_ARRAY(v);
frame->slots[a] = JS_NewInt32(ctx, (int32_t)arr->len);
} else if (MIST_IsImmediateASCII(v)) {
frame->slots[a] = JS_NewInt32(ctx, MIST_GetImmediateASCIILen(v));
} else {
/* fallback to C for text/blob/function (still a GC safepoint) */
JSValue res = JS_CellLength(ctx, v);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
frame->slots[a] = res;
}
break; break;
} }
case MACH_IS_PROXY: { case MACH_IS_PROXY: {
JSValue v = frame->slots[b]; JSValue v = frame->slots[b];
int is_proxy = 0; int is_proxy = 0;
if (JS_IsFunction(v)) { if (mist_is_function(v)) {
JSFunction *fn = JS_VALUE_GET_FUNCTION(v); JSFunction *fn = JS_VALUE_GET_FUNCTION(v);
is_proxy = (fn->length == 2); is_proxy = (fn->length == 2);
} }
frame->slots[a] = JS_NewBool(ctx, is_proxy); frame->slots[a] = JS_NewBool(ctx, is_proxy);
break; break;
} }
case MACH_TYPEOF: {
JSValue val = frame->slots[b];
const char *tname = "unknown";
if (JS_IsNull(val)) tname = "null";
else if (JS_IsInt(val) || JS_IsNumber(val)) tname = "number";
else if (JS_IsBool(val)) tname = "logical";
else if (JS_IsText(val)) tname = "text";
else if (JS_IsFunction(val)) tname = "function";
else if (JS_IsArray(val)) tname = "array";
else if (JS_IsRecord(val)) tname = "object";
JSValue res = JS_NewString(ctx, tname);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
frame->slots[a] = res;
break;
}
/* Logical */ /* Logical */
case MACH_NOT: { case MACH_NOT: {
int bval = JS_ToBool(ctx, frame->slots[b]); int bval = JS_ToBool(ctx, frame->slots[b]);
@@ -1580,7 +1573,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
case MACH_LOAD_FIELD: { case MACH_LOAD_FIELD: {
JSValue obj = frame->slots[b]; JSValue obj = frame->slots[b];
JSValue key = code->cpool[c]; JSValue key = code->cpool[c];
if (JS_IsFunction(obj)) { if (mist_is_function(obj)) {
JS_ThrowTypeError(ctx, "cannot read property of function"); JS_ThrowTypeError(ctx, "cannot read property of function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt; goto disrupt;
@@ -1648,10 +1641,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
if (JS_IsInt(key)) { if (JS_IsInt(key)) {
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(key), val); JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(key), val);
ret = JS_IsException(r) ? -1 : 0; ret = JS_IsException(r) ? -1 : 0;
} else if (JS_IsArray(obj)) { } else if (mist_is_array(obj)) {
JS_ThrowTypeError(ctx, "array index must be a number"); JS_ThrowTypeError(ctx, "array index must be a number");
ret = -1; ret = -1;
} else if (JS_IsBool(key) || JS_IsNull(key) || JS_IsArray(key) || JS_IsFunction(key)) { } else if (JS_IsBool(key) || JS_IsNull(key) || mist_is_array(key) || mist_is_function(key)) {
JS_ThrowTypeError(ctx, "object key must be text"); JS_ThrowTypeError(ctx, "object key must be text");
ret = -1; ret = -1;
} else { } else {
@@ -1676,7 +1669,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
case MACH_GOFRAME: { case MACH_GOFRAME: {
/* A=frame_slot, B=func_reg, C=argc */ /* A=frame_slot, B=func_reg, C=argc */
JSValue func_val = frame->slots[b]; JSValue func_val = frame->slots[b];
if (!JS_IsFunction(func_val)) { if (!mist_is_function(func_val)) {
JS_ThrowTypeError(ctx, "not a function"); JS_ThrowTypeError(ctx, "not a function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt; goto disrupt;
@@ -1840,7 +1833,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JSFrameRegister *trace_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); JSFrameRegister *trace_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
int first = 1; int first = 1;
while (trace_frame) { while (trace_frame) {
if (!JS_IsFunction(trace_frame->function)) break; if (!mist_is_function(trace_frame->function)) break;
JSFunction *trace_fn = JS_VALUE_GET_FUNCTION(trace_frame->function); JSFunction *trace_fn = JS_VALUE_GET_FUNCTION(trace_frame->function);
if (trace_fn->kind == JS_FUNC_KIND_REGISTER && trace_fn->u.reg.code) { if (trace_fn->kind == JS_FUNC_KIND_REGISTER && trace_fn->u.reg.code) {
JSCodeRegister *tc = trace_fn->u.reg.code; JSCodeRegister *tc = trace_fn->u.reg.code;
@@ -2449,7 +2442,6 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
else if (strcmp(op, "is_stone") == 0) { AB2(MACH_IS_STONE); } else if (strcmp(op, "is_stone") == 0) { AB2(MACH_IS_STONE); }
else if (strcmp(op, "length") == 0) { AB2(MACH_LENGTH); } else if (strcmp(op, "length") == 0) { AB2(MACH_LENGTH); }
else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); } else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); }
else if (strcmp(op, "typeof") == 0) { AB2(MACH_TYPEOF); }
/* Logical */ /* Logical */
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); } else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); } else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); }

View File

@@ -399,6 +399,72 @@ typedef struct { uint16_t line; uint16_t col; } MachLineEntry;
#define MACH_GET_sBx(i) ((int16_t)((i) >> 16)) #define MACH_GET_sBx(i) ((int16_t)((i) >> 16))
#define MACH_GET_sJ(i) ((int32_t)((i) & 0xFFFFFF00) >> 8) #define MACH_GET_sJ(i) ((int32_t)((i) & 0xFFFFFF00) >> 8)
/* ============================================================
GC Safepoint Classification for the MACH VM Dispatch Loop
============================================================
Every opcode falls into one of three categories:
[P] Pure inline — never calls C, never allocates. No GC possible.
No frame re-derivation needed after execution.
[N] Non-allocating C call — calls a C function that is guaranteed
to never allocate (e.g. JS_ToBool, js_string_compare_value).
No frame re-derivation needed.
[G] GC safepoint — calls C that may allocate, triggering GC.
After the call, all heap pointers (including `frame`) MUST be
re-derived via: frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
The 18 C entry points that can allocate (GC safepoints):
1. JS_GetProperty — key interning for string keys >7 chars
2. JS_SetProperty — rec_resize when record grows
3. JS_GetPropertyNumber — text substring extraction
4. JS_SetPropertyNumber — array grow
5. JS_NewObject — allocates record
6. JS_NewArray / JS_NewArrayLen — allocates array
7. js_new_register_function — allocates function object (closure)
8. alloc_frame_register — allocates frame via js_mallocz
9. js_call_c_function — arbitrary C code
10. JS_CallInternal — arbitrary bytecode
11. JS_Call — arbitrary call
12. JS_ConcatString — allocates new string
13. JS_ArrayPush — array grow
14. JS_ArrayPop — reads, but frame refresh needed
15. JS_DeleteProperty — mutates record
16. JS_HasProperty — complex traversal
17. js_regexp_constructor — allocates regex
18. reg_vm_binop — polymorphic dispatch (legacy opcodes)
Opcode-level classification:
[P] LOADK, LOADI, LOADNULL, LOADTRUE, LOADFALSE, MOVE, NOP
[P] ADD_INT..MOD_INT, NEG_INT, ADD_FLOAT..MOD_FLOAT, NEG_FLOAT
[P] EQ_INT..GE_INT, EQ_FLOAT..GE_FLOAT, EQ_BOOL, NE_BOOL
[P] IS_IDENTICAL, IS_INT, IS_NUM, IS_TEXT, IS_BOOL, IS_NULL
[P] IS_ARRAY, IS_FUNC, IS_RECORD, IS_STONE, IS_PROXY
[P] NOT, AND, OR, BITNOT, BITAND, BITOR, BITXOR
[P] JMP, JMPTRUE, JMPFALSE, JMPNULL, JMPNOTNULL
[P] RETURN, RETNIL, SETARG, GETUP, SETUP, DISRUPT, THROW
[P] LENGTH (array + imm-ASCII fast path only; text/blob fallback is [G])
[N] EQ_TEXT..GE_TEXT (js_string_compare_value — no allocation)
[N] LNOT (JS_ToBool — no allocation)
[G] ADD..USHR, NEG, INC, DEC, EQ..GE (legacy: reg_vm_binop)
[G] EQ_TOL, NEQ_TOL (tolerance comparison, may fall back)
[G] CONCAT (JS_ConcatString)
[G] GETFIELD, SETFIELD, GETINDEX, SETINDEX (property access)
[G] LOAD_FIELD, STORE_FIELD, LOAD_INDEX, STORE_INDEX
[G] LOAD_DYNAMIC, STORE_DYNAMIC
[G] GETNAME, GETINTRINSIC, GETENV, SET_VAR
[G] NEWOBJECT, NEWRECORD, NEWARRAY (object/array creation)
[G] CLOSURE (js_new_register_function)
[G] FRAME, GOFRAME (alloc_frame_register)
[G] INVOKE, GOINVOKE (function calls)
[G] PUSH (JS_ArrayPush), POP (JS_ArrayPop)
[G] DELETE, DELETEINDEX (JS_DeleteProperty)
[G] HASPROP, IN (JS_HasProperty)
[G] REGEXP (js_regexp_constructor)
============================================================ */
typedef enum MachOpcode { typedef enum MachOpcode {
/* === Legacy opcodes (used by existing .mach files) === */ /* === Legacy opcodes (used by existing .mach files) === */
@@ -544,7 +610,6 @@ typedef enum MachOpcode {
MACH_IS_TEXT, /* R(A) = is_text(R(B)) */ MACH_IS_TEXT, /* R(A) = is_text(R(B)) */
MACH_IS_BOOL, /* R(A) = is_bool(R(B)) */ MACH_IS_BOOL, /* R(A) = is_bool(R(B)) */
MACH_IS_NULL, /* R(A) = is_null(R(B)) */ MACH_IS_NULL, /* R(A) = is_null(R(B)) */
MACH_TYPEOF, /* R(A) = typeof(R(B)) */
/* Logical (mcode-style) */ /* Logical (mcode-style) */
MACH_NOT, /* R(A) = !R(B) — boolean not (AB) */ MACH_NOT, /* R(A) = !R(B) — boolean not (AB) */
@@ -697,7 +762,6 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_IS_TEXT] = "is_text", [MACH_IS_TEXT] = "is_text",
[MACH_IS_BOOL] = "is_bool", [MACH_IS_BOOL] = "is_bool",
[MACH_IS_NULL] = "is_null", [MACH_IS_NULL] = "is_null",
[MACH_TYPEOF] = "typeof",
[MACH_NOT] = "not", [MACH_NOT] = "not",
[MACH_AND] = "and", [MACH_AND] = "and",
[MACH_OR] = "or", [MACH_OR] = "or",
@@ -799,6 +863,38 @@ static inline objhdr_t *chase(JSValue v) {
return oh; return oh;
} }
/* Inline type checks — use these in the VM dispatch loop to avoid
function call overhead. The public API (JS_IsArray etc. in quickjs.h)
remains non-inline for external callers; those wrappers live in runtime.c. */
static inline JS_BOOL mist_is_gc_object(JSValue v) {
return JS_IsPtr(v);
}
static inline JS_BOOL mist_is_array(JSValue v) {
return mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_ARRAY;
}
static inline JS_BOOL mist_is_record(JSValue v) {
return mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_RECORD;
}
static inline JS_BOOL mist_is_function(JSValue v) {
return mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_FUNCTION;
}
static inline JS_BOOL mist_is_text(JSValue v) {
return MIST_IsImmediateASCII(v)
|| (mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_TEXT);
}
static inline JS_BOOL mist_is_blob(JSValue v) {
return mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_BLOB;
}
static inline JS_BOOL mist_is_stone(JSValue v) {
return !mist_is_gc_object(v) || objhdr_s(*chase(v));
}
/* Intrinsic array type - tagged as JS_TAG_PTR with mist_hdr type OBJ_ARRAY */ /* Intrinsic array type - tagged as JS_TAG_PTR with mist_hdr type OBJ_ARRAY */
typedef struct JSArray { typedef struct JSArray {

View File

@@ -298,25 +298,34 @@ typedef JSValue JSCFunctionData (JSContext *ctx, JSValue this_val,
int argc, JSValue *argv, int magic, int argc, JSValue *argv, int magic,
JSValue *data); JSValue *data);
JSValue JS_Stone (JSContext *ctx, JSValue this_val); /* ============================================================
1. Runtime / Context Lifecycle
============================================================ */
JSRuntime *JS_NewRuntime (void); JSRuntime *JS_NewRuntime (void);
/* info lifetime must exceed that of rt */
void JS_SetMemoryLimit (JSRuntime *rt, size_t limit);
/* use 0 to disable maximum stack size check */
void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size);
void JS_FreeRuntime (JSRuntime *rt); void JS_FreeRuntime (JSRuntime *rt);
void JS_SetMemoryLimit (JSRuntime *rt, size_t limit);
JSContext *JS_NewContext (JSRuntime *rt); JSContext *JS_NewContext (JSRuntime *rt);
JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size);
void JS_FreeContext (JSContext *s); void JS_FreeContext (JSContext *s);
void *JS_GetContextOpaque (JSContext *ctx); void *JS_GetContextOpaque (JSContext *ctx);
void JS_SetContextOpaque (JSContext *ctx, void *opaque); void JS_SetContextOpaque (JSContext *ctx, void *opaque);
JSRuntime *JS_GetRuntime (JSContext *ctx); JSRuntime *JS_GetRuntime (JSContext *ctx);
void JS_SetClassProto (JSContext *ctx, JSClassID class_id, JSValue obj); /* use 0 to disable maximum stack size check */
JSValue JS_GetClassProto (JSContext *ctx, JSClassID class_id); void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size);
/* should be called when changing thread to update the stack top value
used to check stack overflow. */
void JS_UpdateStackTop (JSContext *ctx);
JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size); /* return != 0 if the JS code needs to be interrupted */
typedef int JSInterruptHandler (JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler (JSContext *ctx, JSInterruptHandler *cb,
void *opaque);
JS_BOOL JS_IsLiveObject (JSRuntime *rt, JSValue obj);
/* Class system */
typedef void JSClassFinalizer (JSRuntime *rt, JSValue val); typedef void JSClassFinalizer (JSRuntime *rt, JSValue val);
typedef JSValue JSClassCall (JSContext *ctx, JSValue func_obj, typedef JSValue JSClassCall (JSContext *ctx, JSValue func_obj,
JSValue this_val, int argc, JSValue this_val, int argc,
@@ -329,6 +338,7 @@ typedef struct JSClassDef {
} JSClassDef; } JSClassDef;
#define JS_INVALID_CLASS_ID 0 #define JS_INVALID_CLASS_ID 0
extern JSClassID js_class_id_alloc;
JSClassID JS_NewClassID (JSClassID *pclass_id); JSClassID JS_NewClassID (JSClassID *pclass_id);
/* Returns the class ID if `v` is an object, otherwise returns /* Returns the class ID if `v` is an object, otherwise returns
* JS_INVALID_CLASS_ID. */ * JS_INVALID_CLASS_ID. */
@@ -336,22 +346,12 @@ JSClassID JS_GetClassID (JSValue v);
int JS_NewClass (JSContext *ctx, JSClassID class_id, int JS_NewClass (JSContext *ctx, JSClassID class_id,
const JSClassDef *class_def); const JSClassDef *class_def);
int JS_IsRegisteredClass (JSContext *ctx, JSClassID class_id); int JS_IsRegisteredClass (JSContext *ctx, JSClassID class_id);
void JS_SetClassProto (JSContext *ctx, JSClassID class_id, JSValue obj);
extern JSClassID js_class_id_alloc; JSValue JS_GetClassProto (JSContext *ctx, JSClassID class_id);
/* ============================================================ /* ============================================================
Copying GC - No Reference Counting Needed 2. Value Creation and Type Checks
============================================================
With a copying GC, reference counting is not needed since all live
objects are discovered by tracing from roots. These macros make
existing DupValue/FreeValue calls into no-ops.
============================================================ */ ============================================================ */
#define JS_DupValue(ctx, v) (v)
#define JS_FreeValue(ctx, v) ((void)0)
#define JS_DupValueRT(rt, v) (v)
#define JS_FreeValueRT(rt, v) ((void)0)
/* value handling */
static inline JSValue static inline JSValue
JS_NewBool (JSContext *ctx, JS_BOOL val) { JS_NewBool (JSContext *ctx, JS_BOOL val) {
@@ -403,6 +403,7 @@ JS_NewFloat64 (JSContext *ctx, double d) {
return __JS_NewFloat64 (ctx, d); return __JS_NewFloat64 (ctx, d);
} }
/* Inline type checks (immediate tags) */
static inline JS_BOOL JS_IsNumber (JSValue v) { static inline JS_BOOL JS_IsNumber (JSValue v) {
int tag = JS_VALUE_GET_TAG (v); int tag = JS_VALUE_GET_TAG (v);
return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64 (tag); return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64 (tag);
@@ -420,7 +421,7 @@ static inline JS_BOOL JS_IsException (JSValue v) {
return (JS_VALUE_GET_TAG (v) == JS_TAG_EXCEPTION); return (JS_VALUE_GET_TAG (v) == JS_TAG_EXCEPTION);
} }
/* Immediate String Helpers */ /* Immediate ASCII string helpers */
#define MIST_ASCII_MAX_LEN 7 #define MIST_ASCII_MAX_LEN 7
static inline JS_BOOL static inline JS_BOOL
@@ -448,6 +449,8 @@ static inline JSValue MIST_TryNewImmediateASCII (const char *str, size_t len) {
return v; return v;
} }
/* Heap object type checks (non-inline — see mist_is_* in quickjs-internal.h
for inline versions used by the VM dispatch loop) */
JS_BOOL JS_IsArray(JSValue v); JS_BOOL JS_IsArray(JSValue v);
JS_BOOL JS_IsRecord(JSValue v); JS_BOOL JS_IsRecord(JSValue v);
#define JS_IsObject JS_IsRecord #define JS_IsObject JS_IsRecord
@@ -456,9 +459,90 @@ JS_BOOL JS_IsBlob(JSValue v);
JS_BOOL JS_IsText(JSValue v); JS_BOOL JS_IsText(JSValue v);
JS_BOOL JS_IsStone(JSValue v); JS_BOOL JS_IsStone(JSValue v);
// Fundamental /* ============================================================
3. GC References
============================================================
With a copying GC, reference counting is not needed since all live
objects are discovered by tracing from roots. These macros make
existing DupValue/FreeValue calls into no-ops.
============================================================ */
#define JS_DupValue(ctx, v) (v)
#define JS_FreeValue(ctx, v) ((void)0)
#define JS_DupValueRT(rt, v) (v)
#define JS_FreeValueRT(rt, v) ((void)0)
/* ============================================================
4. Property Access
============================================================ */
JSValue JS_GetProperty (JSContext *ctx, JSValue this_obj, JSValue prop);
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val);
JSValue JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop);
int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSValue val);
JSValue JS_GetPropertyNumber (JSContext *ctx, JSValue this_obj, int idx);
JSValue JS_SetPropertyNumber (JSContext *ctx, JSValue obj, int idx, JSValue val);
JSValue JS_GetPrototype (JSContext *ctx, JSValue val);
JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj);
int JS_GetLength (JSContext *ctx, JSValue obj, int64_t *pres); int JS_GetLength (JSContext *ctx, JSValue obj, int64_t *pres);
void JS_SetOpaque (JSValue obj, void *opaque);
void *JS_GetOpaque (JSValue obj, JSClassID class_id);
void *JS_GetOpaque2 (JSContext *ctx, JSValue obj, JSClassID class_id);
void *JS_GetAnyOpaque (JSValue obj, JSClassID *class_id);
/* ============================================================
5. Object / Array / String Creation
============================================================ */
JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto, JSClassID class_id);
JSValue JS_NewObjectClass (JSContext *ctx, int class_id);
JSValue JS_NewObjectProto (JSContext *ctx, JSValue proto);
JSValue JS_NewObject (JSContext *ctx);
JSValue JS_NewArray (JSContext *ctx);
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values);
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);
JSValue JS_NewStringLen (JSContext *ctx, const char *str1, size_t len1);
static inline JSValue JS_NewString (JSContext *ctx, const char *str) {
return JS_NewStringLen (ctx, str, strlen (str));
}
/* ============================================================
6. Type Conversion
============================================================ */
int JS_ToBool (JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */
int JS_ToInt32 (JSContext *ctx, int32_t *pres, JSValue val);
static inline int JS_ToUint32 (JSContext *ctx, uint32_t *pres, JSValue val) {
return JS_ToInt32 (ctx, (int32_t *)pres, val);
}
int JS_ToInt64 (JSContext *ctx, int64_t *pres, JSValue val);
int JS_ToFloat64 (JSContext *ctx, double *pres, JSValue val);
JSValue JS_ToString (JSContext *ctx, JSValue val);
JSValue JS_ToPropertyKey (JSContext *ctx, JSValue val);
const char *JS_ToCStringLen2 (JSContext *ctx, size_t *plen, JSValue val1, JS_BOOL cesu8);
static inline const char * JS_ToCStringLen (JSContext *ctx, size_t *plen, JSValue val1) {
return JS_ToCStringLen2 (ctx, plen, val1, 0);
}
static inline const char * JS_ToCString (JSContext *ctx, JSValue val1) {
return JS_ToCStringLen2 (ctx, NULL, val1, 0);
}
void JS_FreeCString (JSContext *ctx, const char *ptr);
JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2);
/* ============================================================
7. Error Handling
============================================================ */
JSValue JS_Throw (JSContext *ctx, JSValue obj); JSValue JS_Throw (JSContext *ctx, JSValue obj);
JSValue JS_GetException (JSContext *ctx); JSValue JS_GetException (JSContext *ctx);
JS_BOOL JS_HasException (JSContext *ctx); JS_BOOL JS_HasException (JSContext *ctx);
@@ -474,44 +558,28 @@ JSValue __js_printf_like (2, 3)
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...); JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
JSValue JS_ThrowOutOfMemory (JSContext *ctx); JSValue JS_ThrowOutOfMemory (JSContext *ctx);
// TODO: rename this to just "eq" /* ============================================================
JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2); 8. Function Creation and Invocation
============================================================ */
int JS_ToBool (JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */ JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
int JS_ToInt32 (JSContext *ctx, int32_t *pres, JSValue val); JSValue JS_Stone (JSContext *ctx, JSValue this_val);
static inline int JS_ToUint32 (JSContext *ctx, uint32_t *pres, JSValue val) {
return JS_ToInt32 (ctx, (int32_t *)pres, val);
}
int JS_ToInt64 (JSContext *ctx, int64_t *pres, JSValue val);
int JS_ToFloat64 (JSContext *ctx, double *pres, JSValue val);
/* return an exception if 'val' is a Number */
JSValue JS_NewStringLen (JSContext *ctx, const char *str1, size_t len1); /* JSON */
static inline JSValue JS_NewString (JSContext *ctx, const char *str) { /* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
return JS_NewStringLen (ctx, str, strlen (str)); JSValue JS_ParseJSON (JSContext *ctx, const char *buf, size_t buf_len,
} const char *filename);
JSValue JS_ToString (JSContext *ctx, JSValue val); #define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */
const char *JS_ToCStringLen2 (JSContext *ctx, size_t *plen, JSValue val1, JS_BOOL cesu8); JSValue JS_ParseJSON2 (JSContext *ctx, const char *buf, size_t buf_len,
static inline const char * JS_ToCStringLen (JSContext *ctx, size_t *plen, JSValue val1) { const char *filename, int flags);
return JS_ToCStringLen2 (ctx, plen, val1, 0); JSValue JS_JSONStringify (JSContext *ctx, JSValue obj,
} JSValue replacer, JSValue space0);
static inline const char * JS_ToCString (JSContext *ctx, JSValue val1) {
return JS_ToCStringLen2 (ctx, NULL, val1, 0);
}
void JS_FreeCString (JSContext *ctx, const char *ptr);
JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto, JSClassID class_id); /* ============================================================
JSValue JS_NewObjectClass (JSContext *ctx, int class_id); 9. Intrinsic Wrappers (JS_Cell* / JS_Array*)
JSValue JS_NewObjectProto (JSContext *ctx, JSValue proto); ============================================================ */
JSValue JS_NewObject (JSContext *ctx);
JSValue JS_NewArray (JSContext *ctx); /* Intrinsic array operations */
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);
/* Intrinsic array operations - signatures match internal functions */
JSValue JS_Array (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3); JSValue JS_Array (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3);
JSValue JS_ArrayFilter (JSContext *ctx, JSValue arr, JSValue fn); JSValue JS_ArrayFilter (JSContext *ctx, JSValue arr, JSValue fn);
JSValue JS_ArraySort (JSContext *ctx, JSValue arr, JSValue selector); JSValue JS_ArraySort (JSContext *ctx, JSValue arr, JSValue selector);
@@ -519,9 +587,7 @@ JSValue JS_ArrayFind (JSContext *ctx, JSValue arr, JSValue target_or_fn, JSValue
JSValue JS_ArrFor (JSContext *ctx, JSValue arr, JSValue fn, JSValue reverse, JSValue exit_val); JSValue JS_ArrFor (JSContext *ctx, JSValue arr, JSValue fn, JSValue reverse, JSValue exit_val);
JSValue JS_ArrayReduce (JSContext *ctx, JSValue arr, JSValue fn, JSValue initial, JSValue reverse); JSValue JS_ArrayReduce (JSContext *ctx, JSValue arr, JSValue fn, JSValue initial, JSValue reverse);
/* Cell intrinsic functions - C API wrappers */ /* Core cell functions */
/* Core functions */
JSValue JS_CellStone (JSContext *ctx, JSValue val); JSValue JS_CellStone (JSContext *ctx, JSValue val);
JSValue JS_CellLength (JSContext *ctx, JSValue val); JSValue JS_CellLength (JSContext *ctx, JSValue val);
JSValue JS_CellReverse (JSContext *ctx, JSValue val); JSValue JS_CellReverse (JSContext *ctx, JSValue val);
@@ -534,7 +600,7 @@ JSValue JS_CellModulo (JSContext *ctx, JSValue a, JSValue b);
JSValue JS_CellNeg (JSContext *ctx, JSValue val); JSValue JS_CellNeg (JSContext *ctx, JSValue val);
JSValue JS_CellNot (JSContext *ctx, JSValue val); JSValue JS_CellNot (JSContext *ctx, JSValue val);
/* Text functions */ /* Text cell functions */
JSValue JS_CellText (JSContext *ctx, JSValue val); JSValue JS_CellText (JSContext *ctx, JSValue val);
JSValue JS_CellLower (JSContext *ctx, JSValue text); JSValue JS_CellLower (JSContext *ctx, JSValue text);
JSValue JS_CellUpper (JSContext *ctx, JSValue text); JSValue JS_CellUpper (JSContext *ctx, JSValue text);
@@ -545,7 +611,7 @@ JSValue JS_CellSearch (JSContext *ctx, JSValue text, JSValue pattern, JSValue fr
JSValue JS_CellExtract (JSContext *ctx, JSValue text, JSValue from, JSValue to); JSValue JS_CellExtract (JSContext *ctx, JSValue text, JSValue from, JSValue to);
JSValue JS_CellCharacter (JSContext *ctx, JSValue codepoint); JSValue JS_CellCharacter (JSContext *ctx, JSValue codepoint);
/* Number functions */ /* Number cell functions */
JSValue JS_CellNumber (JSContext *ctx, JSValue val); JSValue JS_CellNumber (JSContext *ctx, JSValue val);
JSValue JS_CellAbs (JSContext *ctx, JSValue num); JSValue JS_CellAbs (JSContext *ctx, JSValue num);
JSValue JS_CellSign (JSContext *ctx, JSValue num); JSValue JS_CellSign (JSContext *ctx, JSValue num);
@@ -559,56 +625,20 @@ JSValue JS_CellMin (JSContext *ctx, JSValue a, JSValue b);
JSValue JS_CellMax (JSContext *ctx, JSValue a, JSValue b); JSValue JS_CellMax (JSContext *ctx, JSValue a, JSValue b);
JSValue JS_CellRemainder (JSContext *ctx, JSValue a, JSValue b); JSValue JS_CellRemainder (JSContext *ctx, JSValue a, JSValue b);
/* Object functions */ /* Object cell functions */
JSValue JS_CellObject (JSContext *ctx, JSValue proto, JSValue props); JSValue JS_CellObject (JSContext *ctx, JSValue proto, JSValue props);
/* Format function */ /* Format */
JSValue JS_CellFormat (JSContext *ctx, JSValue text, JSValue collection, JSValue transformer); JSValue JS_CellFormat (JSContext *ctx, JSValue text, JSValue collection, JSValue transformer);
/* Helper functions */ /* Output helpers */
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values);
void JS_PrintText (JSContext *ctx, JSValue val); void JS_PrintText (JSContext *ctx, JSValue val);
void JS_PrintTextLn (JSContext *ctx, JSValue val); void JS_PrintTextLn (JSContext *ctx, JSValue val);
void JS_PrintFormatted (JSContext *ctx, const char *fmt, int count, JSValue *values); void JS_PrintFormatted (JSContext *ctx, const char *fmt, int count, JSValue *values);
JSValue JS_GetProperty (JSContext *ctx, JSValue this_obj, JSValue prop); /* ============================================================
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val); 10. C Function Definition
============================================================ */
// For records
JSValue JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop);
int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSValue val);
// Must be an array
JSValue JS_GetPropertyNumber (JSContext *ctx, JSValue this_obj, int idx);
JSValue JS_SetPropertyNumber (JSContext *ctx, JSValue obj, int idx, JSValue val);
JSValue JS_GetPrototype (JSContext *ctx, JSValue val);
/* Get property keys as array of text */
JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj);
JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
void JS_SetOpaque (JSValue obj, void *opaque);
void *JS_GetOpaque (JSValue obj, JSClassID class_id);
void *JS_GetOpaque2 (JSContext *ctx, JSValue obj, JSClassID class_id);
void *JS_GetAnyOpaque (JSValue obj, JSClassID *class_id);
/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
JSValue JS_ParseJSON (JSContext *ctx, const char *buf, size_t buf_len,
const char *filename);
#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */
JSValue JS_ParseJSON2 (JSContext *ctx, const char *buf, size_t buf_len,
const char *filename, int flags);
JSValue JS_JSONStringify (JSContext *ctx, JSValue obj,
JSValue replacer, JSValue space0);
/* return != 0 if the JS code needs to be interrupted */
typedef int JSInterruptHandler (JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler (JSContext *ctx, JSInterruptHandler *cb,
void *opaque);
/* C function definition */
typedef enum JSCFunctionEnum { typedef enum JSCFunctionEnum {
JS_CFUNC_generic, JS_CFUNC_generic,
JS_CFUNC_generic_magic, JS_CFUNC_generic_magic,
@@ -868,7 +898,27 @@ typedef struct JSCFunctionListEntry {
int JS_SetPropertyFunctionList (JSContext *ctx, JSValue obj, int JS_SetPropertyFunctionList (JSContext *ctx, JSValue obj,
const JSCFunctionListEntry *tab, int len); const JSCFunctionListEntry *tab, int len);
/* debug value output */ /* ============================================================
11. Debug / Dump Utilities
============================================================ */
typedef struct JSMemoryUsage {
int64_t malloc_size, malloc_limit, memory_used_size;
int64_t malloc_count;
int64_t memory_used_count;
int64_t str_count, str_size;
int64_t obj_count, obj_size;
int64_t prop_count, prop_size;
int64_t shape_count, shape_size;
int64_t js_func_count, js_func_size, js_func_code_size;
int64_t js_func_pc2line_count, js_func_pc2line_size;
int64_t c_func_count, array_count;
int64_t fast_array_count, fast_array_elements;
int64_t binary_object_count, binary_object_size;
} JSMemoryUsage;
void JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s);
void JS_DumpMemoryUsage (FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
typedef struct { typedef struct {
JS_BOOL show_hidden : 8; /* only show enumerable properties */ JS_BOOL show_hidden : 8; /* only show enumerable properties */
@@ -913,7 +963,20 @@ typedef void (*js_hook) (JSContext *, int type, js_debug *dbg, void *user);
#define JS_HOOK_GC 8 #define JS_HOOK_GC 8
void js_debug_sethook (JSContext *ctx, js_hook, int type, void *user); void js_debug_sethook (JSContext *ctx, js_hook, int type, void *user);
/* Memory allocation functions (bump allocator) */ uint32_t js_debugger_stack_depth (JSContext *ctx);
JSValue js_debugger_backtrace_fns (JSContext *ctx);
JSValue js_debugger_closure_variables (JSContext *ctx, JSValue fn);
JSValue js_debugger_local_variables (JSContext *ctx, int stack_index);
void js_debugger_set_closure_variable (JSContext *js, JSValue fn,
JSValue var_name, JSValue val);
JSValue js_debugger_build_backtrace (JSContext *ctx);
JSValue js_debugger_fn_info (JSContext *ctx, JSValue fn);
JSValue js_debugger_fn_bytecode (JSContext *js, JSValue fn);
void *js_debugger_val_address (JSContext *js, JSValue val);
/* ============================================================
12. Memory Allocation
============================================================ */
void *js_malloc (JSContext *ctx, size_t size); void *js_malloc (JSContext *ctx, size_t size);
void *js_mallocz (JSContext *ctx, size_t size); void *js_mallocz (JSContext *ctx, size_t size);
void *js_realloc (JSContext *ctx, void *ptr, size_t size); void *js_realloc (JSContext *ctx, void *ptr, size_t size);
@@ -925,9 +988,11 @@ void *js_malloc_rt (size_t size);
void *js_mallocz_rt (size_t size); void *js_mallocz_rt (size_t size);
void js_free_rt (void *ptr); void js_free_rt (void *ptr);
struct cJSON; /* ============================================================
13. Compilation and Bytecode
============================================================ */
/* Compiled bytecode (context-free, serializable) */ struct cJSON;
typedef struct MachCode MachCode; typedef struct MachCode MachCode;
/* Free a compiled MachCode tree. */ /* Free a compiled MachCode tree. */

View File

@@ -36,36 +36,28 @@ static inline JS_BOOL JS_IsInteger (JSValue v) {
return d == (double)(int64_t)d; return d == (double)(int64_t)d;
} }
static inline JS_BOOL JS_IsGCObject (JSValue v) {
return JS_IsPtr (v);
}
JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
/* === Function definitions from header region (non-inline) === */ /* === Public API wrappers (non-inline, for quickjs.h declarations) ===
These delegate to the static inline versions in quickjs-internal.h. */
JS_BOOL JS_IsStone(JSValue v) { JS_BOOL JS_IsStone(JSValue v) { return mist_is_stone(v); }
return !JS_IsGCObject(v) || objhdr_s(*chase(v)); JS_BOOL JS_IsArray(JSValue v) { return mist_is_array(v); }
JS_BOOL JS_IsRecord(JSValue v) { return mist_is_record(v); }
JS_BOOL JS_IsFunction(JSValue v) { return mist_is_function(v); }
JS_BOOL JS_IsBlob(JSValue v) { return mist_is_blob(v); }
JS_BOOL JS_IsText(JSValue v) { return mist_is_text(v); }
JS_BOOL JS_IsCode(JSValue v) {
return mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_CODE;
} }
JS_BOOL JS_IsArray(JSValue v) { JS_BOOL JS_IsForwarded(JSValue v) {
return JS_IsGCObject(v) && objhdr_type(*chase(v)) == OBJ_ARRAY; return mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_FORWARD;
} }
JS_BOOL JS_IsRecord (JSValue v) { JS_BOOL JS_IsFrame(JSValue v) {
return JS_IsGCObject(v) && objhdr_type(*chase(v)) == OBJ_RECORD; return mist_is_gc_object(v) && objhdr_type(*chase(v)) == OBJ_FRAME;
}
JS_BOOL JS_IsFunction (JSValue v) {
return JS_IsGCObject(v) && objhdr_type(*chase(v)) == OBJ_FUNCTION;
}
JS_BOOL JS_IsBlob (JSValue v) {
return JS_IsGCObject(v) && objhdr_type(*chase(v)) == OBJ_BLOB;
}
JS_BOOL JS_IsText(JSValue v) {
return MIST_IsImmediateASCII(v) || (JS_IsGCObject(v) && objhdr_type(*chase(v)) == OBJ_TEXT);
} }
uint64_t get_text_hash (JSText *text) { uint64_t get_text_hash (JSText *text) {
@@ -4521,7 +4513,7 @@ static JSRegExp *js_get_regexp (JSContext *ctx, JSValue obj, BOOL throw_error) {
static int js_is_regexp (JSContext *ctx, JSValue obj) { static int js_is_regexp (JSContext *ctx, JSValue obj) {
JSValue m; JSValue m;
if (!JS_IsGCObject (obj)) return FALSE; if (!mist_is_gc_object (obj)) return FALSE;
m = JS_GetPropertyStr (ctx, obj, "Symbol.match"); m = JS_GetPropertyStr (ctx, obj, "Symbol.match");
if (JS_IsException (m)) return -1; if (JS_IsException (m)) return -1;
if (!JS_IsNull (m)) return JS_ToBool (ctx, m); if (!JS_IsNull (m)) return JS_ToBool (ctx, m);
@@ -4664,7 +4656,7 @@ fail:
JSValue js_regexp_toString (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { JSValue js_regexp_toString (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
JSValue pattern, flags; JSValue pattern, flags;
if (!JS_IsGCObject (this_val)) return JS_ThrowTypeErrorNotAnObject (ctx); if (!mist_is_gc_object (this_val)) return JS_ThrowTypeErrorNotAnObject (ctx);
JSText *b = pretext_init (ctx, 0); JSText *b = pretext_init (ctx, 0);
if (!b) return JS_EXCEPTION; if (!b) return JS_EXCEPTION;
@@ -5062,7 +5054,7 @@ static JSValue js_json_check (JSContext *ctx, JSONStringifyContext *jsc, JSValue
/* check for object.toJSON method */ /* check for object.toJSON method */
/* ECMA specifies this is done only for Object and BigInt */ /* ECMA specifies this is done only for Object and BigInt */
if (JS_IsGCObject (val)) { if (mist_is_gc_object (val)) {
JSValue f = JS_GetProperty (ctx, val, JS_KEY_toJSON); JSValue f = JS_GetProperty (ctx, val, JS_KEY_toJSON);
if (JS_IsException (f)) goto exception; if (JS_IsException (f)) goto exception;
if (JS_IsFunction (f)) { if (JS_IsFunction (f)) {
@@ -5134,7 +5126,7 @@ static int js_json_to_str (JSContext *ctx, JSONStringifyContext *jsc, JSValue ho
goto concat_value; goto concat_value;
} }
if (JS_IsGCObject ( if (mist_is_gc_object (
val_ref.val)) { /* includes arrays (OBJ_ARRAY) since they have JS_TAG_PTR */ val_ref.val)) { /* includes arrays (OBJ_ARRAY) since they have JS_TAG_PTR */
v = js_array_includes (ctx, jsc->stack, 1, &val_ref.val); v = js_array_includes (ctx, jsc->stack, 1, &val_ref.val);
if (JS_IsException (v)) goto exception; if (JS_IsException (v)) goto exception;
@@ -5322,7 +5314,7 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj, JSValue replacer, JSValue
JSValue present; JSValue present;
v = JS_GetPropertyNumber (ctx, replacer, i); v = JS_GetPropertyNumber (ctx, replacer, i);
if (JS_IsException (v)) goto exception; if (JS_IsException (v)) goto exception;
if (JS_IsGCObject (v)) { if (mist_is_gc_object (v)) {
/* Objects are not valid property list items */ /* Objects are not valid property list items */
continue; continue;
} else if (JS_IsNumber (v)) { } else if (JS_IsNumber (v)) {
@@ -6561,7 +6553,7 @@ static JSValue make_replacement (JSContext *ctx, int argc, JSValue *argv, int fo
} }
static int JS_IsRegExp (JSContext *ctx, JSValue v) { static int JS_IsRegExp (JSContext *ctx, JSValue v) {
if (!JS_IsGCObject (v)) return 0; if (!mist_is_gc_object (v)) return 0;
JSValue exec = JS_GetPropertyStr (ctx, v, "exec"); JSValue exec = JS_GetPropertyStr (ctx, v, "exec");
if (JS_IsException (exec)) return -1; if (JS_IsException (exec)) return -1;
@@ -6595,7 +6587,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
{ {
if (JS_IsText (argv[1])) { if (JS_IsText (argv[1])) {
target_is_regex = 0; target_is_regex = 0;
} else if (JS_IsGCObject (argv[1]) && JS_IsRegExp (ctx, argv[1])) { } else if (mist_is_gc_object (argv[1]) && JS_IsRegExp (ctx, argv[1])) {
target_is_regex = 1; target_is_regex = 1;
} else { } else {
return JS_NULL; return JS_NULL;
@@ -6877,7 +6869,7 @@ static JSValue js_cell_text_search (JSContext *ctx, JSValue this_val, int argc,
int target_is_regex = 0; int target_is_regex = 0;
if (JS_IsText (argv[1])) { if (JS_IsText (argv[1])) {
target_is_regex = 0; target_is_regex = 0;
} else if (JS_IsGCObject (argv[1]) && JS_IsRegExp (ctx, argv[1])) { } else if (mist_is_gc_object (argv[1]) && JS_IsRegExp (ctx, argv[1])) {
target_is_regex = 1; target_is_regex = 1;
} else { } else {
return JS_NULL; return JS_NULL;
@@ -7015,7 +7007,7 @@ static JSValue js_cell_text_extract (JSContext *ctx, JSValue this_val, int argc,
if (from > to) return JS_NULL; if (from > to) return JS_NULL;
/* RegExp path: convert new exec result record -> classic array */ /* RegExp path: convert new exec result record -> classic array */
if (JS_IsGCObject (argv[1]) && JS_IsRegExp (ctx, argv[1])) { if (mist_is_gc_object (argv[1]) && JS_IsRegExp (ctx, argv[1])) {
/* Root rx, str, out across allocating calls */ /* Root rx, str, out across allocating calls */
JSGCRef rx_ref, str_ref, out_ref; JSGCRef rx_ref, str_ref, out_ref;
JS_PushGCRef (ctx, &rx_ref); JS_PushGCRef (ctx, &rx_ref);
@@ -7736,7 +7728,7 @@ static JSValue js_cell_array (JSContext *ctx, JSValue this_val, int argc, JSValu
return result; return result;
} }
if (JS_IsGCObject (argv[1]) && JS_IsRegExp (ctx, argv[1])) { if (mist_is_gc_object (argv[1]) && JS_IsRegExp (ctx, argv[1])) {
/* Split by regex (manual "global" iteration; ignore g flag semantics) */ /* Split by regex (manual "global" iteration; ignore g flag semantics) */
/* Root rx, result, arg across allocating calls */ /* Root rx, result, arg across allocating calls */
JSGCRef rx_ref, res_ref, arg_ref; JSGCRef rx_ref, res_ref, arg_ref;
@@ -8396,7 +8388,7 @@ static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSVal
JSValue arg = argv[0]; JSValue arg = argv[0];
/* object(object) - shallow mutable copy */ /* object(object) - shallow mutable copy */
if (JS_IsGCObject (arg) && !JS_IsArray (arg) && !JS_IsFunction (arg)) { if (mist_is_gc_object (arg) && !JS_IsArray (arg) && !JS_IsFunction (arg)) {
if (argc < 2 || JS_IsNull (argv[1])) { if (argc < 2 || JS_IsNull (argv[1])) {
/* Shallow copy - root arg, result, keys across allocating calls */ /* Shallow copy - root arg, result, keys across allocating calls */
JSGCRef arg_ref, res_ref, keys_ref; JSGCRef arg_ref, res_ref, keys_ref;
@@ -8438,7 +8430,7 @@ static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSVal
#undef OBJ_COPY_CLEANUP #undef OBJ_COPY_CLEANUP
/* object(object, another_object) - combine */ /* object(object, another_object) - combine */
if (JS_IsGCObject (argv[1]) && !JS_IsArray (argv[1])) { if (mist_is_gc_object (argv[1]) && !JS_IsArray (argv[1])) {
JSGCRef arg_ref, arg2_ref, res_ref, keys_ref; JSGCRef arg_ref, arg2_ref, res_ref, keys_ref;
JS_PushGCRef (ctx, &arg_ref); JS_PushGCRef (ctx, &arg_ref);
arg_ref.val = arg; arg_ref.val = arg;
@@ -8760,7 +8752,7 @@ static JSValue js_blob_constructor (JSContext *ctx, JSValue this_val, int argc,
} }
} }
/* blob(blob, from, to) - copy from another blob */ /* blob(blob, from, to) - copy from another blob */
else if (argc >= 1 && JS_IsGCObject (argv[0]) && !JS_IsText (argv[0])) { else if (argc >= 1 && mist_is_gc_object (argv[0]) && !JS_IsText (argv[0])) {
blob *src = js_get_blob (ctx, argv[0]); blob *src = js_get_blob (ctx, argv[0]);
if (!src) if (!src)
return JS_ThrowTypeError (ctx, return JS_ThrowTypeError (ctx,
@@ -9239,7 +9231,7 @@ static JSValue js_mach_load (JSContext *ctx, JSValue this_val, int argc, JSValue
if (!mc) if (!mc)
return JS_ThrowSyntaxError (ctx, "mach_load: failed to deserialize bytecode"); return JS_ThrowSyntaxError (ctx, "mach_load: failed to deserialize bytecode");
JSValue env = (argc >= 2 && JS_IsGCObject (argv[1])) ? argv[1] : JS_NULL; JSValue env = (argc >= 2 && mist_is_gc_object (argv[1])) ? argv[1] : JS_NULL;
JSGCRef env_ref; JSGCRef env_ref;
JS_PushGCRef (ctx, &env_ref); JS_PushGCRef (ctx, &env_ref);
@@ -9287,7 +9279,7 @@ static JSValue js_mach_eval_mcode (JSContext *ctx, JSValue this_val, int argc, J
return JS_ThrowInternalError (ctx, "mach_eval_mcode: compilation failed"); return JS_ThrowInternalError (ctx, "mach_eval_mcode: compilation failed");
} }
JSValue env = (argc >= 3 && JS_IsGCObject (argv[2])) ? argv[2] : JS_NULL; JSValue env = (argc >= 3 && mist_is_gc_object (argv[2])) ? argv[2] : JS_NULL;
JSGCRef env_ref; JSGCRef env_ref;
JS_PushGCRef (ctx, &env_ref); JS_PushGCRef (ctx, &env_ref);
@@ -9334,7 +9326,7 @@ static JSValue js_mach_dump_mcode (JSContext *ctx, JSValue this_val, int argc, J
return JS_ThrowInternalError (ctx, "mach_dump_mcode: compilation failed"); return JS_ThrowInternalError (ctx, "mach_dump_mcode: compilation failed");
} }
JSValue env = (argc >= 3 && JS_IsGCObject (argv[2])) ? argv[2] : JS_NULL; JSValue env = (argc >= 3 && mist_is_gc_object (argv[2])) ? argv[2] : JS_NULL;
JSGCRef env_ref; JSGCRef env_ref;
JS_PushGCRef (ctx, &env_ref); JS_PushGCRef (ctx, &env_ref);
@@ -9414,7 +9406,7 @@ static JSValue js_cell_stone (JSContext *ctx, JSValue this_val, int argc, JSValu
return obj; return obj;
} }
if (JS_IsGCObject (obj)) { if (mist_is_gc_object (obj)) {
JSRecord *rec = JS_VALUE_GET_RECORD (obj); JSRecord *rec = JS_VALUE_GET_RECORD (obj);
obj_set_stone (rec); obj_set_stone (rec);
return obj; return obj;
@@ -9922,15 +9914,15 @@ static JSValue js_cell_proto (JSContext *ctx, JSValue this_val, int argc, JSValu
return JS_ThrowTypeError (ctx, "arrays do not have prototypes"); return JS_ThrowTypeError (ctx, "arrays do not have prototypes");
} }
if (!JS_IsGCObject (obj)) return JS_NULL; if (!mist_is_gc_object (obj)) return JS_NULL;
JSValue proto = JS_GetPrototype (ctx, obj); JSValue proto = JS_GetPrototype (ctx, obj);
if (JS_IsException (proto)) return JS_NULL; if (JS_IsException (proto)) return JS_NULL;
/* If prototype is Object.prototype, return null */ /* If prototype is Object.prototype, return null */
if (JS_IsGCObject (proto)) { if (mist_is_gc_object (proto)) {
JSValue obj_proto = ctx->class_proto[JS_CLASS_OBJECT]; JSValue obj_proto = ctx->class_proto[JS_CLASS_OBJECT];
if (JS_IsGCObject (obj_proto) if (mist_is_gc_object (obj_proto)
&& JS_VALUE_GET_OBJ (proto) == JS_VALUE_GET_OBJ (obj_proto)) { && JS_VALUE_GET_OBJ (proto) == JS_VALUE_GET_OBJ (obj_proto)) {
return JS_NULL; return JS_NULL;
} }
@@ -9956,7 +9948,7 @@ static JSValue js_cell_meme (JSContext *ctx, JSValue this_val, int argc, JSValue
/* Helper function to apply a single mixin */ /* Helper function to apply a single mixin */
#define APPLY_MIXIN(mix_val) \ #define APPLY_MIXIN(mix_val) \
do { \ do { \
if (!JS_IsGCObject (mix_val) || JS_IsNull (mix_val) || JS_IsArray (mix_val)) \ if (!mist_is_gc_object (mix_val) || JS_IsNull (mix_val) || JS_IsArray (mix_val)) \
break; \ break; \
JSGCRef _mix_ref; \ JSGCRef _mix_ref; \
JS_PushGCRef (ctx, &_mix_ref); \ JS_PushGCRef (ctx, &_mix_ref); \
@@ -10012,7 +10004,7 @@ static JSValue js_cell_meme (JSContext *ctx, JSValue this_val, int argc, JSValue
APPLY_MIXIN (mix); APPLY_MIXIN (mix);
} }
JS_PopGCRef (ctx, &mixins_ref); JS_PopGCRef (ctx, &mixins_ref);
} else if (JS_IsGCObject (mixins) && !JS_IsNull (mixins)) { } else if (mist_is_gc_object (mixins) && !JS_IsNull (mixins)) {
/* Single mixin object */ /* Single mixin object */
APPLY_MIXIN (mixins); APPLY_MIXIN (mixins);
} }
@@ -10034,7 +10026,7 @@ static JSValue js_cell_splat (JSContext *ctx, JSValue this_val, int argc, JSValu
if (argc < 1) return JS_NULL; if (argc < 1) return JS_NULL;
JSValue obj = argv[0]; JSValue obj = argv[0];
if (!JS_IsGCObject (obj) || JS_IsNull (obj)) return JS_NULL; if (!mist_is_gc_object (obj) || JS_IsNull (obj)) return JS_NULL;
/* Root obj, result, current, keys across allocating calls */ /* Root obj, result, current, keys across allocating calls */
JSGCRef obj_ref, res_ref, cur_ref, keys_ref; JSGCRef obj_ref, res_ref, cur_ref, keys_ref;
@@ -10078,7 +10070,7 @@ static JSValue js_cell_splat (JSContext *ctx, JSValue this_val, int argc, JSValu
return JS_EXCEPTION; return JS_EXCEPTION;
} }
int tag = JS_VALUE_GET_TAG (val); int tag = JS_VALUE_GET_TAG (val);
if (JS_IsGCObject (val) || JS_IsNumber (val) || tag == JS_TAG_STRING if (mist_is_gc_object (val) || JS_IsNumber (val) || tag == JS_TAG_STRING
|| tag == JS_TAG_STRING_IMM || tag == JS_TAG_BOOL) { || tag == JS_TAG_STRING_IMM || tag == JS_TAG_BOOL) {
JS_SetProperty (ctx, res_ref.val, key, val); JS_SetProperty (ctx, res_ref.val, key, val);
} }
@@ -10093,7 +10085,7 @@ static JSValue js_cell_splat (JSContext *ctx, JSValue this_val, int argc, JSValu
if (JS_IsFunction (to_data)) { if (JS_IsFunction (to_data)) {
JSValue args[1] = { res_ref.val }; JSValue args[1] = { res_ref.val };
JSValue extra = JS_Call (ctx, to_data, obj_ref.val, 1, args); JSValue extra = JS_Call (ctx, to_data, obj_ref.val, 1, args);
if (!JS_IsException (extra) && JS_IsGCObject (extra)) { if (!JS_IsException (extra) && mist_is_gc_object (extra)) {
keys_ref.val = JS_GetOwnPropertyNames (ctx, extra); keys_ref.val = JS_GetOwnPropertyNames (ctx, extra);
if (!JS_IsException (keys_ref.val)) { if (!JS_IsException (keys_ref.val)) {
uint32_t len; uint32_t len;
@@ -10153,7 +10145,7 @@ static JSValue js_cell_length (JSContext *ctx, JSValue this_val, int argc, JSVal
} }
/* Objects with length property */ /* Objects with length property */
if (JS_IsGCObject (val)) { if (mist_is_gc_object (val)) {
JSValue len = JS_GetPropertyStr (ctx, val, "length"); JSValue len = JS_GetPropertyStr (ctx, val, "length");
if (!JS_IsException (len) && !JS_IsNull (len)) { if (!JS_IsException (len) && !JS_IsNull (len)) {
if (JS_IsFunction (len)) { if (JS_IsFunction (len)) {
@@ -10247,7 +10239,7 @@ static JSValue js_cell_is_blob (JSContext *ctx, JSValue this_val, int argc, JSVa
static JSValue js_cell_is_data (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { static JSValue js_cell_is_data (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE; if (argc < 1) return JS_FALSE;
JSValue val = argv[0]; JSValue val = argv[0];
if (!JS_IsGCObject (val)) return JS_FALSE; if (!mist_is_gc_object (val)) return JS_FALSE;
if (JS_IsArray (val)) return JS_FALSE; if (JS_IsArray (val)) return JS_FALSE;
if (JS_IsFunction (val)) return JS_FALSE; if (JS_IsFunction (val)) return JS_FALSE;
if (js_get_blob (ctx, val)) return JS_FALSE; if (js_get_blob (ctx, val)) return JS_FALSE;
@@ -10296,7 +10288,7 @@ static JSValue js_cell_is_number (JSContext *ctx, JSValue this_val, int argc, JS
static JSValue js_cell_is_object (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { static JSValue js_cell_is_object (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 1) return JS_FALSE; if (argc < 1) return JS_FALSE;
JSValue val = argv[0]; JSValue val = argv[0];
if (!JS_IsGCObject (val)) return JS_FALSE; if (!mist_is_gc_object (val)) return JS_FALSE;
if (JS_IsArray (val)) return JS_FALSE; if (JS_IsArray (val)) return JS_FALSE;
return JS_TRUE; return JS_TRUE;
} }
@@ -10330,7 +10322,7 @@ static JSValue js_cell_is_proto (JSContext *ctx, JSValue this_val, int argc, JSV
JSValue val = argv[0]; JSValue val = argv[0];
JSValue master = argv[1]; JSValue master = argv[1];
if (!JS_IsGCObject (val) || JS_IsNull (master)) return JS_FALSE; if (!mist_is_gc_object (val) || JS_IsNull (master)) return JS_FALSE;
/* Walk prototype chain */ /* Walk prototype chain */
JSValue proto = JS_GetPrototype (ctx, val); JSValue proto = JS_GetPrototype (ctx, val);
@@ -10348,7 +10340,7 @@ static JSValue js_cell_is_proto (JSContext *ctx, JSValue this_val, int argc, JSV
} }
} }
/* Also check if proto == master directly */ /* Also check if proto == master directly */
if (JS_IsGCObject (master)) { if (mist_is_gc_object (master)) {
JSRecord *p1 = JS_VALUE_GET_OBJ (proto); JSRecord *p1 = JS_VALUE_GET_OBJ (proto);
JSRecord *p2 = JS_VALUE_GET_OBJ (master); JSRecord *p2 = JS_VALUE_GET_OBJ (master);
if (p1 == p2) { if (p1 == p2) {
@@ -10719,7 +10711,7 @@ static int nota_stack_has (NotaEncodeContext *enc, JSValueConst val) {
int len = nota_get_arr_len (ctx, enc->visitedStack_ref->val); int len = nota_get_arr_len (ctx, enc->visitedStack_ref->val);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
JSValue elem = JS_GetPropertyNumber (ctx, enc->visitedStack_ref->val, i); JSValue elem = JS_GetPropertyNumber (ctx, enc->visitedStack_ref->val, i);
if (JS_IsGCObject (elem) && JS_IsGCObject (val)) { if (mist_is_gc_object (elem) && mist_is_gc_object (val)) {
if (JS_StrictEq (ctx, elem, val)) { if (JS_StrictEq (ctx, elem, val)) {
JS_FreeValue (ctx, elem); JS_FreeValue (ctx, elem);
return 1; return 1;