1174 lines
45 KiB
C
1174 lines
45 KiB
C
#ifndef CELL_H
|
|
#define CELL_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#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.
|
|
============================================================ */
|
|
|
|
/* Check whether an actor with the given ID exists. */
|
|
int JS_ActorExists(const char *actor_id);
|
|
|
|
/* Send a WOTA-encoded message to the actor that owns ctx.
|
|
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(JSContext *ctx, WotaBuffer *wb);
|
|
|
|
#undef js_unlikely
|
|
#undef inline
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* CELL_H */
|