#ifndef QUICKJS_INTERNAL_H #define QUICKJS_INTERNAL_H /* * QuickJS Javascript Engine * * Copyright (c) 2017-2025 Fabrice Bellard * Copyright (c) 2017-2025 Charlie Gordon * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #if defined(__APPLE__) #include #elif defined(__linux__) || defined(__GLIBC__) #include #elif defined(__FreeBSD__) #include #endif #include "cutils.h" #include "dtoa.h" #include "libregexp.h" #include "libunicode.h" #include "list.h" #include "quickjs.h" #include "cJSON.h" #include "blob.h" #include "nota.h" #include "wota.h" #if !defined(_WIN32) /* define it if printf uses the RNDN rounding mode instead of RNDNA */ #define CONFIG_PRINTF_RNDN #endif #if !defined(EMSCRIPTEN) /* enable stack limitation */ #define CONFIG_STACK_CHECK #endif /* dump object free */ // #define DUMP_FREE // #define DUMP_CLOSURE /* dump the bytecode of the compiled functions: combination of bits 1: dump pass 3 final byte code 2: dump pass 2 code 4: dump pass 1 code 8: dump stdlib functions 16: dump bytecode in hex 32: dump line number table 64: dump compute_stack_size */ // #define DUMP_BYTECODE (1) /* dump GC summary: old/new heap, recovery %, heap growth */ // #define DUMP_GC /* dump detailed GC: roots, scanning, object traversal (implies DUMP_GC) */ // #define DUMP_GC_DETAIL #ifdef DUMP_GC_DETAIL #define DUMP_GC #endif /* dump objects freed by the garbage collector */ // #define DUMP_GC_FREE /* dump memory usage before running the garbage collector */ // #define DUMP_MEM // #define DUMP_OBJECTS /* dump objects in JS_FreeContext */ // #define DUMP_READ_OBJECT // #define DUMP_ROPE_REBALANCE /* test the GC by forcing it before each object allocation */ // #define FORCE_GC_AT_MALLOC #define POISON_HEAP /* POISON_HEAP: Use ASan's memory poisoning to detect stale pointer access */ #ifdef POISON_HEAP #if defined(__has_feature) #if __has_feature(address_sanitizer) #define HAVE_ASAN 1 #endif #elif defined(__SANITIZE_ADDRESS__) #define HAVE_ASAN 1 #endif #ifdef HAVE_ASAN #include #define gc_poison_region(addr, size) __asan_poison_memory_region((addr), (size)) #define gc_unpoison_region(addr, size) __asan_unpoison_memory_region((addr), (size)) #else /* Fallback: no-op when not building with ASan */ #define gc_poison_region(addr, size) ((void)0) #define gc_unpoison_region(addr, size) ((void)0) #endif #endif /* POISON_HEAP */ #ifdef HAVE_ASAN static struct JSContext *__asan_js_ctx; #endif /* Forward declarations for heap object types */ typedef struct JSArray JSArray; typedef struct JSBlob JSBlob; typedef struct JSText JSText; typedef struct JSRecord JSRecord; typedef struct JSFunction JSFunction; typedef struct JSFrame JSFrame; typedef struct JSCode JSCode; #define OBJHDR_CAP_SHIFT 8u #define OBJHDR_CAP_MASK (((objhdr_t)1ull << 56) - 1ull) #define JS_MKPTR(ptr) (((JSValue)(uintptr_t)(ptr)) | JS_TAG_PTR) #define JS_MKFWD(ptr) (((objhdr_t)(uintptr_t)(ptr) << OBJHDR_FWD_PTR_SHIFT) | OBJ_FORWARD) #define JS_VALUE_GET_FWD_PTR(v) ((void*)(uintptr_t)(((v) >> OBJHDR_FWD_PTR_SHIFT) & OBJHDR_FWD_PTR_MASK)) /* For OBJ_FORWARD type: bits 3-63 contain a 61-bit pointer */ #define OBJHDR_FWD_PTR_SHIFT 3u #define OBJHDR_FWD_PTR_MASK (((objhdr_t)1ull << 61) - 1ull) #define objhdr_fwd_ptr(h) ((void*)(uintptr_t)(((h) >> OBJHDR_FWD_PTR_SHIFT) & OBJHDR_FWD_PTR_MASK)) #define objhdr_make_fwd(ptr) (((objhdr_t)(uintptr_t)(ptr) << OBJHDR_FWD_PTR_SHIFT) | OBJ_FORWARD) /* Extract pointer (clear low bits) */ #define JS_VALUE_GET_PTR(v) ((void *)((v) & ~((JSValue)(JSW - 1)))) static inline JS_BOOL JS_VALUE_IS_TEXT (JSValue v) { int tag = JS_VALUE_GET_TAG (v); return tag == JS_TAG_STRING_IMM || (JS_IsPtr(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_TEXT); } static inline JS_BOOL JS_VALUE_IS_NUMBER (JSValue v) { int tag = JS_VALUE_GET_TAG (v); return tag == JS_TAG_INT || tag == JS_TAG_SHORT_FLOAT; } /* JS_KEY_* macros: JSValue immediate ASCII strings for common property names. These replace JS_ATOM_* for use with the new JSValue-based property API. */ #define _JS_KEY1(c1) \ ((JSValue)JS_TAG_STRING_IMM | ((JSValue)1 << 5) | ((JSValue)(c1) << 8)) #define _JS_KEY2(c1, c2) \ ((JSValue)JS_TAG_STRING_IMM | ((JSValue)2 << 5) | ((JSValue)(c1) << 8) \ | ((JSValue)(c2) << 16)) #define _JS_KEY3(c1, c2, c3) \ ((JSValue)JS_TAG_STRING_IMM | ((JSValue)3 << 5) | ((JSValue)(c1) << 8) \ | ((JSValue)(c2) << 16) | ((JSValue)(c3) << 24)) #define _JS_KEY4(c1, c2, c3, c4) \ ((JSValue)JS_TAG_STRING_IMM | ((JSValue)4 << 5) | ((JSValue)(c1) << 8) \ | ((JSValue)(c2) << 16) | ((JSValue)(c3) << 24) | ((JSValue)(c4) << 32)) #define _JS_KEY5(c1, c2, c3, c4, c5) \ ((JSValue)JS_TAG_STRING_IMM | ((JSValue)5 << 5) | ((JSValue)(c1) << 8) \ | ((JSValue)(c2) << 16) | ((JSValue)(c3) << 24) | ((JSValue)(c4) << 32) \ | ((JSValue)(c5) << 40)) #define _JS_KEY6(c1, c2, c3, c4, c5, c6) \ ((JSValue)JS_TAG_STRING_IMM | ((JSValue)6 << 5) | ((JSValue)(c1) << 8) \ | ((JSValue)(c2) << 16) | ((JSValue)(c3) << 24) | ((JSValue)(c4) << 32) \ | ((JSValue)(c5) << 40) | ((JSValue)(c6) << 48)) #define _JS_KEY7(c1, c2, c3, c4, c5, c6, c7) \ ((JSValue)JS_TAG_STRING_IMM | ((JSValue)7 << 5) | ((JSValue)(c1) << 8) \ | ((JSValue)(c2) << 16) | ((JSValue)(c3) << 24) | ((JSValue)(c4) << 32) \ | ((JSValue)(c5) << 40) | ((JSValue)(c6) << 48) | ((JSValue)(c7) << 56)) /* Common property keys as JSValue immediate strings (max 7 ASCII chars) */ /* Empty string: tag with length 0 */ #define JS_KEY_empty ((JSValue)JS_TAG_STRING_IMM) #define JS_KEY_name _JS_KEY4 ('n', 'a', 'm', 'e') #define JS_KEY_length _JS_KEY6 ('l', 'e', 'n', 'g', 't', 'h') #define JS_KEY_message _JS_KEY7 ('m', 'e', 's', 's', 'a', 'g', 'e') #define JS_KEY_stack _JS_KEY5 ('s', 't', 'a', 'c', 'k') #define JS_KEY_cause _JS_KEY5 ('c', 'a', 'u', 's', 'e') #define JS_KEY_errors _JS_KEY6 ('e', 'r', 'r', 'o', 'r', 's') #define JS_KEY_Error _JS_KEY5 ('E', 'r', 'r', 'o', 'r') #define JS_KEY_raw _JS_KEY3 ('r', 'a', 'w') #define JS_KEY_flags _JS_KEY5 ('f', 'l', 'a', 'g', 's') #define JS_KEY_source _JS_KEY6 ('s', 'o', 'u', 'r', 'c', 'e') #define JS_KEY_exec _JS_KEY4 ('e', 'x', 'e', 'c') #define JS_KEY_toJSON _JS_KEY6 ('t', 'o', 'J', 'S', 'O', 'N') #define JS_KEY_eval _JS_KEY4 ('e', 'v', 'a', 'l') #define JS_KEY_this _JS_KEY4 ('t', 'h', 'i', 's') #define JS_KEY_true _JS_KEY4 ('t', 'r', 'u', 'e') #define JS_KEY_false _JS_KEY5 ('f', 'a', 'l', 's', 'e') #define JS_KEY_null _JS_KEY4 ('n', 'u', 'l', 'l') #define JS_KEY_NaN _JS_KEY3 ('N', 'a', 'N') #define JS_KEY_default _JS_KEY7 ('d', 'e', 'f', 'a', 'u', 'l', 't') #define JS_KEY_value _JS_KEY5 ('v', 'a', 'l', 'u', 'e') #define JS_KEY_index _JS_KEY5 ('i', 'n', 'd', 'e', 'x') #define JS_KEY_input _JS_KEY5 ('i', 'n', 'p', 'u', 't') #define JS_KEY_groups _JS_KEY6 ('g', 'r', 'o', 'u', 'p', 's') #define JS_KEY_indices _JS_KEY7 ('i', 'n', 'd', 'i', 'c', 'e', 's') #define JS_KEY_let _JS_KEY3 ('l', 'e', 't') #define JS_KEY_var _JS_KEY3 ('v', 'a', 'r') #define JS_KEY_new _JS_KEY3 ('n', 'e', 'w') #define JS_KEY_of _JS_KEY2 ('o', 'f') #define JS_KEY_yield _JS_KEY5 ('y', 'i', 'e', 'l', 'd') #define JS_KEY_async _JS_KEY5 ('a', 's', 'y', 'n', 'c') #define JS_KEY_target _JS_KEY6 ('t', 'a', 'r', 'g', 'e', 't') #define JS_KEY_from _JS_KEY4 ('f', 'r', 'o', 'm') #define JS_KEY_meta _JS_KEY4 ('m', 'e', 't', 'a') #define JS_KEY_as _JS_KEY2 ('a', 's') #define JS_KEY_get _JS_KEY3 ('g', 'e', 't') #define JS_KEY_set _JS_KEY3 ('s', 'e', 't') #define JS_KEY_with _JS_KEY4 ('w', 'i', 't', 'h') /* Internal variable names */ #define JS_KEY__ret_ _JS_KEY5 ('<', 'r', 'e', 't', '>') #define JS_KEY__eval_ _JS_KEY6 ('<', 'e', 'v', 'a', 'l', '>') #define JS_KEY__var_ _JS_KEY5 ('<', 'v', 'a', 'r', '>') /* Keys for strings > 7 chars - these use string literals and are created at runtime. The caller must free the returned JSValue if it's a heap string. */ #define JS_KEY_STR(ctx, str) JS_NewStringLen ((ctx), (str), sizeof (str) - 1) #define KEY_GET_STR_BUF_SIZE 256 /* JS_IsPretext, JS_KeyGetStr, JS_PushGCRef, JS_PopGCRef, JS_AddGCRef, JS_DeleteGCRef are defined after JSContext (they need its fields) */ /* Forward declarations for memory functions (now declared in quickjs.h) */ void *js_realloc (JSContext *ctx, void *ptr, size_t size); /* Forward declaration for string_get */ static inline int string_get (const JSText *p, int idx); #undef JS_PUSH_VALUE #undef JS_POP_VALUE #define JS_PUSH_VALUE(ctx, v) \ do { \ v##_ref.prev = ctx->top_gc_ref; \ ctx->top_gc_ref = &v##_ref; \ v##_ref.val = v; \ } while (0) #define JS_POP_VALUE(ctx, v) \ do { \ v = v##_ref.val; \ ctx->top_gc_ref = v##_ref.prev; \ } while (0) /* JS_Invoke - invoke method on object using JSValue key */ static JSValue JS_Invoke (JSContext *ctx, JSValue this_val, JSValue method, int argc, JSValue *argv); enum { /* classid tag */ /* union usage | properties */ JS_CLASS_OBJECT = 1, /* must be first */ JS_CLASS_REGEXP = 3, /* u.regexp */ JS_CLASS_BLOB, /* u.opaque (blob *) */ JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ }; typedef enum JSErrorEnum { JS_EVAL_ERROR, JS_RANGE_ERROR, JS_REFERENCE_ERROR, JS_SYNTAX_ERROR, JS_TYPE_ERROR, JS_URI_ERROR, JS_INTERNAL_ERROR, JS_AGGREGATE_ERROR, JS_NATIVE_ERROR_COUNT, /* number of different error name strings */ } JSErrorEnum; /* the variable and scope indexes must fit on 16 bits. The (-1) and ARG_SCOPE_END values are reserved. */ #define JS_MAX_LOCAL_VARS 65534 #define JS_STACK_SIZE_MAX 65534 /* Max string length matches header capacity (56 bits on 64-bit builds) */ #define JS_STRING_LEN_MAX OBJHDR_CAP_MASK #define __exception __attribute__ ((warn_unused_result)) /* Forward declaration for bytecode freeing */ #define JS_VALUE_GET_ARRAY(v) ((JSArray *)chase (v)) #define JS_VALUE_GET_OBJ(v) ((JSRecord *)chase (v)) #define JS_VALUE_GET_TEXT(v) ((JSText *)chase (v)) #define JS_VALUE_GET_BLOB(v) ((JSBlob *)JS_VALUE_GET_PTR (v)) #define JS_VALUE_GET_FUNCTION(v) ((JSFunction *)chase (v)) #define JS_VALUE_GET_FRAME(v) ((JSFrame *)chase (v)) #define JS_VALUE_GET_CODE(v) (JS_VALUE_GET_PTR (v)) #define JS_VALUE_GET_STRING(v) ((JSText *)chase (v)) /* Compatibility: JS_TAG_STRING is an alias for text type checks */ #define JS_TAG_STRING JS_TAG_STRING_IMM /* JS_TAG_FUNCTION doesn't exist in new encoding - use JS_IsFunction check instead */ #define JS_TAG_FUNCTION 0xFE /* dummy value, never matches any tag */ /* JS_ThrowMemoryError is an alias for JS_ThrowOutOfMemory */ #define JS_ThrowMemoryError(ctx) JS_ThrowOutOfMemory(ctx) /* Helper to set cap in objhdr */ static inline objhdr_t objhdr_set_cap56 (objhdr_t h, uint64_t cap) { return (h & 0xFF) | ((cap & OBJHDR_CAP_MASK) << OBJHDR_CAP_SHIFT); } typedef enum OPCodeEnum OPCodeEnum; /* ============================================================ Buddy Allocator for Actor Memory Blocks ============================================================ */ /* Platform-specific minimum block size */ #if defined(__LP64__) || defined(_WIN64) #define BUDDY_MIN_ORDER 10 /* 1KB minimum on 64-bit */ #else #define BUDDY_MIN_ORDER 9 /* 512B minimum on 32-bit */ #endif #define BUDDY_MAX_ORDER 28 /* 256MB maximum */ #define BUDDY_LEVELS (BUDDY_MAX_ORDER - BUDDY_MIN_ORDER + 1) #define BUDDY_POOL_SIZE (1ULL << BUDDY_MAX_ORDER) typedef struct BuddyBlock { struct BuddyBlock *next; struct BuddyBlock *prev; uint8_t order; /* log2 of size */ uint8_t is_free; } BuddyBlock; typedef struct BuddyAllocator { uint8_t *base; /* 256MB base address */ size_t total_size; /* 256MB */ BuddyBlock *free_lists[BUDDY_LEVELS]; uint8_t initialized; } BuddyAllocator; /* Forward declarations for buddy allocator functions */ static void buddy_destroy (BuddyAllocator *b); /* controls a host of contexts, handing out memory and scheduling */ struct JSRuntime { size_t malloc_limit; BuddyAllocator buddy; }; struct JSClass { const char *class_name; JSClassFinalizer *finalizer; uint32_t class_id; /* 0 means free entry */ }; #define JS_MODE_BACKTRACE_BARRIER \ (1 << 3) /* stop backtrace before this frame */ typedef struct JSFrameRegister { objhdr_t hdr; // capacity in this is the total number of words of the object, including the 4 words of overhead and all slots JSValue function; // JSFunction, function object being invoked JSValue caller; // JSFrameRegister, the frame that called this one JSValue address; // address of the instruction in the code that should be executed upon return JSValue slots[]; // inline memory. order is [this][input args][closed over vars][non closed over vars][temporaries] } JSFrameRegister; /// extra note: when this frame returns, caller should be set to 0. If caller is found to be 0, then the GC can reduce this frame's slots down to [this][input_args][closed over vars]; if no closed over vars it can be totally removed; may happen naturally in GC since it would have no refs? /* ============================================================ Register-Based VM Data Structures ============================================================ */ /* 32-bit instruction encoding (Lua-style) Formats: iABC: [op:8][A:8][B:8][C:8] — register ops iABx: [op:8][A:8][Bx:16] — constant/global loads (unsigned) iAsBx: [op:8][A:8][sBx:16] — conditional jumps (signed offset) isJ: [op:8][sJ:24] — unconditional jump (signed offset) */ typedef uint32_t MachInstr32; typedef struct { uint16_t line; uint16_t col; } MachLineEntry; /* Encoding macros */ #define MACH_ABC(op, a, b, c) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(b)<<16) | ((uint32_t)(c)<<24)) #define MACH_ABx(op, a, bx) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(bx)<<16)) #define MACH_AsBx(op, a, sbx) ((uint32_t)(op) | ((uint32_t)(a)<<8) | ((uint32_t)(uint16_t)(sbx)<<16)) #define MACH_sJ(op, sj) ((uint32_t)(op) | (((uint32_t)(sj) & 0xFFFFFF) << 8)) /* Decoding macros */ #define MACH_GET_OP(i) ((i) & 0xFF) #define MACH_GET_A(i) (((i) >> 8) & 0xFF) #define MACH_GET_B(i) (((i) >> 16) & 0xFF) #define MACH_GET_C(i) (((i) >> 24) & 0xFF) #define MACH_GET_Bx(i) ((i) >> 16) #define MACH_GET_sBx(i) ((int16_t)((i) >> 16)) #define MACH_GET_sJ(i) ((int32_t)((i) & 0xFFFFFF00) >> 8) typedef enum MachOpcode { /* === Legacy opcodes (used by existing .mach files) === */ /* Constants & Loading */ MACH_LOADK, /* R(A) = K(Bx) — load from constant pool (ABx) */ MACH_LOADI, /* R(A) = (int16_t)sBx — load small integer (AsBx) */ MACH_LOADNULL, /* R(A) = null (A only) */ MACH_LOADTRUE, /* R(A) = true (A only) */ MACH_LOADFALSE, /* R(A) = false (A only) */ /* Movement */ MACH_MOVE, /* R(A) = R(B) */ /* Generic arithmetic (ABC) — used by legacy .mach */ MACH_ADD, /* R(A) = R(B) + R(C) */ MACH_SUB, /* R(A) = R(B) - R(C) */ MACH_MUL, /* R(A) = R(B) * R(C) */ MACH_DIV, /* R(A) = R(B) / R(C) */ MACH_MOD, /* R(A) = R(B) % R(C) */ MACH_POW, /* R(A) = R(B) ** R(C) */ MACH_NEG, /* R(A) = -R(B) */ MACH_INC, /* R(A) = R(B) + 1 */ MACH_DEC, /* R(A) = R(B) - 1 */ /* Generic comparison (ABC) — used by legacy .mach */ MACH_EQ, /* R(A) = (R(B) == R(C)) */ MACH_NEQ, /* R(A) = (R(B) != R(C)) */ MACH_LT, /* R(A) = (R(B) < R(C)) */ MACH_LE, /* R(A) = (R(B) <= R(C)) */ MACH_GT, /* R(A) = (R(B) > R(C)) */ MACH_GE, /* R(A) = (R(B) >= R(C)) */ /* Logical/Bitwise — used by legacy .mach */ MACH_LNOT, /* R(A) = !R(B) */ MACH_BNOT, /* R(A) = ~R(B) */ MACH_BAND, /* R(A) = R(B) & R(C) */ MACH_BOR, /* R(A) = R(B) | R(C) */ MACH_BXOR, /* R(A) = R(B) ^ R(C) */ MACH_SHL, /* R(A) = R(B) << R(C) */ MACH_SHR, /* R(A) = R(B) >> R(C) */ MACH_USHR, /* R(A) = R(B) >>> R(C) */ /* Property access — used by legacy .mach */ MACH_GETFIELD, /* R(A) = R(B)[K(C)] — named property */ MACH_SETFIELD, /* R(A)[K(B)] = R(C) — named property */ MACH_GETINDEX, /* R(A) = R(B)[R(C)] — computed property */ MACH_SETINDEX, /* R(A)[R(B)] = R(C) — computed property */ /* Unbound variable access (ABx) */ MACH_GETNAME, /* R(A) = resolve(K(Bx)) — compiler placeholder, patched by link */ MACH_GETINTRINSIC, /* R(A) = global[K(Bx)] — post-link, intrinsic/built-in */ MACH_GETENV, /* R(A) = env[K(Bx)] — post-link, module environment */ /* Closure access (ABC) */ MACH_GETUP, /* R(A) = outer_frame[B].slots[C] */ MACH_SETUP, /* outer_frame[B].slots[C] = R(A) */ /* Control flow */ MACH_JMP, /* pc += sJ — unconditional (isJ format) */ MACH_JMPTRUE, /* if R(A): pc += sBx — (iAsBx format) */ MACH_JMPFALSE, /* if !R(A): pc += sBx — (iAsBx format) */ MACH_JMPNULL, /* if R(A)==null: pc += sBx */ /* Function calls — Lua-style consecutive registers (legacy .mach) */ MACH_RETURN, /* Return R(A) */ MACH_RETNIL, /* Return null */ /* Object/array creation — legacy .mach */ MACH_NEWOBJECT, /* R(A) = {} */ MACH_NEWARRAY, /* R(A) = new array, B = element count in R(A+1)..R(A+B) */ MACH_CLOSURE, /* R(A) = closure(functions[Bx]) (ABx) */ MACH_THROW, /* disrupt — trigger disruption */ MACH_PUSH, /* push R(B) onto array R(A) */ MACH_POP, /* R(A) = pop last element from array R(B) */ MACH_DELETE, /* R(A) = delete R(B)[K(C)] — named property delete */ MACH_DELETEINDEX, /* R(A) = delete R(B)[R(C)] — computed property delete */ MACH_HASPROP, /* R(A) = R(C) in R(B) — has property check */ MACH_REGEXP, /* R(A) = regexp(K(B), K(C)) — regex literal */ MACH_EQ_TOL, /* R(A) = eq_tol(R(B), R(B+1), R(B+2)), C=3 */ MACH_NEQ_TOL, /* R(A) = ne_tol(R(B), R(B+1), R(B+2)), C=3 */ MACH_NOP, /* === New mcode-derived opcodes (1:1 mapping to mcode IR) === */ /* Typed integer arithmetic (ABC) */ MACH_ADD_INT, /* R(A) = R(B) + R(C) — int, overflow → float */ MACH_SUB_INT, /* R(A) = R(B) - R(C) — int */ MACH_MUL_INT, /* R(A) = R(B) * R(C) — int */ MACH_DIV_INT, /* R(A) = R(B) / R(C) — int */ MACH_MOD_INT, /* R(A) = R(B) % R(C) — int */ MACH_NEG_INT, /* R(A) = -R(B) — int (AB) */ /* Typed float arithmetic (ABC) */ MACH_ADD_FLOAT, /* R(A) = R(B) + R(C) — float */ MACH_SUB_FLOAT, /* R(A) = R(B) - R(C) — float */ MACH_MUL_FLOAT, /* R(A) = R(B) * R(C) — float */ MACH_DIV_FLOAT, /* R(A) = R(B) / R(C) — float */ MACH_MOD_FLOAT, /* R(A) = R(B) % R(C) — float */ MACH_NEG_FLOAT, /* R(A) = -R(B) — float (AB) */ /* Text */ MACH_CONCAT, /* R(A) = R(B) ++ R(C) — string concatenation */ /* Typed integer comparisons (ABC) */ MACH_EQ_INT, /* R(A) = (R(B) == R(C)) — int */ MACH_NE_INT, /* R(A) = (R(B) != R(C)) — int */ MACH_LT_INT, /* R(A) = (R(B) < R(C)) — int */ MACH_LE_INT, /* R(A) = (R(B) <= R(C)) — int */ MACH_GT_INT, /* R(A) = (R(B) > R(C)) — int */ MACH_GE_INT, /* R(A) = (R(B) >= R(C)) — int */ /* Typed float comparisons (ABC) */ MACH_EQ_FLOAT, /* R(A) = (R(B) == R(C)) — float */ MACH_NE_FLOAT, /* R(A) = (R(B) != R(C)) — float */ MACH_LT_FLOAT, /* R(A) = (R(B) < R(C)) — float */ MACH_LE_FLOAT, /* R(A) = (R(B) <= R(C)) — float */ MACH_GT_FLOAT, /* R(A) = (R(B) > R(C)) — float */ MACH_GE_FLOAT, /* R(A) = (R(B) >= R(C)) — float */ /* Typed text comparisons (ABC) */ MACH_EQ_TEXT, /* R(A) = (R(B) == R(C)) — text */ MACH_NE_TEXT, /* R(A) = (R(B) != R(C)) — text */ MACH_LT_TEXT, /* R(A) = (R(B) < R(C)) — text */ MACH_LE_TEXT, /* R(A) = (R(B) <= R(C)) — text */ MACH_GT_TEXT, /* R(A) = (R(B) > R(C)) — text */ MACH_GE_TEXT, /* R(A) = (R(B) >= R(C)) — text */ /* Typed bool comparisons (ABC) */ MACH_EQ_BOOL, /* R(A) = (R(B) == R(C)) — bool */ MACH_NE_BOOL, /* R(A) = (R(B) != R(C)) — bool */ /* Special comparisons */ MACH_IS_IDENTICAL, /* R(A) = (R(B) === R(C)) — identity check (ABC) */ /* Type checks (AB) */ MACH_IS_INT, /* R(A) = is_int(R(B)) */ MACH_IS_NUM, /* R(A) = is_num(R(B)) */ MACH_IS_TEXT, /* R(A) = is_text(R(B)) */ MACH_IS_BOOL, /* R(A) = is_bool(R(B)) */ MACH_IS_NULL, /* R(A) = is_null(R(B)) */ MACH_TYPEOF, /* R(A) = typeof(R(B)) */ /* Logical (mcode-style) */ MACH_NOT, /* R(A) = !R(B) — boolean not (AB) */ MACH_AND, /* R(A) = R(B) && R(C) (ABC) */ MACH_OR, /* R(A) = R(B) || R(C) (ABC) */ /* Bitwise (mcode names) */ MACH_BITNOT, /* R(A) = ~R(B) (AB) */ MACH_BITAND, /* R(A) = R(B) & R(C) (ABC) */ MACH_BITOR, /* R(A) = R(B) | R(C) (ABC) */ MACH_BITXOR, /* R(A) = R(B) ^ R(C) (ABC) */ /* Property access (mcode names) */ MACH_LOAD_FIELD, /* R(A) = R(B).K(C) — named property (ABC) */ MACH_STORE_FIELD, /* R(A).K(B) = R(C) — named property (ABC) */ MACH_LOAD_INDEX, /* R(A) = R(B)[R(C)] — integer index (ABC) */ MACH_STORE_INDEX, /* R(A)[R(B)] = R(C) — integer index (ABC) */ MACH_LOAD_DYNAMIC, /* R(A) = R(B)[R(C)] — dynamic key (ABC) */ MACH_STORE_DYNAMIC, /* R(A)[R(B)] = R(C) — dynamic key (ABC) */ /* Object/Array creation (mcode names) */ MACH_NEWRECORD, /* R(A) = {} — new empty record (A only) */ /* Decomposed function calls (mcode-style) */ MACH_FRAME, /* R(A) = frame(R(B), C) — alloc call frame (ABC) */ MACH_SETARG, /* frame R(A)[B] = R(C) — set arg in frame (ABC) */ MACH_INVOKE, /* R(B) = invoke(R(A)) — call frame, result in R(B) (AB) */ MACH_GOFRAME, /* R(A) = goframe(R(B), C) — async frame (ABC) */ MACH_GOINVOKE, /* goinvoke(R(A)) — async invoke, no result (A only) */ /* Control flow */ MACH_JMPNOTNULL, /* if R(A)!=null: pc += sBx (iAsBx) */ /* Error handling */ MACH_DISRUPT, /* trigger disruption (A only) */ /* Variable storage */ MACH_SET_VAR, /* env/global[K(Bx)] = R(A) — store to var (ABx) */ /* Misc */ MACH_IN, /* R(A) = (R(B) in R(C)) — has property (ABC) */ /* Extended type checks (AB) */ MACH_IS_ARRAY, /* R(A) = is_array(R(B)) */ MACH_IS_FUNC, /* R(A) = is_function(R(B)) */ MACH_IS_RECORD, /* R(A) = is_object(R(B)) */ MACH_IS_STONE, /* R(A) = is_stone(R(B)) */ MACH_LENGTH, /* R(A) = length(R(B)) — array/text/blob length */ MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */ MACH_OP_COUNT } MachOpcode; static const char *mach_opcode_names[MACH_OP_COUNT] = { /* Legacy */ [MACH_LOADK] = "loadk", [MACH_LOADI] = "loadi", [MACH_LOADNULL] = "loadnull", [MACH_LOADTRUE] = "loadtrue", [MACH_LOADFALSE] = "loadfalse", [MACH_MOVE] = "move", [MACH_ADD] = "add", [MACH_SUB] = "sub", [MACH_MUL] = "mul", [MACH_DIV] = "div", [MACH_MOD] = "mod", [MACH_POW] = "pow", [MACH_NEG] = "neg", [MACH_INC] = "inc", [MACH_DEC] = "dec", [MACH_EQ] = "eq", [MACH_NEQ] = "neq", [MACH_LT] = "lt", [MACH_LE] = "le", [MACH_GT] = "gt", [MACH_GE] = "ge", [MACH_LNOT] = "lnot", [MACH_BNOT] = "bnot", [MACH_BAND] = "band", [MACH_BOR] = "bor", [MACH_BXOR] = "bxor", [MACH_SHL] = "shl", [MACH_SHR] = "shr", [MACH_USHR] = "ushr", [MACH_GETFIELD] = "getfield", [MACH_SETFIELD] = "setfield", [MACH_GETINDEX] = "getindex", [MACH_SETINDEX] = "setindex", [MACH_GETNAME] = "getname", [MACH_GETINTRINSIC] = "getintrinsic", [MACH_GETENV] = "getenv", [MACH_GETUP] = "getup", [MACH_SETUP] = "setup", [MACH_JMP] = "jmp", [MACH_JMPTRUE] = "jmptrue", [MACH_JMPFALSE] = "jmpfalse", [MACH_JMPNULL] = "jmpnull", [MACH_RETURN] = "return", [MACH_RETNIL] = "retnil", [MACH_NEWOBJECT] = "newobject", [MACH_NEWARRAY] = "newarray", [MACH_CLOSURE] = "closure", [MACH_THROW] = "throw", [MACH_PUSH] = "push", [MACH_POP] = "pop", [MACH_DELETE] = "delete", [MACH_DELETEINDEX] = "deleteindex", [MACH_HASPROP] = "hasprop", [MACH_REGEXP] = "regexp", [MACH_EQ_TOL] = "eq_tol", [MACH_NEQ_TOL] = "neq_tol", [MACH_NOP] = "nop", /* Mcode-derived */ [MACH_ADD_INT] = "add_int", [MACH_SUB_INT] = "sub_int", [MACH_MUL_INT] = "mul_int", [MACH_DIV_INT] = "div_int", [MACH_MOD_INT] = "mod_int", [MACH_NEG_INT] = "neg_int", [MACH_ADD_FLOAT] = "add_float", [MACH_SUB_FLOAT] = "sub_float", [MACH_MUL_FLOAT] = "mul_float", [MACH_DIV_FLOAT] = "div_float", [MACH_MOD_FLOAT] = "mod_float", [MACH_NEG_FLOAT] = "neg_float", [MACH_CONCAT] = "concat", [MACH_EQ_INT] = "eq_int", [MACH_NE_INT] = "ne_int", [MACH_LT_INT] = "lt_int", [MACH_LE_INT] = "le_int", [MACH_GT_INT] = "gt_int", [MACH_GE_INT] = "ge_int", [MACH_EQ_FLOAT] = "eq_float", [MACH_NE_FLOAT] = "ne_float", [MACH_LT_FLOAT] = "lt_float", [MACH_LE_FLOAT] = "le_float", [MACH_GT_FLOAT] = "gt_float", [MACH_GE_FLOAT] = "ge_float", [MACH_EQ_TEXT] = "eq_text", [MACH_NE_TEXT] = "ne_text", [MACH_LT_TEXT] = "lt_text", [MACH_LE_TEXT] = "le_text", [MACH_GT_TEXT] = "gt_text", [MACH_GE_TEXT] = "ge_text", [MACH_EQ_BOOL] = "eq_bool", [MACH_NE_BOOL] = "ne_bool", [MACH_IS_IDENTICAL] = "is_identical", [MACH_IS_INT] = "is_int", [MACH_IS_NUM] = "is_num", [MACH_IS_TEXT] = "is_text", [MACH_IS_BOOL] = "is_bool", [MACH_IS_NULL] = "is_null", [MACH_TYPEOF] = "typeof", [MACH_NOT] = "not", [MACH_AND] = "and", [MACH_OR] = "or", [MACH_BITNOT] = "bitnot", [MACH_BITAND] = "bitand", [MACH_BITOR] = "bitor", [MACH_BITXOR] = "bitxor", [MACH_LOAD_FIELD] = "load_field", [MACH_STORE_FIELD] = "store_field", [MACH_LOAD_INDEX] = "load_index", [MACH_STORE_INDEX] = "store_index", [MACH_LOAD_DYNAMIC] = "load_dynamic", [MACH_STORE_DYNAMIC] = "store_dynamic", [MACH_NEWRECORD] = "newrecord", [MACH_FRAME] = "frame", [MACH_SETARG] = "setarg", [MACH_INVOKE] = "invoke", [MACH_GOFRAME] = "goframe", [MACH_GOINVOKE] = "goinvoke", [MACH_JMPNOTNULL] = "jmpnotnull", [MACH_DISRUPT] = "disrupt", [MACH_SET_VAR] = "set_var", [MACH_IN] = "in", /* Extended type checks */ [MACH_IS_ARRAY] = "is_array", [MACH_IS_FUNC] = "is_func", [MACH_IS_RECORD] = "is_record", [MACH_IS_STONE] = "is_stone", [MACH_LENGTH] = "length", [MACH_IS_PROXY] = "is_proxy", }; /* Compiled register-based code (off-heap, never GC'd). Created by JS_CompileMach from AST JSON. */ typedef struct JSCodeRegister { uint16_t arity; /* number of arguments */ uint16_t nr_close_slots; /* closed-over variable count */ uint16_t nr_slots; /* total frame size */ uint16_t entry_point; /* start instruction (usually 0) */ /* Constant pool */ uint32_t cpool_count; JSValue *cpool; /* allocated via js_malloc_rt */ /* Compiled 32-bit instructions */ uint32_t instr_count; MachInstr32 *instructions; /* Nested functions (for closure) */ uint32_t func_count; struct JSCodeRegister **functions; /* Debug info */ JSValue name; /* function name (stone text) */ MachLineEntry *line_table; /* [instr_count], parallel to instructions[] */ char *filename_cstr; /* plain C string for stack trace (js_malloc_rt) */ char *name_cstr; /* plain C string for stack trace (js_malloc_rt) */ uint16_t disruption_pc; /* start of disruption handler (0 = none) */ } JSCodeRegister; /* Frame for closures - used by link-time relocation model where closures reference outer frames via (depth, slot) addressing. Stores function as JSValue to survive GC movements. */ typedef struct JSFrame { objhdr_t header; /* OBJ_FRAME, cap56 = slot count */ JSValue function; /* JSValue for GC safety (use JS_VALUE_GET_FUNCTION) */ JSValue caller; /* JSValue for GC safety (unused currently) */ uint32_t return_pc; JSValue slots[]; /* [this][args][captured][locals][temps] */ } JSFrame; /* Execution state returned by vm_execute_frame */ typedef enum { VM_EXEC_NORMAL, /* Continue executing current frame */ VM_EXEC_RETURN, /* Frame returned, pop and resume caller */ VM_EXEC_CALL, /* Need to push new frame for call */ VM_EXEC_EXCEPTION, /* Exception thrown, unwind frames */ } VMExecState; /* Call info for frame push */ typedef struct { JSValue func_obj; JSValue this_obj; int argc; JSValue *argv; const uint8_t *ret_pc; int ret_sp_offset; int call_argc; int call_has_this; int is_tail_call; } VMCallInfo; static inline objhdr_t objhdr_set_s (objhdr_t h, bool s) { return s ? (h | OBJHDR_S_MASK) : (h & ~OBJHDR_S_MASK); } static inline uint64_t objhdr_cap56 (objhdr_t h) { return (uint64_t)((h >> OBJHDR_CAP_SHIFT) & OBJHDR_CAP_MASK); } static inline objhdr_t objhdr_make (uint64_t cap56, uint8_t type, bool r, bool a, bool p, bool s) { objhdr_t h = 0; h |= ((objhdr_t)(cap56 & OBJHDR_CAP_MASK)) << OBJHDR_CAP_SHIFT; h |= (objhdr_t)(type & 7u); if (s) h |= OBJHDR_S_MASK; if (p) h |= OBJHDR_P_MASK; if (a) h |= OBJHDR_A_MASK; if (r) h |= OBJHDR_R_MASK; return h; } static inline objhdr_t *chase(JSValue v) { objhdr_t *oh = JS_VALUE_GET_PTR(v); if (objhdr_type(*oh) != OBJ_FORWARD) return oh; do { objhdr_t *next = (objhdr_t*)objhdr_fwd_ptr(*oh); if (!next) return oh; /* NULL forward pointer (stale reference bug) */ oh = next; } while (objhdr_type(*oh) == OBJ_FORWARD); return oh; } /* Intrinsic array type - tagged as JS_TAG_PTR with mist_hdr type OBJ_ARRAY */ typedef struct JSArray { objhdr_t mist_hdr; /* mist header at offset 0 */ word_t len; /* current length */ JSValue values[]; /* inline flexible array member */ } JSArray; /* JSBlob - binary data per memory.md */ typedef struct JSBlob { objhdr_t mist_hdr; word_t length; uint8_t bits[]; } JSBlob; typedef struct JSText { objhdr_t hdr; /* mist header */ word_t length; /* length (or hash for stoned text) */ word_t packed[]; /* two chars per packed word */ } JSText; typedef struct slot { JSValue key; JSValue val; /* use 'val' to match existing code */ } slot; /* Compatibility alias - old code uses JSRecordEntry */ typedef slot JSRecordEntry; typedef struct JSRecord { objhdr_t mist_hdr; struct JSRecord *proto; word_t len; /* number of entries */ slot slots[]; /* slots[0] reserved: key low32=class_id, key high32=rec_id, val=opaque */ } JSRecord; /* Helper macros to access class_id, rec_id, opaque from slots[0] per memory.md */ #define REC_GET_CLASS_ID(rec) ((uint32_t)((rec)->slots[0].key & 0xFFFFFFFF)) #define REC_GET_REC_ID(rec) ((uint32_t)((rec)->slots[0].key >> 32)) #define REC_SET_CLASS_ID(rec, cid) do { \ (rec)->slots[0].key = ((rec)->slots[0].key & 0xFFFFFFFF00000000ULL) | (uint64_t)(cid); \ } while(0) #define REC_SET_REC_ID(rec, rid) do { \ (rec)->slots[0].key = ((rec)->slots[0].key & 0xFFFFFFFF) | ((uint64_t)(rid) << 32); \ } while(0) #define REC_GET_OPAQUE(rec) ((void*)(uintptr_t)(rec)->slots[0].val) #define REC_SET_OPAQUE(rec, ptr) do { (rec)->slots[0].val = (JSValue)(uintptr_t)(ptr); } while(0) /* ============================================================ Record key helpers (JSValue keys) ============================================================ */ /* GC-SAFE: No allocations. */ static inline JS_BOOL rec_key_is_empty (JSValue key) { return JS_IsNull (key); } /* GC-SAFE: No allocations. */ static inline JS_BOOL rec_key_is_tomb (JSValue key) { return JS_IsException (key); } typedef struct fash64_state { uint64_t result; uint64_t sum; } fash64_state; enum { FASH64_PRIME_11 = 11111111111111111027ull, FASH64_PRIME_8 = 8888888888888888881ull, FASH64_PRIME_3 = 3333333333333333271ull }; static inline void fash64_mul_hi_lo (uint64_t a, uint64_t b, uint64_t *hi, uint64_t *lo) { #if defined(__SIZEOF_INT128__) __uint128_t p = (__uint128_t)a * (__uint128_t)b; *lo = (uint64_t)p; *hi = (uint64_t)(p >> 64); #elif defined(_MSC_VER) && defined(_M_X64) *lo = _umul128 (a, b, hi); #else /* Portable fallback (no 128-bit type, no _umul128). */ uint64_t a0 = (uint32_t)a; uint64_t a1 = a >> 32; uint64_t b0 = (uint32_t)b; uint64_t b1 = b >> 32; uint64_t p00 = a0 * b0; uint64_t p01 = a0 * b1; uint64_t p10 = a1 * b0; uint64_t p11 = a1 * b1; uint64_t mid = (p00 >> 32) + (uint32_t)p01 + (uint32_t)p10; *lo = (p00 & 0xffffffffull) | (mid << 32); *hi = p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32); #endif } static inline void fash64_begin (fash64_state *s) { s->result = (uint64_t)FASH64_PRIME_8; s->sum = (uint64_t)FASH64_PRIME_3; } static inline void fash64_word (fash64_state *s, uint64_t word) { uint64_t high, low; uint64_t mixed = s->result ^ word; fash64_mul_hi_lo (mixed, (uint64_t)FASH64_PRIME_11, &high, &low); s->sum += high; s->result = low ^ s->sum; } static inline void fash64_block (fash64_state *s, const uint64_t *block, size_t word_count) { for (size_t i = 0; i < word_count; i++) fash64_word (s, block[i]); } static inline uint64_t fash64_end (const fash64_state *s) { return s->result; } /* Convenience one-shot helper */ static inline uint64_t fash64_hash_words (const uint64_t *words, size_t word_count, uint64_t extra_word) { fash64_state s; fash64_begin (&s); fash64_block (&s, words, word_count); fash64_word (&s, extra_word); return fash64_end (&s); } static inline uint64_t fash64_hash_one (uint64_t word) { uint64_t high, low; uint64_t mixed = (uint64_t)FASH64_PRIME_8 ^ word; fash64_mul_hi_lo (mixed, (uint64_t)FASH64_PRIME_11, &high, &low); uint64_t sum = (uint64_t)FASH64_PRIME_3 + high; return low ^ sum; } static inline word_t JSText_len (const JSText *text) { if (objhdr_s (text->hdr)) return objhdr_cap56 (text->hdr); return text->length; } static inline JS_BOOL JSText_equal (const JSText *a, const JSText *b) { word_t len_a = JSText_len (a); word_t len_b = JSText_len (b); if (len_a != len_b) return FALSE; size_t word_count = (len_a + 1) / 2; return memcmp (a->packed, b->packed, word_count * sizeof (word_t)) == 0; } static JS_BOOL JSText_equal_ascii (const JSText *text, JSValue imm) { int len = MIST_GetImmediateASCIILen (imm); if (len != JSText_len (text)) return FALSE; for (int i = 0; i < len; i++) { uint32_t c = string_get (text, i); if (c >= 0x80) return FALSE; if ((uint8_t)c != (uint8_t)MIST_GetImmediateASCIIChar (imm, i)) return FALSE; } return TRUE; } /* Get hash for a JSText value. For stoned text (s=1): hash is cached in length field (computed on first access). For pre-text (s=0): compute hash on the fly. */ /* Max length for key strings (identifiers, property names) */ #define JS_KEY_MAX_LEN 4096 /* Forward declarations for stone arena functions (defined after JSContext) */ /* must be large enough to have a negligible runtime cost and small enough to call the interrupt callback often. */ #define JS_INTERRUPT_COUNTER_INIT 10000 struct JSContext { JSRuntime *rt; /* Actor memory block (bump allocation) */ uint8_t *heap_base; /* start of current block */ uint8_t *heap_free; /* bump pointer */ uint8_t *heap_end; /* end of block */ size_t current_block_size; /* current block size (64KB initially) */ size_t next_block_size; /* doubles if <10% recovered after GC */ /* Stone arena - permanent immutable allocations */ uint8_t *stone_base; /* stone arena base */ uint8_t *stone_free; /* stone arena bump pointer */ uint8_t *stone_end; /* stone arena end */ /* Stone text intern table */ void *st_pages; /* stone page list for large allocations */ uint32_t *st_text_hash; /* hash table (slot -> id) */ JSText **st_text_array; /* array of JSText pointers indexed by id */ uint32_t st_text_size; /* hash table size (power of 2) */ uint32_t st_text_count; /* number of interned texts */ uint32_t st_text_resize; /* threshold for resize */ uint16_t binary_object_count; int binary_object_size; /* Record key allocator */ uint32_t rec_key_next; JSGCRef *top_gc_ref; /* used to reference temporary GC roots (stack top) */ JSGCRef *last_gc_ref; /* used to reference temporary GC roots (list) */ int class_count; /* size of class_array and class_proto */ JSClass *class_array; JSValue *class_proto; JSValue regexp_ctor; JSValue throw_type_error; JSValue global_obj; /* global object (immutable intrinsics) */ uint64_t random_state; /* when the counter reaches zero, JSRutime.interrupt_handler is called */ int interrupt_counter; /* if NULL, RegExp compilation is not supported */ JSValue (*compile_regexp) (JSContext *ctx, JSValue pattern, JSValue flags); void *user_opaque; js_hook trace_hook; int trace_type; void *trace_data; /* Register VM frame root (updated by GC when frame moves) */ JSValue reg_current_frame; /* current JSFrameRegister being executed */ uint32_t current_register_pc; /* PC at exception time */ JSInterruptHandler *interrupt_handler; void *interrupt_opaque; JSValue current_exception; /* Stack overflow protection */ // todo: want this, but should be a simple increment/decrement counter while frames are pushed size_t stack_depth; size_t stack_limit; /* Parser state (for GC to scan cpool during parsing) */ struct JSFunctionDef *current_parse_fd; }; /* ============================================================ Functions that need JSContext definition ============================================================ */ /* Convert JSValue key (string) to C string in buffer. Returns buf, filled with the string content or "[?]" if not a string. */ static inline const char *JS_KeyGetStr (JSContext *ctx, char *buf, size_t buf_size, JSValue key) { if (JS_IsText (key)) { const char *cstr = JS_ToCString (ctx, key); if (cstr) { snprintf (buf, buf_size, "%s", cstr); JS_FreeCString (ctx, cstr); return buf; } } snprintf (buf, buf_size, "[?]"); return buf; } /* ============================================================ Stone Arena Functions ============================================================ */ /* Stone page for large allocations */ typedef struct StonePage { struct StonePage *next; size_t size; uint8_t data[]; } StonePage; /* Initial stone text table size */ #define ST_TEXT_INITIAL_SIZE 256 /* Allocate from stone arena (permanent, immutable memory) */ /* Resize the stone text intern hash table */ /* Realloc with slack reporting (for bump allocator) WARNING: This function is NOT GC-safe! The caller must protect the source object with a GC ref before calling, and re-chase the pointer after. */ /* ============================================================ GC Public API ============================================================ */ /* Forward declaration for ctx_gc */ static int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size); /* JS_MarkValue - mark a value during GC traversal. With copying GC, this is a no-op as we discover live objects by tracing. */ /* Helper to check if a pointer is in stone memory */ static inline int is_stone_ptr (JSContext *ctx, void *ptr) { return (uint8_t *)ptr >= ctx->stone_base && (uint8_t *)ptr < ctx->stone_end; } /* Intern a UTF-32 string as a stone text, returning a JSValue string */ /* Create a stoned, interned key from a UTF-8 C string. Returns immediate ASCII text if ≤7 ASCII chars, otherwise stoned interned text. The returned JSValue does NOT need to be freed (it's either immediate or part of the stone arena). */ /* Create a key from a UTF-8 string with explicit length */ typedef union JSFloat64Union { double d; uint64_t u64; uint32_t u32[2]; } JSFloat64Union; /* JS_IsText is defined in quickjs.h */ /* Helper to get array capacity from mist_hdr */ static inline uint32_t js_array_cap (JSArray *arr) { return objhdr_cap56 (arr->mist_hdr); } /* JSRegExp: regular expression object data (must come before JSRecord/JSRecord) */ typedef struct JSRegExp { char *pattern; /* UTF-8, null-terminated, js_malloc_rt'd */ uint32_t pattern_len; uint8_t *bytecode; /* raw lre bytecode, js_malloc_rt'd */ uint32_t bytecode_len; } JSRegExp; #define obj_is_stone(rec) objhdr_s ((rec)->mist_hdr) #define obj_set_stone(rec) ((rec)->mist_hdr = objhdr_set_s ((rec)->mist_hdr, true)) #define JS_VALUE_GET_RECORD(v) ((JSRecord *)chase (v)) /* Get prototype from object (works for both JSRecord and JSRecord since they * share layout) */ #define JS_OBJ_GET_PROTO(p) ((JSRecord *)((JSRecord *)(p))->proto) /* Initial capacity for new records (mask = 7, 8 slots total) */ #define JS_RECORD_INITIAL_MASK 7 /* ============================================================ JSRecord Core Operations ============================================================ */ // can check if key by checking for 0 here /* Compare a JSValue key with a C string literal. Used for comparing with internal names that are too long for immediate ASCII. */ /* GC-SAFE: No allocations. Caller must pass freshly-chased rec. Find slot for a key in record's own table. Returns slot index (>0) if found, or -(insert_slot) if not found. */ /* GC-SAFE: No allocations. Walks prototype chain via direct pointers. Caller must pass freshly-chased rec. */ /* GC-SAFE: Resize record by allocating a new larger record and copying all data. Uses GC ref to protect source during allocation. Updates *pobj to point to the new record. Returns 0 on success, -1 on failure. */ /* GC-SAFE: May call rec_resize which allocates. Takes JSValue* so the object can be tracked through GC. Updates *pobj if the record is resized. */ /* Helper macros to access class_id and rec_id from slots[0].key per memory.md: - Low 32 bits of slots[0].key = class_id - High 32 bits of slots[0].key = rec_id - slots[0].val = opaque C pointer */ #define REC_GET_CLASS_ID(rec) ((uint32_t)((rec)->slots[0].key & 0xFFFFFFFF)) #define REC_GET_REC_ID(rec) ((uint32_t)((rec)->slots[0].key >> 32)) #define REC_SET_CLASS_ID(rec, cid) do { \ (rec)->slots[0].key = ((rec)->slots[0].key & 0xFFFFFFFF00000000ULL) | (uint64_t)(cid); \ } while(0) #define REC_SET_REC_ID(rec, rid) do { \ (rec)->slots[0].key = ((rec)->slots[0].key & 0xFFFFFFFF) | ((uint64_t)(rid) << 32); \ } while(0) #define REC_GET_OPAQUE(rec) ((void*)(uintptr_t)(rec)->slots[0].val) #define REC_SET_OPAQUE(rec, ptr) do { (rec)->slots[0].val = (JSValue)(uintptr_t)(ptr); } while(0) /* Allocate a new record with specified class_id. Uses bump allocation. Slots are inline (flexible array member). Per memory.md: slots[0] is reserved for class_id, rec_id, and opaque. */ typedef enum { JS_FUNC_KIND_C, JS_FUNC_KIND_BYTECODE, JS_FUNC_KIND_C_DATA, JS_FUNC_KIND_REGISTER, /* register-based VM function */ } JSFunctionKind; typedef struct JSFunction { objhdr_t header; /* must come first */ JSValue name; /* function name as JSValue text */ int16_t length; /* arity: max allowed arguments (-1 = variadic) */ uint8_t kind; union { struct { JSCFunctionType c_function; uint8_t cproto; int16_t magic; } cfunc; struct { JSCodeRegister *code; /* compiled register code (off-heap) */ JSValue env_record; /* stone record, module environment */ JSValue outer_frame; /* JSFrame JSValue, for closures */ } reg; } u; } JSFunction; /* for the encoding of the pc2line table */ #define PC2LINE_BASE (-1) #define PC2LINE_RANGE 5 #define PC2LINE_OP_FIRST 1 #define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) /* New simplified compiled unit structure for Phase 1+ simplification. Replaces JSFunctionBytecode with a simpler model: - No closure machinery (uses outer_frame chain at runtime) - Free variables resolved at link time against env + globals - Nested functions stored as separate units in cpool */ typedef struct JSCompiledUnit { objhdr_t header; /* must come first */ /* Bytecode (self pointer) */ uint8_t *byte_code_buf; int byte_code_len; /* Constants - strings, numbers, nested unit refs (self pointer) */ JSValue *cpool; int cpool_count; /* Stack requirements */ uint16_t local_count; /* total local slots (args + vars) */ uint16_t stack_size; /* operand stack depth */ /* Flags */ uint8_t has_debug : 1; uint8_t read_only_bytecode : 1; /* Debug info (optional - only present if has_debug) */ struct { JSValue filename; int source_len; int pc2line_len; uint8_t *pc2line_buf; char *source; } debug; } JSCompiledUnit; /* ============================================================ Context-Neutral Module Format (Phase 2+) Struct definitions are in quickjs.h ============================================================ */ #define JS_PROP_INITIAL_SIZE 2 #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ #define JS_ARRAY_INITIAL_SIZE 2 /* Max capacity depends on platform - cap field size varies */ #ifdef JS_PTR64 #define JS_ARRAY_MAX_CAP ((word_t)((1ULL << 56) - 1)) #else #define JS_ARRAY_MAX_CAP ((word_t)((1UL << 24) - 1)) #endif JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv); JSValue js_call_bound_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv); JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv, int flags); JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue this_obj, int argc, JSValue *argv, JSValue env, JSValue outer_frame); int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop); JSValue __attribute__ ((format (printf, 2, 3))) JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...); __maybe_unused void JS_DumpString (JSRuntime *rt, const JSText *text); __maybe_unused void JS_DumpObjectHeader (JSRuntime *rt); __maybe_unused void JS_DumpObject (JSRuntime *rt, JSRecord *rec); __maybe_unused void JS_DumpGCObject (JSRuntime *rt, objhdr_t *p); __maybe_unused void JS_DumpValue (JSContext *ctx, const char *str, JSValue val); void js_dump_value_write (void *opaque, const char *buf, size_t len); void js_regexp_finalizer (JSRuntime *rt, JSValue val); JSValue js_new_function (JSContext *ctx, JSFunctionKind kind); /* Forward declaration - helper to set cap in objhdr */ static inline objhdr_t objhdr_set_cap56 (objhdr_t h, uint64_t cap); /* JS_ThrowMemoryError is an alias for JS_ThrowOutOfMemory */ #define JS_ThrowMemoryError(ctx) JS_ThrowOutOfMemory(ctx) /* GC-SAFE: JS_SetPropertyInternal: same as JS_SetProperty but doesn't check stone. Internal use only. May trigger GC if record needs to resize. */ blob *js_get_blob (JSContext *ctx, JSValue val); JSValue js_new_string8_len (JSContext *ctx, const char *buf, int len); JSValue pretext_end (JSContext *ctx, JSText *s); JSValue js_compile_regexp (JSContext *ctx, JSValue pattern, JSValue flags); JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern, JSValue bc); int JS_NewClass1 (JSContext *ctx, JSClassID class_id, const JSClassDef *class_def, const char *name); BOOL js_strict_eq (JSContext *ctx, JSValue op1, JSValue op2); static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_push (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_pop (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_array_find (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_stone (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_length (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_reverse (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_proto (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_splat (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_meme (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_fn_apply (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_call (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_modulo (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_neg (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_not (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); JSValue js_cell_text_lower (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); JSValue js_cell_text_upper (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_text_trim (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); JSValue js_cell_text_codepoint (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_text_search (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_text_extract (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); JSValue js_cell_character (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_abs (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_sign (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_floor (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_ceiling (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_round (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_trunc (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_whole (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_fraction (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_min (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_max (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_number_remainder (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); cJSON *JS_GetStack(JSContext *ctx); JSValue JS_ThrowOutOfMemory (JSContext *ctx); JSValue JS_ToNumber (JSContext *ctx, JSValue val); int JS_SetPropertyValue (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val); int JS_GetOwnPropertyInternal (JSContext *ctx, JSValue *desc, JSRecord *p, JSValue prop); /* JS_AddIntrinsicBasicObjects is declared in quickjs.h */ __exception int js_get_length32 (JSContext *ctx, uint32_t *pres, JSValue obj); __exception int js_get_length64 (JSContext *ctx, int64_t *pres, JSValue obj); void free_arg_list (JSContext *ctx, JSValue *tab, uint32_t len); JSValue *build_arg_list (JSContext *ctx, uint32_t *plen, JSValue *parray_arg); JSValue js_regexp_toString (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); /* === Inline utility functions (used across modules) === */ static inline int is_digit (int c) { return c >= '0' && c <= '9'; } static inline int string_get (const JSText *p, int idx) { int word_idx = idx >> 1; int shift = (1 - (idx & 1)) * 32; return (uint32_t)((p->packed[word_idx] >> shift) & 0xFFFFFFFF); } static inline void string_put (JSText *p, int idx, uint32_t c) { int word_idx = idx >> 1; int shift = (1 - (idx & 1)) * 32; uint64_t mask = 0xFFFFFFFFULL << shift; p->packed[word_idx] = (p->packed[word_idx] & ~mask) | ((uint64_t)c << shift); } /* Get character from any string value (immediate ASCII or JSText) */ static inline uint32_t js_string_value_get (JSValue v, int idx) { if (MIST_IsImmediateASCII (v)) { return MIST_GetImmediateASCIIChar (v, idx); } else { JSText *s = JS_VALUE_GET_TEXT (v); return string_get (s, idx); } } /* Get length from any string value */ static inline int js_string_value_len (JSValue v) { if (MIST_IsImmediateASCII (v)) { return MIST_GetImmediateASCIILen (v); } else { return JSText_len (JS_VALUE_GET_TEXT (v)); } } /* System allocator wrappers (not GC-managed) */ static inline void sys_free (void *ptr) { free (ptr); } static inline void *sys_malloc (size_t size) { return malloc (size); } static inline void *sys_realloc (void *ptr, size_t size) { return realloc (ptr, size); } /* Parser system allocator wrappers (not GC-managed) */ static inline void *pjs_malloc (size_t size) { return malloc (size); } static inline void *pjs_mallocz (size_t size) { void *ptr = malloc (size); if (ptr) memset (ptr, 0, size); return ptr; } static inline void *pjs_realloc (void *ptr, size_t size) { return realloc (ptr, size); } static inline void pjs_free (void *ptr) { free (ptr); } static inline int pjs_realloc_array (void **parray, int elem_size, int *psize, int req_size) { int new_size; void *new_array; size_t old_size; new_size = max_int (req_size, *psize * 3 / 2); old_size = (size_t)(*psize) * elem_size; new_array = pjs_realloc (*parray, (size_t)new_size * elem_size); if (!new_array) return -1; if (new_size > *psize) { memset ((char *)new_array + old_size, 0, (size_t)(new_size - *psize) * elem_size); } *psize = new_size; *parray = new_array; return 0; } static inline int pjs_resize_array (void **parray, int elem_size, int *psize, int req_size) { if (unlikely (req_size > *psize)) return pjs_realloc_array (parray, elem_size, psize, req_size); else return 0; } /* set the new value and free the old value after */ static inline void set_value (JSContext *ctx, JSValue *pval, JSValue new_val) { (void)ctx; *pval = new_val; } void JS_ThrowInterrupted (JSContext *ctx); static no_inline __exception int __js_poll_interrupts (JSContext *ctx) { ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; if (ctx->interrupt_handler) { if (ctx->interrupt_handler (ctx->rt, ctx->interrupt_opaque)) { JS_ThrowInterrupted (ctx); return -1; } } return 0; } static inline __exception int js_poll_interrupts (JSContext *ctx) { if (unlikely (--ctx->interrupt_counter <= 0)) { return __js_poll_interrupts (ctx); } else { return 0; } } /* === PPretext (parser pretext, system-malloc, used by cell_js.c parser) === */ typedef struct PPretext { uint32_t *data; int len; int cap; } PPretext; #define JS_CALL_FLAG_COPY_ARGV (1 << 1) extern JSClassID js_class_id_alloc; /* === Forward declarations for functions split across modules === */ /* runtime.c — line/column, GC, and VM dispatch */ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size); JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv, int flags); /* runtime.c exports */ JSValue JS_ThrowStackOverflow (JSContext *ctx); int JS_DefineObjectName (JSContext *ctx, JSValue obj, JSValue name); int JS_DefineObjectNameComputed (JSContext *ctx, JSValue obj, JSValue str); int js_method_set_properties (JSContext *ctx, JSValue func_obj, JSValue name, int flags, JSValue home_obj); JSValue JS_GetPropertyValue (JSContext *ctx, JSValue this_obj, JSValue prop); __exception int JS_CopyDataProperties (JSContext *ctx, JSValue target, JSValue source, JSValue excluded, BOOL setprop); int js_string_compare_value (JSContext *ctx, JSValue op1, JSValue op2, BOOL eq_only); int js_string_compare_value_nocase (JSContext *ctx, JSValue op1, JSValue op2); JSValue js_regexp_constructor (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); int JS_HasProperty (JSContext *ctx, JSValue obj, JSValue prop); int JS_HasPropertyKey (JSContext *ctx, JSValue obj, JSValue key); void *js_realloc_rt (void *ptr, size_t size); char *js_strdup_rt (const char *str); JSValue JS_ConcatString (JSContext *ctx, JSValue op1, JSValue op2); __exception int js_post_inc_slow (JSContext *ctx, JSValue *sp, OPCodeEnum op); no_inline int js_not_slow (JSContext *ctx, JSValue *sp); no_inline int js_relational_slow (JSContext *ctx, JSValue *sp, OPCodeEnum op); __exception int js_operator_in (JSContext *ctx, JSValue *sp); __exception int js_operator_delete (JSContext *ctx, JSValue *sp); JSText *pretext_init (JSContext *ctx, int capacity); JSText *pretext_putc (JSContext *ctx, JSText *s, uint32_t c); JSText *pretext_concat_value (JSContext *ctx, JSText *s, JSValue v); JSValue js_new_blob (JSContext *ctx, blob *b); /* Functions from header region (defined in runtime.c) */ void *js_realloc (JSContext *ctx, void *ptr, size_t size); void *st_alloc (JSContext *ctx, size_t bytes, size_t align); void st_free_all (JSContext *ctx); int st_text_resize (JSContext *ctx); JSValue intern_text_to_value (JSContext *ctx, const uint32_t *utf32, uint32_t len); JSValue js_key_new (JSContext *ctx, const char *str); JSValue js_key_new_len (JSContext *ctx, const char *str, size_t len); uint64_t js_key_hash (JSValue key); JS_BOOL js_key_equal (JSValue a, JSValue b); JS_BOOL js_key_equal_str (JSValue a, const char *str); int rec_find_slot (JSRecord *rec, JSValue k); JSValue rec_get (JSContext *ctx, JSRecord *rec, JSValue k); int rec_resize (JSContext *ctx, JSValue *pobj, uint64_t new_mask); int rec_set_own (JSContext *ctx, JSValue *pobj, JSValue k, JSValue val); JSRecord *js_new_record_class (JSContext *ctx, uint32_t initial_mask, JSClassID class_id); int JS_SetPropertyInternal (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val); uint64_t get_text_hash (JSText *text); void pack_utf32_to_words (const uint32_t *utf32, uint32_t len, uint64_t *packed); int text_equal (JSText *a, const uint64_t *packed_b, uint32_t len_b); static inline JSValue *get_upvalue_ptr (JSValue frame_val, int depth, int slot) { if (JS_IsNull(frame_val)) return NULL; JSFrame *frame = JS_VALUE_GET_FRAME(frame_val); while (depth > 0) { JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); frame_val = fn->u.reg.outer_frame; if (JS_IsNull(frame_val)) return NULL; frame = JS_VALUE_GET_FRAME(frame_val); depth--; } return &frame->slots[slot]; } void print_backtrace (JSContext *ctx, const char *filename, int line_num, int col_num); JSValue JS_ThrowError2 (JSContext *ctx, JSErrorEnum error_num, const char *fmt, va_list ap, BOOL add_backtrace); JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *from_end, uint8_t *to_base, uint8_t **to_free, uint8_t *to_end); PPretext *ppretext_init (int capacity); PPretext *ppretext_putc (PPretext *p, uint32_t c); void ppretext_free (PPretext *p); JSValue ppretext_end (JSContext *ctx, PPretext *p); PPretext *ppretext_append_jsvalue (PPretext *p, JSValue str); PPretext *ppretext_append_int (PPretext *p, int n); JSValue js_atof (JSContext *ctx, const char *str, const char **pp, int radix, int flags); /* Defines from runtime section needed by cell_js.c */ #define DEFINE_GLOBAL_LEX_VAR (1 << 7) #define DEFINE_GLOBAL_FUNC_VAR (1 << 6) #define ATOD_INT_ONLY (1 << 0) /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ #define ATOD_ACCEPT_BIN_OCT (1 << 2) /* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ #define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) /* accept _ between digits as a digit separator */ #define ATOD_ACCEPT_UNDERSCORES (1 << 5) /* allow a suffix to override the type */ #define ATOD_ACCEPT_SUFFIX (1 << 6) /* default type */ #define ATOD_TYPE_MASK (3 << 7) #define ATOD_TYPE_FLOAT64 (0 << 7) #define ATOD_TYPE_BIG_INT (1 << 7) /* accept -0x1 */ #define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) #define GLOBAL_VAR_OFFSET 0x40000000 #define ARGUMENT_VAR_OFFSET 0x20000000 /* Inline functions from runtime section needed by cell_js.c */ static inline void js_dbuf_init (JSContext *ctx, DynBuf *s) { dbuf_init2 (s, ctx->rt, NULL); } static inline int to_digit (int c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'A' && c <= 'Z') return c - 'A' + 10; else if (c >= 'a' && c <= 'z') return c - 'a' + 10; else return 36; } static inline void dbuf_put_leb128 (DynBuf *s, uint32_t v) { uint32_t a; for (;;) { a = v & 0x7f; v >>= 7; if (v != 0) { dbuf_putc (s, a | 0x80); } else { dbuf_putc (s, a); break; } } } static inline void dbuf_put_sleb128 (DynBuf *s, int32_t v1) { uint32_t v = v1; dbuf_put_leb128 (s, (2 * v) ^ -(v >> 31)); } static inline int get_leb128 (uint32_t *pval, const uint8_t *buf, const uint8_t *buf_end) { const uint8_t *ptr = buf; uint32_t v, a, i; v = 0; for (i = 0; i < 5; i++) { if (unlikely (ptr >= buf_end)) break; a = *ptr++; v |= (a & 0x7f) << (i * 7); if (!(a & 0x80)) { *pval = v; return ptr - buf; } } *pval = 0; return -1; } static inline int get_sleb128 (int32_t *pval, const uint8_t *buf, const uint8_t *buf_end) { int ret; uint32_t val; ret = get_leb128 (&val, buf, buf_end); if (ret < 0) { *pval = 0; return -1; } *pval = (val >> 1) ^ -(val & 1); return ret; } no_inline int js_realloc_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size); static inline int js_resize_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size) { if (unlikely (req_size > *psize)) return js_realloc_array (ctx, parray, elem_size, psize, req_size); else return 0; } JSText *js_alloc_string (JSContext *ctx, int max_len); JSValue js_key_from_string (JSContext *ctx, JSValue val); /* mach.c exports */ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue this_obj, int argc, JSValue *argv, JSValue env, JSValue outer_frame); JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count); cJSON *mach_find_scope_record(cJSON *scopes, int function_nr); int reg_vm_check_interrupt(JSContext *ctx); #endif /* QUICKJS_INTERNAL_H */