#ifndef CELL_H #define CELL_H #include #include #include #include "wota.h" #ifdef __cplusplus extern "C" { #endif #if defined(__GNUC__) || defined(__clang__) #define __js_printf_like(f, a) __attribute__ ((format (printf, f, a))) #else #define __js_printf_like(a, b) #endif /* ============================================================ JSValue — 64-bit tagged value ============================================================ */ typedef uint64_t JSValue; typedef JSValue JSValueConst; #define JS_BOOL int typedef struct JSRuntime JSRuntime; // the entire VM typedef struct JSContext JSContext; // Each actor typedef struct JSClass JSClass; typedef uint32_t JSClassID; /* Forward declaration - JSGCRef moved after JSValue definition */ struct JSGCRef; /* ============================================================ Mist Value Encoding (LSB-based type discrimination) ============================================================ 64-bit builds: LSB = 0 → 31-bit signed integer (value >> 1) LSB = 001 → 61-bit pointer (aligned, clear low 3 bits) LSB = 101 → Short float (61-bit, 3 fewer exponent bits than double) LSB = 11 → Special tag (next 3 bits = subtype, 5 bits total) 32-bit builds: LSB = 0 → 31-bit signed integer LSB = 01 → 30-bit pointer LSB = 11 → Special tag (next 3 bits = subtype) ============================================================ */ /* LSB-based tags */ enum { /* Primary tags (low bits) */ JS_TAG_INT = 0, /* LSB = 0 */ JS_TAG_PTR = 1, /* LSB = 01 */ JS_TAG_SHORT_FLOAT = 5, /* LSB = 101 */ JS_TAG_SPECIAL = 3, /* LSB = 11 */ /* Special subtypes (5 bits: xxxx11) */ JS_TAG_BOOL = 0x03, /* 00011 */ JS_TAG_NULL = 0x07, /* 00111 */ JS_TAG_EXCEPTION = 0x0F, /* 01111 */ JS_TAG_STRING_IMM = 0x0B, /* 01011 - immediate ASCII (up to 7 chars) */ }; /* Compatibility tag aliases for external code */ #define JS_TAG_STRING JS_TAG_STRING_IMM /* Alias: text/string type */ #define JS_TAG_FLOAT64 JS_TAG_SHORT_FLOAT /* Alias: floats use short float encoding */ #define JS_EMPTY_TEXT ((JSValue)JS_TAG_STRING_IMM) /* ============================================================ GC Rooting Structures ============================================================ */ /* JSGCRef - for rooting values during GC */ typedef struct JSGCRef { JSValue val; struct JSGCRef *prev; } JSGCRef; /* JSLocalRef - GC updates C locals through pointers (OCaml-style) */ typedef struct JSLocalRef { JSValue *ptr; struct JSLocalRef *prev; } JSLocalRef; /* stack of JSGCRef */ JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref); JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref); /* JS_FRAME/JS_ROOT/JS_LOCAL helpers (for use from cell.h macros) */ JSGCRef *JS_GetGCFrame(JSContext *ctx); JSLocalRef *JS_GetLocalFrame(JSContext *ctx); void JS_PushLocalRef(JSContext *ctx, JSLocalRef *ref); void JS_RestoreFrame(JSContext *ctx, JSGCRef *gc_frame, JSLocalRef *local_frame); #define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0) #define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref) /* list of JSGCRef (they can be removed in any order, slower) */ JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref); void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref); /* ============================================================ Value Extraction ============================================================ */ /* Get primary tag (low 2-3 bits) */ static inline int JS_VALUE_GET_TAG (JSValue v) { if ((v & 1) == 0) return JS_TAG_INT; if ((v & 7) == JS_TAG_SHORT_FLOAT) return JS_TAG_SHORT_FLOAT; if ((v & 3) == JS_TAG_PTR) return JS_TAG_PTR; return (int)(v & 0x1F); /* special tag */ } #define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG (v) /* Extract 31-bit signed int (when LSB=0) */ #define JS_VALUE_GET_INT(v) ((int32_t)((int64_t)(v) >> 1)) /* Extract bool value from special tag */ #define JS_VALUE_GET_BOOL(v) ((int)((v) >> 5) & 1) /* Extract special tag payload (bits 5+) */ #define JS_VALUE_GET_SPECIAL_PAYLOAD(v) ((int32_t)((v) >> 5)) /* ============================================================ Value Creation ============================================================ */ #define JS_MKVAL(tag, val) _JS_MkVal (tag, val) static inline JSValue _JS_MkVal (int tag, int32_t val) { if (tag == JS_TAG_INT) { return ((JSValue)(uint32_t)val << 1); } /* Special tags encode payload in upper bits */ return ((JSValue)tag) | ((JSValue)(uint32_t)val << 5); } /* ============================================================ Short Float (64-bit only) Range: ~+-3.4e38 (8-bit exponent vs double's 11-bit) Out of range → JS_NULL ============================================================ */ static inline JSValue __JS_NewFloat64 (JSContext *ctx, double d) { union { double d; uint64_t u; } u; u.d = d; uint64_t sign = u.u >> 63; int exp = (u.u >> 52) & 0x7FF; uint64_t mantissa = u.u & ((1ULL << 52) - 1); /* Zero → short float zero (always +0, no -0) */ if (exp == 0 && mantissa == 0) { return JS_TAG_SHORT_FLOAT; } /* NaN/Inf → NULL */ if (exp == 0x7FF) { return JS_MKVAL (JS_TAG_NULL, 0); } /* Subnormals → zero */ if (exp == 0) { return (sign << 63) | JS_TAG_SHORT_FLOAT; } /* Convert exponent: double bias 1023 → short bias 127 */ int short_exp = exp - 1023 + 127; if (short_exp < 1 || short_exp > 254) { return JS_MKVAL (JS_TAG_NULL, 0); /* out of range */ } /* Prefer integer if exact */ if (d >= INT32_MIN && d <= INT32_MAX) { int32_t i = (int32_t)d; if ((double)i == d) { return JS_MKVAL (JS_TAG_INT, i); } } /* Encode: [sign:1][exp:8][mantissa:52][tag:3] */ return (sign << 63) | ((uint64_t)short_exp << 55) | (mantissa << 3) | JS_TAG_SHORT_FLOAT; } static inline double JS_VALUE_GET_FLOAT64 (JSValue v) { if ((v & 7) != JS_TAG_SHORT_FLOAT) return 0.0; uint64_t sign = v >> 63; uint64_t short_exp = (v >> 55) & 0xFF; uint64_t mantissa = (v >> 3) & ((1ULL << 52) - 1); if (short_exp == 0) return 0.0; /* Always +0, no -0 */ uint64_t exp = short_exp - 127 + 1023; union { double d; uint64_t u; } u; u.u = (sign << 63) | (exp << 52) | mantissa; return u.d; } #define JS_TAG_IS_FLOAT64(tag) ((tag) == JS_TAG_SHORT_FLOAT) #define JS_NAN JS_MKVAL (JS_TAG_NULL, 0) /* ============================================================ Type Checks ============================================================ */ static inline JS_BOOL JS_IsInt (JSValue v) { return (v & 1) == 0; } static inline JS_BOOL JS_IsPtr (JSValue v) { return (v & 7) == JS_TAG_PTR; } static inline JS_BOOL JS_IsSpecial (JSValue v) { return (v & 3) == JS_TAG_SPECIAL; } static inline JS_BOOL JS_IsShortFloat (JSValue v) { return (v & 7) == JS_TAG_SHORT_FLOAT; } #define JS_VALUE_IS_BOTH_INT(v1, v2) (((v1) & 1) == 0 && ((v2) & 1) == 0) #define JS_VALUE_IS_BOTH_FLOAT(v1, v2) \ (JS_IsShortFloat (v1) && JS_IsShortFloat (v2)) /* ============================================================ Special Constants ============================================================ */ #define JS_NULL ((JSValue)JS_TAG_NULL) #define JS_FALSE ((JSValue)JS_TAG_BOOL) #define JS_TRUE ((JSValue)(JS_TAG_BOOL | (1 << 5))) #define JS_EXCEPTION ((JSValue)JS_TAG_EXCEPTION) /* Immediate ASCII string helpers */ #define MIST_ASCII_MAX_LEN 7 static inline JS_BOOL MIST_IsImmediateASCII (JSValue v) { return (v & 0x1F) == JS_TAG_STRING_IMM; } static inline int MIST_GetImmediateASCIILen (JSValue v) { return (int)((v >> 5) & 0x7); } static inline int MIST_GetImmediateASCIIChar (JSValue v, int idx) { return (int)((v >> (8 + idx * 8)) & 0xFF); } static inline JSValue MIST_TryNewImmediateASCII (const char *str, size_t len) { if (len > MIST_ASCII_MAX_LEN) return JS_NULL; JSValue v = (JSValue)JS_TAG_STRING_IMM | ((JSValue)len << 5); for (size_t i = 0; i < len; i++) { uint8_t c = (uint8_t)str[i]; if (c >= 0x80) return JS_NULL; v |= (JSValue)c << (8 + i * 8); } return v; } /* Heap object type checks (non-inline — see mist_is_* in pit_internal.h for inline versions used by the VM dispatch loop) */ JS_BOOL JS_IsArray(JSValue v); JS_BOOL JS_IsRecord(JSValue v); #define JS_IsObject JS_IsRecord JS_BOOL JS_IsFunction(JSValue v); JS_BOOL JS_IsBlob(JSValue v); JS_BOOL JS_IsText(JSValue v); JS_BOOL JS_IsStone(JSValue v); /* Sensory function wrappers (require JSContext for string inspection) */ JS_BOOL JS_IsDigit(JSContext *ctx, JSValue val); JS_BOOL JS_IsLetter(JSContext *ctx, JSValue val); JS_BOOL JS_IsLower(JSContext *ctx, JSValue val); JS_BOOL JS_IsUpper(JSContext *ctx, JSValue val); JS_BOOL JS_IsWhitespace(JSContext *ctx, JSValue val); JS_BOOL JS_IsCharacter(JSContext *ctx, JSValue val); JS_BOOL JS_IsFit(JSContext *ctx, JSValue val); JS_BOOL JS_IsData(JSValue val); static inline JS_BOOL JS_IsTrue(JSValue v) { return v == JS_TRUE; } static inline JS_BOOL JS_IsFalse(JSValue v) { return v == JS_FALSE; } JS_BOOL JS_IsActor(JSContext *ctx, JSValue val); /* ============================================================ C Function Typedefs ============================================================ */ typedef JSValue JSCFunction (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); typedef JSValue JSCFunctionMagic (JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic); typedef JSValue JSCFunctionData (JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic, JSValue *data); /* ============================================================ Class System ============================================================ */ typedef void JSClassFinalizer (JSRuntime *rt, JSValue val); typedef JSValue JSClassCall (JSContext *ctx, JSValue func_obj, JSValue this_val, int argc, JSValue *argv, int flags); typedef struct JSClassDef { const char *class_name; JSClassFinalizer *finalizer; JSClassCall *call; } JSClassDef; #define JS_INVALID_CLASS_ID 0 extern JSClassID js_class_id_alloc; JSClassID JS_NewClassID (JSClassID *pclass_id); /* Returns the class ID if `v` is an object, otherwise returns * JS_INVALID_CLASS_ID. */ JSClassID JS_GetClassID (JSValue v); int JS_NewClass (JSContext *ctx, JSClassID class_id, const JSClassDef *class_def); int JS_IsRegisteredClass (JSContext *ctx, JSClassID class_id); void JS_SetClassProto (JSContext *ctx, JSClassID class_id, JSValue obj); JSValue JS_GetClassProto (JSContext *ctx, JSClassID class_id); /* ============================================================ Value Creation ============================================================ */ static inline JSValue JS_NewBool (JSContext *ctx, JS_BOOL val) { return JS_MKVAL (JS_TAG_BOOL, (val != 0)); } static inline JSValue JS_NewInt32 (JSContext *ctx, int32_t val) { return JS_MKVAL (JS_TAG_INT, val); } static inline JSValue JS_NewInt64 (JSContext *ctx, int64_t val) { JSValue v; if (val == (int32_t)val) { v = JS_NewInt32 (ctx, val); } else { v = __JS_NewFloat64 (ctx, val); } return v; } static inline JSValue JS_NewUint32 (JSContext *ctx, uint32_t val) { JSValue v; if (val <= 0x7fffffff) { v = JS_NewInt32 (ctx, val); } else { v = __JS_NewFloat64 (ctx, val); } return v; } static inline JSValue JS_NewFloat64 (JSContext *ctx, double d) { int32_t val; union { double d; uint64_t u; } u, t; if (d >= INT32_MIN && d <= INT32_MAX) { u.d = d; val = (int32_t)d; t.d = val; /* -0 cannot be represented as integer, so we compare the bit representation */ if (u.u == t.u) return JS_MKVAL (JS_TAG_INT, val); } return __JS_NewFloat64 (ctx, d); } /* Inline type checks (immediate tags) */ static inline JS_BOOL JS_IsNumber (JSValue v) { int tag = JS_VALUE_GET_TAG (v); return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64 (tag); } JSValue wota2value(JSContext *js, void *data); static inline JS_BOOL JS_IsBool (JSValue v) { return JS_VALUE_GET_TAG (v) == JS_TAG_BOOL; } static inline JS_BOOL JS_IsNull (JSValue v) { return JS_VALUE_GET_TAG (v) == JS_TAG_NULL; } static inline JS_BOOL JS_IsException (JSValue v) { return (JS_VALUE_GET_TAG (v) == JS_TAG_EXCEPTION); } /* ============================================================ 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); 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); /* ============================================================ 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_NewObjectCap (JSContext *ctx, uint32_t n); JSValue JS_NewArray (JSContext *ctx); JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len); JSValue JS_NewArrayCap (JSContext *ctx, uint32_t cap); 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)); } /* ============================================================ 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); /* ============================================================ Error Handling ============================================================ */ JSValue JS_GetException (JSContext *ctx); JS_BOOL JS_HasException (JSContext *ctx); /* Set the disruption flag. No logging. */ JSValue JS_Disrupt (JSContext *ctx); /* Log a message to a named channel. Routes through the ƿit log callback if one has been set, otherwise falls back to fprintf(stderr). */ void __js_printf_like (3, 4) JS_Log (JSContext *ctx, const char *channel, const char *fmt, ...); /* Log to "error" channel + raise disruption. The common case. */ JSValue __js_printf_like (2, 3) JS_RaiseDisrupt (JSContext *ctx, const char *fmt, ...); /* Log to "memory" channel + disrupt. Skips JS callback (can't allocate). */ JSValue JS_RaiseOOM (JSContext *ctx); #define JS_ThrowOutOfMemory JS_RaiseOOM #define JS_ThrowReferenceError JS_RaiseDisrupt #define JS_ThrowTypeError JS_RaiseDisrupt #define JS_ThrowInternalError JS_RaiseDisrupt #define JS_ThrowRangeError JS_RaiseDisrupt /* ============================================================ Function Invocation ============================================================ */ JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv); JSValue JS_Stone (JSContext *ctx, JSValue this_val); /* JSON */ /* '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, JS_BOOL compact_arrays); /* ============================================================ Intrinsic Wrappers (JS_Cell* / JS_Array*) ============================================================ */ /* Intrinsic array operations */ JSValue JS_Array (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3); JSValue JS_ArrayFilter (JSContext *ctx, JSValue arr, JSValue fn); JSValue JS_ArraySort (JSContext *ctx, JSValue arr, JSValue selector); JSValue JS_ArrayFind (JSContext *ctx, JSValue arr, JSValue target_or_fn, JSValue reverse, JSValue from); 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); /* Core cell functions */ JSValue JS_CellStone (JSContext *ctx, JSValue val); JSValue JS_CellLength (JSContext *ctx, JSValue val); JSValue JS_CellReverse (JSContext *ctx, JSValue val); JSValue JS_CellProto (JSContext *ctx, JSValue obj); JSValue JS_CellSplat (JSContext *ctx, JSValue val); JSValue JS_CellMeme (JSContext *ctx, JSValue obj, JSValue deep); JSValue JS_CellApply (JSContext *ctx, JSValue fn, JSValue args); JSValue JS_CellCall (JSContext *ctx, JSValue fn, JSValue this_val, JSValue args); JSValue JS_CellModulo (JSContext *ctx, JSValue a, JSValue b); JSValue JS_CellNeg (JSContext *ctx, JSValue val); JSValue JS_CellNot (JSContext *ctx, JSValue val); /* Text cell functions */ JSValue JS_CellText (JSContext *ctx, JSValue val); JSValue JS_CellLower (JSContext *ctx, JSValue text); JSValue JS_CellUpper (JSContext *ctx, JSValue text); JSValue JS_CellTrim (JSContext *ctx, JSValue text, JSValue chars); JSValue JS_CellCodepoint (JSContext *ctx, JSValue text, JSValue idx); JSValue JS_CellReplace (JSContext *ctx, JSValue text, JSValue pattern, JSValue replacement); JSValue JS_CellSearch (JSContext *ctx, JSValue text, JSValue pattern, JSValue from); JSValue JS_CellExtract (JSContext *ctx, JSValue text, JSValue from, JSValue to); JSValue JS_CellCharacter (JSContext *ctx, JSValue codepoint); /* Number cell functions */ JSValue JS_CellNumber (JSContext *ctx, JSValue val); JSValue JS_CellAbs (JSContext *ctx, JSValue num); JSValue JS_CellSign (JSContext *ctx, JSValue num); JSValue JS_CellFloor (JSContext *ctx, JSValue num); JSValue JS_CellCeiling (JSContext *ctx, JSValue num); JSValue JS_CellRound (JSContext *ctx, JSValue num); JSValue JS_CellTrunc (JSContext *ctx, JSValue num); JSValue JS_CellWhole (JSContext *ctx, JSValue num); JSValue JS_CellFraction (JSContext *ctx, JSValue num); JSValue JS_CellMin (JSContext *ctx, JSValue a, JSValue b); JSValue JS_CellMax (JSContext *ctx, JSValue a, JSValue b); JSValue JS_CellRemainder (JSContext *ctx, JSValue a, JSValue b); /* Object cell functions */ JSValue JS_CellObject (JSContext *ctx, JSValue proto, JSValue props); /* Format */ JSValue JS_CellFormat (JSContext *ctx, JSValue text, JSValue collection, JSValue transformer); /* Additional cell functions */ JSValue JS_CellLogical (JSContext *ctx, JSValue val); JSValue JS_CellEvery (JSContext *ctx, JSValue arr, JSValue pred); JSValue JS_CellSome (JSContext *ctx, JSValue arr, JSValue pred); JSValue JS_CellStartsWith (JSContext *ctx, JSValue text, JSValue prefix); JSValue JS_CellEndsWith (JSContext *ctx, JSValue text, JSValue suffix); JSValue JS_CellNormalize (JSContext *ctx, JSValue text); /* Output helpers */ void JS_PrintText (JSContext *ctx, JSValue val); void JS_PrintTextLn (JSContext *ctx, JSValue val); void JS_PrintFormatted (JSContext *ctx, const char *fmt, int count, JSValue *values); /* ============================================================ C Function Definition ============================================================ */ typedef enum JSCFunctionEnum { JS_CFUNC_generic, JS_CFUNC_generic_magic, JS_CFUNC_f_f, JS_CFUNC_f_f_f, /* Fixed-arity fast paths - no argc/argv overhead */ JS_CFUNC_0, /* JSValue f(ctx, this_val) */ JS_CFUNC_1, /* JSValue f(ctx, this_val, arg0) */ JS_CFUNC_2, /* JSValue f(ctx, this_val, arg0, arg1) */ JS_CFUNC_3, /* JSValue f(ctx, this_val, arg0, arg1, arg2) */ JS_CFUNC_4, /* Pure functions (no this_val) - for global utility functions */ JS_CFUNC_PURE, /* JSValue f(ctx, argc, argv) - generic pure */ JS_CFUNC_PURE_0, /* JSValue f(ctx) */ JS_CFUNC_PURE_1, /* JSValue f(ctx, arg0) */ JS_CFUNC_PURE_2, /* JSValue f(ctx, arg0, arg1) */ JS_CFUNC_PURE_3, /* JSValue f(ctx, arg0, arg1, arg2) */ JS_CFUNC_PURE_4 /* JSValue f(ctx, arg0, arg1, arg2, arg3) */ } JSCFunctionEnum; /* Fixed-arity C function types for fast paths */ typedef JSValue JSCFunction0 (JSContext *ctx, JSValue this_val); typedef JSValue JSCFunction1 (JSContext *ctx, JSValue this_val, JSValue arg0); typedef JSValue JSCFunction2 (JSContext *ctx, JSValue this_val, JSValue arg0, JSValue arg1); typedef JSValue JSCFunction3 (JSContext *ctx, JSValue this_val, JSValue arg0, JSValue arg1, JSValue arg2); typedef JSValue JSCFunction4 (JSContext *ctx, JSValue this_val, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3); /* Pure function types (no this_val) */ typedef JSValue JSCFunctionPure (JSContext *ctx, int argc, JSValue *argv); typedef JSValue JSCFunctionPure0 (JSContext *ctx); typedef JSValue JSCFunctionPure1 (JSContext *ctx, JSValue arg0); typedef JSValue JSCFunctionPure2 (JSContext *ctx, JSValue arg0, JSValue arg1); typedef JSValue JSCFunctionPure3 (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2); typedef JSValue JSCFunctionPure4 (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3); typedef union JSCFunctionType { JSCFunction *generic; JSValue (*generic_magic) (JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic); double (*f_f) (double); double (*f_f_f) (double, double); /* Fixed-arity fast paths */ JSCFunction0 *f0; JSCFunction1 *f1; JSCFunction2 *f2; JSCFunction3 *f3; JSCFunction4 *f4; /* Pure function pointers */ JSCFunctionPure *pure; JSCFunctionPure0 *pure0; JSCFunctionPure1 *pure1; JSCFunctionPure2 *pure2; JSCFunctionPure3 *pure3; JSCFunctionPure4 *pure4; } JSCFunctionType; JSValue JS_NewCFunction2 (JSContext *ctx, JSCFunction *func, const char *name, int length, JSCFunctionEnum cproto, int magic); static inline JSValue JS_NewCFunction (JSContext *ctx, JSCFunction *func, const char *name, int length) { return JS_NewCFunction2 (ctx, func, name, length, JS_CFUNC_generic, 0); } static inline JSValue JS_NewCFunctionMagic (JSContext *ctx, JSCFunctionMagic *func, const char *name, int length, JSCFunctionEnum cproto, int magic) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, length, cproto, magic); } /* Fixed-arity fast path constructors */ static inline JSValue JS_NewCFuncFixed0 (JSContext *ctx, JSCFunction0 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 0, JS_CFUNC_0, 0); } static inline JSValue JS_NewCFuncFixed1 (JSContext *ctx, JSCFunction1 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 1, JS_CFUNC_1, 0); } static inline JSValue JS_NewCFuncFixed2 (JSContext *ctx, JSCFunction2 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 2, JS_CFUNC_2, 0); } static inline JSValue JS_NewCFuncFixed3 (JSContext *ctx, JSCFunction3 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 3, JS_CFUNC_3, 0); } static inline JSValue JS_NewCFuncFixed4 (JSContext *ctx, JSCFunction4 *func, const char *name) { return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 4, JS_CFUNC_4, 0); } /* C property definition */ typedef struct JSCFunctionListEntry { const char *name; uint8_t prop_flags; uint8_t def_type; int16_t magic; union { struct { uint8_t length; /* XXX: should move outside union */ uint8_t cproto; /* XXX: should move outside union */ JSCFunctionType cfunc; } func; struct { const char *name; int base; } alias; struct { const struct JSCFunctionListEntry *tab; int len; } prop_list; const char *str; int32_t i32; int64_t i64; double f64; } u; } JSCFunctionListEntry; #define JS_DEF_CFUNC 0 #define JS_DEF_PROP_STRING 3 #define JS_DEF_PROP_INT32 4 #define JS_DEF_PROP_INT64 5 #define JS_DEF_PROP_DOUBLE 6 #define JS_DEF_PROP_UNDEFINED 7 #define JS_DEF_OBJECT 8 #define JS_DEF_ALIAS 9 /* Note: c++ does not like nested designators */ #define JS_CFUNC_DEF(name, length, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { length, JS_CFUNC_generic, { .generic = func1 } } } \ } #define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) \ { \ name, 0, JS_DEF_CFUNC, magic, .u = { \ .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } \ } \ } #define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { length, JS_CFUNC_##cproto, { .cproto = func1 } } } \ } /* Fixed-arity fast path macros */ #define JS_CFUNC0_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 0, JS_CFUNC_0, { .f0 = func1 } } } \ } #define JS_CFUNC1_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 1, JS_CFUNC_1, { .f1 = func1 } } } \ } #define JS_CFUNC2_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 2, JS_CFUNC_2, { .f2 = func1 } } } \ } #define JS_CFUNC3_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 3, JS_CFUNC_3, { .f3 = func1 } } } \ } /* Pure function (no this_val) macros */ #define JS_CFUNC_PURE_DEF(name, length, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { length, JS_CFUNC_PURE, { .pure = func1 } } } \ } #define JS_CFUNC_PURE0_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 0, JS_CFUNC_PURE_0, { .pure0 = func1 } } } \ } #define JS_CFUNC_PURE1_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 1, JS_CFUNC_PURE_1, { .pure1 = func1 } } } \ } #define JS_CFUNC_PURE2_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 2, JS_CFUNC_PURE_2, { .pure2 = func1 } } } \ } #define JS_CFUNC_PURE3_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 3, JS_CFUNC_PURE_3, { .pure3 = func1 } } } \ } #define JS_CFUNC_PURE4_DEF(name, func1) \ { \ name, 0, JS_DEF_CFUNC, 0, \ .u \ = {.func = { 4, JS_CFUNC_PURE_4, { .pure4 = func1 } } } \ } #define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) \ { \ name, 0, JS_DEF_CFUNC, magic, .u = { \ .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } \ } \ } #define JS_PROP_STRING_DEF(name, cstr, prop_flags) \ { \ name, prop_flags, JS_DEF_PROP_STRING, 0, .u = {.str = cstr } \ } #define JS_PROP_INT32_DEF(name, val, prop_flags) \ { \ name, prop_flags, JS_DEF_PROP_INT32, 0, .u = {.i32 = val } \ } #define JS_PROP_INT64_DEF(name, val, prop_flags) \ { \ name, prop_flags, JS_DEF_PROP_INT64, 0, .u = {.i64 = val } \ } #define JS_PROP_DOUBLE_DEF(name, val, prop_flags) \ { \ name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = {.f64 = val } \ } #define JS_PROP_UNDEFINED_DEF(name, prop_flags) \ { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } } #define JS_OBJECT_DEF(name, tab, len, prop_flags) \ { \ name, prop_flags, JS_DEF_OBJECT, 0, .u = {.prop_list = { tab, len } } \ } #define JS_ALIAS_DEF(name, from) \ { \ name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, \ .u \ = {.alias = { from, -1 } } \ } #define JS_ALIAS_BASE_DEF(name, from, base) \ { \ name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, \ .u \ = {.alias = { from, base } } \ } int JS_SetPropertyFunctionList (JSContext *ctx, JSValue obj, const JSCFunctionListEntry *tab, int len); /* ============================================================ Debug Hooks (public) ============================================================ */ typedef struct js_debug { char name[64]; char filename[96]; int unique; int line; int param_n; int closure_n; int vararg; const char *what; const uint8_t *source; int srclen; } js_debug; typedef void (*js_hook) (JSContext *, int type, js_debug *dbg, void *user); #define JS_HOOK_CALL 1 #define JS_HOOK_RET 2 #define JS_HOOK_CYCLE 4 #define JS_HOOK_GC 8 void js_debug_sethook (JSContext *ctx, js_hook, int type, void *user); /* ============================================================ Cell Hooks ============================================================ */ #define CELL_HOOK_ENTER 1 #define CELL_HOOK_EXIT 2 typedef void (*cell_hook)(JSContext *ctx, int type); void cell_trace_sethook(cell_hook); cell_hook cell_trace_gethook(void); void cell_rt_set_trace_hook(JSContext *ctx, cell_hook hook); size_t cell_ctx_heap_used(JSContext *ctx); void js_debug_gethook(JSContext *ctx, js_hook *hook, int *type, void **user); /* ============================================================ Off-heap Memory Allocation (not GC-managed) ============================================================ */ void *js_malloc_rt (size_t size); void *js_mallocz_rt (size_t size); void js_free_rt (void *ptr); /* ============================================================ Random Number Generation ============================================================ */ double cell_random(); uint64_t cell_random_fit(); /* ============================================================ Blob API ============================================================ */ JSValue js_new_blob_alloc(JSContext *js, size_t bytes, void **out); void js_blob_stone(JSValue blob, size_t actual_bytes); JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t bytes); void *js_get_blob_data(JSContext *js, size_t *size, JSValue v); // bytes void *js_get_blob_data_bits(JSContext *js, size_t *bits, JSValue v); // bits int js_is_blob(JSContext *js, JSValue v); #include "blob.h" /* ============================================================ Actor I/O Watch — event-driven fd monitoring ============================================================ */ void actor_watch_readable(JSContext *actor, int fd, JSValue fn); void actor_watch_writable(JSContext *actor, int fd, JSValue fn); void actor_unwatch(JSContext *actor, int fd); /* ============================================================ Convenience Functions ============================================================ */ int JS_ArrayLength(JSContext *js, JSValue a); int js2bool(JSContext *js, JSValue v); JSValue bool2js(JSContext *js, int b); double js2number(JSContext *js, JSValue v); JSValue number2js(JSContext *js, double g); /* ============================================================ Helper Macros ============================================================ */ #ifndef countof #define countof(x) (sizeof(x)/sizeof((x)[0])) #endif #define MIST_CFUNC_DEF(name, length, func1, props) { name, props, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } #define MIST_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN, 0) #define PROTO_FUNC_DEF(TYPE, FN, LEN) MIST_CFUNC_DEF(#FN, LEN, js_##TYPE##_##FN, 0) #define JS_SETSIG JSContext *js, JSValue self, JSValue val #define JSC_CCALL(NAME, ...) static JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \ JSValue ret = JS_NULL; \ __VA_ARGS__ ;\ return ret; \ } #define JSC_CCALL_EXTERN(NAME, ...) JSValue js_##NAME (JSContext *js, JSValue self, int argc, JSValue *argv) { \ JSValue ret = JS_NULL; \ __VA_ARGS__ ;\ return ret; \ } #define JSC_SCALL(NAME, ...) JSC_CCALL(NAME, \ const char *str = JS_ToCString(js,argv[0]); \ __VA_ARGS__ ;\ JS_FreeCString(js,str); \ ) #define JSC_SSCALL(NAME, ...) JSC_CCALL(NAME, \ const char *str = NULL; \ const char *str2 = NULL; \ if (!JS_IsNull(argv[0])) str = JS_ToCString(js,argv[0]); \ if (!JS_IsNull(argv[1])) str2 = JS_ToCString(js,argv[1]); \ __VA_ARGS__ ; \ JS_FreeCString(js,str2); \ JS_FreeCString(js,str); \ ) \ /* ============================================================ Class Definition Macros ============================================================ */ #define QJSCLASS(TYPE, ...)\ JSClassID js_##TYPE##_id;\ static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\ TYPE *n = JS_GetOpaque(val, js_##TYPE##_id); \ TYPE##_free(rt,n);}\ static JSClassDef js_##TYPE##_class = {\ .class_name = #TYPE,\ .finalizer = js_##TYPE##_finalizer,\ };\ TYPE *js2##TYPE (JSContext *js, JSValue val) { \ if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \ return JS_GetOpaque(val,js_##TYPE##_id); \ }\ JSValue TYPE##2js(JSContext *js, TYPE *n) { \ JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\ JS_SetOpaque(j,n);\ __VA_ARGS__ \ return j; }\ \ #define QJSCLASSMARK(TYPE, ...)\ static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\ TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\ TYPE##_free(rt,n);}\ static JSClassDef js_##TYPE##_class = {\ .class_name = #TYPE,\ .finalizer = js_##TYPE##_finalizer,\ };\ TYPE *js2##TYPE (JSContext *js, JSValue val) { \ if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \ return JS_GetOpaque(val,js_##TYPE##_id); \ }\ JSValue TYPE##2js(JSContext *js, TYPE *n) { \ JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\ JS_SetOpaque(j,n);\ __VA_ARGS__ \ return j; }\ \ #define QJSCLASSMARK_EXTERN(TYPE, ...)\ static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val){\ TYPE *n = JS_GetOpaque(val, js_##TYPE##_id);\ TYPE##_free(rt,n);}\ static JSClassDef js_##TYPE##_class = {\ .class_name = #TYPE,\ .finalizer = js_##TYPE##_finalizer,\ };\ extern JSClassID js_##TYPE##_id;\ TYPE *js2##TYPE (JSContext *js, JSValue val) { \ if (JS_GetClassID(val) != js_##TYPE##_id) return NULL; \ return JS_GetOpaque(val,js_##TYPE##_id); \ }\ JSValue TYPE##2js(JSContext *js, TYPE *n) { \ JSValue j = JS_NewObjectClass(js,js_##TYPE##_id);\ JS_SetOpaque(j,n);\ __VA_ARGS__ \ return j; }\ \ /* Defines a class and uses its function list as its prototype */ #define QJSCLASSPREP_FUNCS(TYPE) \ QJSCLASSPREP_NO_FUNCS(TYPE) \ JS_SetPropertyFunctionList(js, TYPE##_proto, js_##TYPE##_funcs, countof(js_##TYPE##_funcs)); \ #define QJSCLASSPREP_NO_FUNCS(TYPE) \ JS_NewClassID(&js_##TYPE##_id);\ JS_NewClass(js, js_##TYPE##_id, &js_##TYPE##_class);\ JSValue TYPE##_proto = JS_NewObject(js); \ JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \ #define QJSCLASSPREP_FUNCS_CTOR(TYPE, CTOR_ARGC) \ ({ \ QJSCLASSPREP_FUNCS(TYPE); \ JSValue TYPE##_ctor = JS_NewCFunction2(js, js_##TYPE##_constructor, #TYPE, CTOR_ARGC, JS_CFUNC_generic, 0); \ TYPE##_ctor; \ }) /* ============================================================ GC Safety Macros ============================================================ */ /* GC safety macros for C functions that allocate multiple heap objects. Any allocation call (JS_NewObject, JS_SetPropertyStr, etc.) can trigger GC. JS_ROOT style: explicit, use .val to access the rooted value. JS_LOCAL style: transparent, GC updates the C local through a pointer. */ #define JS_FRAME(ctx) \ JSContext *_js_ctx = (ctx); \ JSGCRef *_js_gc_frame = JS_GetGCFrame(_js_ctx); \ JSLocalRef *_js_local_frame = JS_GetLocalFrame(_js_ctx) #define JS_ROOT(name, init) \ JSGCRef name; \ JS_PushGCRef(_js_ctx, &name); \ name.val = (init) #define JS_LOCAL(name, init) \ JSValue name = (init); \ JSLocalRef name##__lr; \ name##__lr.ptr = &name; \ JS_PushLocalRef(_js_ctx, &name##__lr) #define JS_RETURN(val) do { \ JSValue _js_ret = (val); \ JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \ return _js_ret; \ } while (0) #define JS_RETURN_NULL() do { \ JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \ return JS_NULL; \ } while (0) #define JS_RETURN_EX() do { \ JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \ return JS_EXCEPTION; \ } while (0) /* ============================================================ Property Extraction Macros ============================================================ */ // Safe integer property extraction (null → 0) #define JS_GETINT(JS, TARGET, VALUE, PROP) { \ JSValue __##PROP##__v = JS_GetPropertyStr(JS, VALUE, #PROP); \ TARGET = JS_IsNull(__##PROP##__v) ? 0 : (int)js2number(JS, __##PROP##__v); } // Common macros for property access #define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\ JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \ TARGET = js2##TYPE(JS, __##PROP##__v); }\ #define JS_GETATOM(JS, TARGET, VALUE, ATOM, TYPE) {\ JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#ATOM); \ TARGET = js2##TYPE(JS, __##PROP##__v); }\ /* ============================================================ Enum Mapping System ============================================================ */ // X-macro enum definition system for string<->enum conversion #define ENUM_MAPPING_TABLE(ENUM) \ static const struct { int value; const char *name; } ENUM##_mapping[] #define JS2ENUM(NAME) \ int js2##NAME(JSContext *js, JSValue v) { \ if (JS_IsNull(v)) return 0; \ const char *str = JS_ToCString(js, v); \ if (!str) return 0; \ for(int i = 0; i < sizeof(NAME##_mapping)/sizeof(NAME##_mapping[0]); i++) \ if(!strcmp(NAME##_mapping[i].name, str)) { \ JS_FreeCString(js, str); \ return NAME##_mapping[i].value; \ } \ JS_FreeCString(js, str); \ return 0; \ } \ JSValue NAME##2js(JSContext *js, int enumval) { \ for(int i = 0; i < sizeof(NAME##_mapping)/sizeof(NAME##_mapping[0]); i++) \ if(NAME##_mapping[i].value == enumval) \ return JS_NewString(js, NAME##_mapping[i].name); \ return JS_NULL; \ } /* ============================================================ Module Init Macros ============================================================ */ #define CELL_USE_INIT(c) \ JSValue CELL_USE_NAME(JSContext *js) { do { c ; } while(0); } #define CELL_USE_FUNCS(FUNCS) \ JSValue CELL_USE_NAME(JSContext *js) { \ JS_FRAME(js); \ JS_LOCAL(mod, JS_NewObject(js)); \ JS_SetPropertyFunctionList(js, mod, FUNCS, countof(FUNCS)); \ JS_RETURN(mod); } #define CELL_PROGRAM_INIT(c) \ JSValue CELL_USE_NAME(JSContext *js) { do { c ; } while(0); } /* ============================================================ WOTA Message Sending — C modules can send messages to actors without needing a JSContext. ============================================================ */ /* Check whether an actor with the given ID exists. */ int JS_ActorExists(const char *actor_id); /* Send a WOTA-encoded message to an actor by ID. Takes ownership of wb's data on success (caller must not free). On failure returns an error string; caller must free wb. */ const char *JS_SendMessage(const char *actor_id, WotaBuffer *wb); #undef js_unlikely #undef inline #ifdef __cplusplus } #endif #endif /* CELL_H */