1292 lines
49 KiB
C
1292 lines
49 KiB
C
/*
|
|
* QuickJS Javascript Engine
|
|
*
|
|
* Copyright (c) 2017-2021 Fabrice Bellard
|
|
* Copyright (c) 2017-2021 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.
|
|
*/
|
|
#ifndef QUICKJS_H
|
|
#define QUICKJS_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.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
|
|
|
|
// #define BINARY16 // 16 bit word type (half)
|
|
// #define BINARY32 // 32 bit word type (float)
|
|
// #define BINARY64 // 64 bit word type (double)
|
|
// #define DEC64 // 64 bit word type (dec)
|
|
|
|
enum mist_obj_type {
|
|
OBJ_ARRAY = 0,
|
|
OBJ_BLOB = 1,
|
|
OBJ_TEXT = 2,
|
|
OBJ_RECORD = 3, // js style objects
|
|
OBJ_FUNCTION = 4,
|
|
OBJ_CODE = 5,
|
|
OBJ_FRAME = 6,
|
|
OBJ_FORWARD = 7
|
|
};
|
|
|
|
|
|
#define OBJHDR_S_BIT 3u
|
|
#define OBJHDR_P_BIT 4u
|
|
#define OBJHDR_A_BIT 5u
|
|
#define OBJHDR_R_BIT 7u
|
|
|
|
|
|
#define OBJHDR_FLAG(bit) ((objhdr_t)1ull << (bit))
|
|
#define OBJHDR_S_MASK OBJHDR_FLAG (OBJHDR_S_BIT)
|
|
#define OBJHDR_P_MASK OBJHDR_FLAG (OBJHDR_P_BIT)
|
|
#define OBJHDR_A_MASK OBJHDR_FLAG (OBJHDR_A_BIT)
|
|
#define OBJHDR_R_MASK OBJHDR_FLAG (OBJHDR_R_BIT)
|
|
|
|
typedef uint64_t word_t; // one actor-memory word
|
|
typedef uint64_t objhdr_t; // header word
|
|
typedef uint64_t objref_t; // 56-bit word address (0 = null)
|
|
|
|
static inline uint8_t objhdr_type (objhdr_t h) { return (uint8_t)(h & 7u); }
|
|
static inline int objhdr_s (objhdr_t h) { return (h & OBJHDR_S_MASK) != 0; }
|
|
|
|
/*
|
|
Half: 10 bits
|
|
Float: 23 bits
|
|
Double: 52 bits
|
|
Dec16: 11 bits
|
|
Dec32: 26 bits
|
|
Dec64: 56 bits
|
|
|
|
On double builds, 48 bits
|
|
On Dec64, max
|
|
*/
|
|
|
|
#define JS_BOOL int
|
|
|
|
typedef struct JSRuntime JSRuntime; // the entire VM
|
|
typedef struct JSContext JSContext; // Each actor
|
|
typedef struct JSClass JSClass;
|
|
typedef struct JSFunctionBytecode JSFunctionBytecode;
|
|
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)
|
|
|
|
============================================================ */
|
|
|
|
#if INTPTR_MAX >= INT64_MAX
|
|
#define JS_PTR64
|
|
#define JS_PTR64_DEF(a) a
|
|
typedef uint64_t JSValue;
|
|
#define JSW 8
|
|
#else
|
|
typedef uint32_t JSValue;
|
|
#define JSW 4
|
|
#define JS_PTR64_DEF(a)
|
|
#endif
|
|
|
|
#define JSValue JSValue
|
|
|
|
/* JSValueConst is just JSValue (const is not needed in value semantics) */
|
|
typedef JSValue JSValueConst;
|
|
|
|
/* LSB-based tags */
|
|
enum {
|
|
/* Primary tags (low bits) */
|
|
JS_TAG_INT = 0, /* LSB = 0 */
|
|
JS_TAG_PTR = 1, /* LSB = 01 */
|
|
#ifdef JS_PTR64
|
|
JS_TAG_SHORT_FLOAT = 5, /* LSB = 101 */
|
|
#endif
|
|
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_UNINITIALIZED = 0x17, /* 10111 */
|
|
JS_TAG_STRING_IMM = 0x1B, /* 11011 - immediate ASCII (up to 7 chars) */
|
|
JS_TAG_CATCH_OFFSET = 0x1F, /* 11111 */
|
|
};
|
|
|
|
/* 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)
|
|
|
|
/* JSGCRef - for rooting values during GC */
|
|
typedef struct JSGCRef {
|
|
JSValue val;
|
|
struct JSGCRef *prev;
|
|
} JSGCRef;
|
|
|
|
/* stack of JSGCRef */
|
|
JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref);
|
|
JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref);
|
|
|
|
#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) {
|
|
#ifdef JS_PTR64
|
|
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 */
|
|
#else
|
|
if ((v & 1) == 0) return JS_TAG_INT;
|
|
if ((v & 3) == JS_TAG_PTR) return JS_TAG_PTR;
|
|
return (int)(v & 0x1F);
|
|
#endif
|
|
}
|
|
|
|
#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
|
|
============================================================ */
|
|
|
|
#ifdef JS_PTR64
|
|
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)
|
|
|
|
#else /* 32-bit: no short float, use boxed double */
|
|
|
|
static inline JSValue __JS_NewFloat64 (JSContext *ctx,
|
|
double d); /* forward decl */
|
|
static inline double JS_VALUE_GET_FLOAT64 (JSValue v);
|
|
|
|
#define JS_TAG_IS_FLOAT64(tag) (0)
|
|
#define JS_NAN JS_MKVAL (JS_TAG_NULL, 0)
|
|
|
|
#endif /* JS_PTR64 */
|
|
|
|
/* ============================================================
|
|
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; }
|
|
|
|
|
|
|
|
#ifdef JS_PTR64
|
|
static inline JS_BOOL
|
|
JS_IsShortFloat (JSValue v) {
|
|
return (v & 7) == JS_TAG_SHORT_FLOAT;
|
|
}
|
|
#endif
|
|
|
|
#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)
|
|
#define JS_UNINITIALIZED ((JSValue)JS_TAG_UNINITIALIZED)
|
|
|
|
/* flags for object properties - simplified model:
|
|
- No per-property writable/configurable (use stone() for immutability)
|
|
- Text keys are enumerable, object keys are not */
|
|
#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, VARREF */
|
|
#define JS_PROP_NORMAL (0 << 4)
|
|
#define JS_PROP_VARREF (2 << 4) /* used internally for closures */
|
|
|
|
#ifndef JS_DEFAULT_STACK_SIZE
|
|
#define JS_DEFAULT_STACK_SIZE (1024 * 1024)
|
|
#endif
|
|
|
|
/* Internal compile flags */
|
|
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) /* internal use */
|
|
|
|
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);
|
|
|
|
typedef struct JSMallocState {
|
|
size_t malloc_count;
|
|
size_t malloc_size;
|
|
size_t malloc_limit;
|
|
void *opaque; /* user opaque */
|
|
} JSMallocState;
|
|
|
|
typedef struct JSMallocFunctions {
|
|
void *(*js_malloc) (JSMallocState *s, size_t size);
|
|
void (*js_free) (JSMallocState *s, void *ptr);
|
|
void *(*js_realloc) (JSMallocState *s, void *ptr, size_t size);
|
|
size_t (*js_malloc_usable_size) (const void *ptr);
|
|
} JSMallocFunctions;
|
|
|
|
typedef struct JSGCObjectHeader JSGCObjectHeader;
|
|
|
|
JSValue JS_Stone (JSContext *ctx, JSValue this_val);
|
|
|
|
JSRuntime *JS_NewRuntime (void);
|
|
/* info lifetime must exceed that of rt */
|
|
void JS_SetRuntimeInfo (JSRuntime *rt, const char *info);
|
|
void JS_SetMemoryLimit (JSRuntime *rt, size_t limit);
|
|
/* use 0 to disable maximum stack size check */
|
|
void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size);
|
|
/* should be called when changing thread to update the stack top value
|
|
used to check stack overflow. */
|
|
void JS_UpdateStackTop (JSContext *ctx);
|
|
void JS_FreeRuntime (JSRuntime *rt);
|
|
void *JS_GetRuntimeOpaque (JSRuntime *rt);
|
|
void JS_SetRuntimeOpaque (JSRuntime *rt, void *opaque);
|
|
typedef void JS_MarkFunc (JSRuntime *rt, JSGCObjectHeader *gp);
|
|
/* JS_MarkValue is a no-op with copying GC (values are traced from roots) */
|
|
void JS_MarkValue (JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func);
|
|
JS_BOOL JS_IsLiveObject (JSRuntime *rt, JSValue obj);
|
|
|
|
JSContext *JS_NewContext (JSRuntime *rt);
|
|
void JS_FreeContext (JSContext *s);
|
|
JSContext *JS_DupContext (JSContext *ctx);
|
|
JSContext *JS_GetContext (JSRuntime *rt);
|
|
void *JS_GetContextOpaque (JSContext *ctx);
|
|
void JS_SetContextOpaque (JSContext *ctx, void *opaque);
|
|
JSRuntime *JS_GetRuntime (JSContext *ctx);
|
|
void JS_SetClassProto (JSContext *ctx, JSClassID class_id, JSValue obj);
|
|
JSValue JS_GetClassProto (JSContext *ctx, JSClassID class_id);
|
|
|
|
JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size);
|
|
|
|
typedef struct JSMemoryUsage {
|
|
int64_t malloc_size, malloc_limit, memory_used_size;
|
|
int64_t malloc_count;
|
|
int64_t memory_used_count;
|
|
int64_t str_count, str_size;
|
|
int64_t obj_count, obj_size;
|
|
int64_t prop_count, prop_size;
|
|
int64_t shape_count, shape_size;
|
|
int64_t js_func_count, js_func_size, js_func_code_size;
|
|
int64_t js_func_pc2line_count, js_func_pc2line_size;
|
|
int64_t c_func_count, array_count;
|
|
int64_t fast_array_count, fast_array_elements;
|
|
int64_t binary_object_count, binary_object_size;
|
|
} JSMemoryUsage;
|
|
|
|
void JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s);
|
|
void JS_DumpMemoryUsage (FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
|
|
|
|
typedef void JSClassFinalizer (JSRuntime *rt, JSValue val);
|
|
typedef void JSClassGCMark (JSRuntime *rt, JSValue val,
|
|
JS_MarkFunc *mark_func);
|
|
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;
|
|
JSClassGCMark *gc_mark;
|
|
/* if call != NULL, the object is a function */
|
|
JSClassCall *call;
|
|
} JSClassDef;
|
|
|
|
#define JS_INVALID_CLASS_ID 0
|
|
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);
|
|
|
|
extern JSClassID js_class_id_alloc;
|
|
|
|
/* ============================================================
|
|
Copying GC - No Reference Counting Needed
|
|
============================================================
|
|
With a copying GC, reference counting is not needed since all live
|
|
objects are discovered by tracing from roots. These macros make
|
|
existing DupValue/FreeValue calls into no-ops.
|
|
============================================================ */
|
|
#define JS_DupValue(ctx, v) (v)
|
|
#define JS_FreeValue(ctx, v) ((void)0)
|
|
#define JS_DupValueRT(rt, v) (v)
|
|
#define JS_FreeValueRT(rt, v) ((void)0)
|
|
|
|
/* value handling */
|
|
|
|
static inline JSValue
|
|
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_NewCatchOffset (JSContext *ctx, int32_t val) {
|
|
return JS_MKVAL (JS_TAG_CATCH_OFFSET, 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static inline JS_BOOL JS_IsUninitialized (JSValue v) {
|
|
return (JS_VALUE_GET_TAG (v) == JS_TAG_UNINITIALIZED);
|
|
}
|
|
|
|
/* Immediate 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;
|
|
}
|
|
|
|
static inline JS_BOOL JS_IsInteger (JSValue v) {
|
|
return JS_VALUE_GET_TAG (v) == JS_TAG_INT;
|
|
}
|
|
|
|
static inline JS_BOOL JS_IsObject (JSValue v) {
|
|
return JS_IsPtr (v);
|
|
}
|
|
|
|
JS_BOOL JS_IsArray(JSValue v);
|
|
JS_BOOL JS_IsRecord(JSValue v);
|
|
JS_BOOL JS_IsFunction(JSValue v);
|
|
JS_BOOL JS_IsCode(JSValue v);
|
|
JS_BOOL JS_IsForwarded(JSValue v);
|
|
JS_BOOL JS_IsFrame(JSValue v);
|
|
JS_BOOL JS_IsBlob(JSValue v);
|
|
JS_BOOL JS_IsText(JSValue v);
|
|
static JS_BOOL JS_IsStone(JSValue v);
|
|
|
|
// Fundamental
|
|
int JS_GetLength (JSContext *ctx, JSValue obj, int64_t *pres);
|
|
|
|
JSValue JS_Throw (JSContext *ctx, JSValue obj);
|
|
void JS_SetUncatchableException (JSContext *ctx, JS_BOOL flag);
|
|
JSValue JS_GetException (JSContext *ctx);
|
|
JS_BOOL JS_HasException (JSContext *ctx);
|
|
JS_BOOL JS_IsError (JSContext *ctx, JSValue val);
|
|
JSValue JS_NewError (JSContext *ctx);
|
|
JSValue __js_printf_like (2, 3)
|
|
JS_ThrowSyntaxError (JSContext *ctx, const char *fmt, ...);
|
|
JSValue __js_printf_like (2, 3)
|
|
JS_ThrowTypeError (JSContext *ctx, const char *fmt, ...);
|
|
JSValue __js_printf_like (2, 3)
|
|
JS_ThrowReferenceError (JSContext *ctx, const char *fmt, ...);
|
|
JSValue __js_printf_like (2, 3)
|
|
JS_ThrowRangeError (JSContext *ctx, const char *fmt, ...);
|
|
JSValue __js_printf_like (2, 3)
|
|
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
|
|
JSValue JS_ThrowOutOfMemory (JSContext *ctx);
|
|
|
|
JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2);
|
|
|
|
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);
|
|
/* return an exception if 'val' is a Number */
|
|
|
|
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));
|
|
}
|
|
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);
|
|
|
|
JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto, JSClassID class_id);
|
|
JSValue JS_NewObjectClass (JSContext *ctx, int class_id);
|
|
JSValue JS_NewObjectProto (JSContext *ctx, JSValue proto);
|
|
JSValue JS_NewObject (JSContext *ctx);
|
|
|
|
JSValue JS_NewArray (JSContext *ctx);
|
|
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
|
|
|
|
/* GC-safe push: takes pointer to array, updates it if array grows */
|
|
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
|
|
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);
|
|
|
|
/* Intrinsic array operations - signatures match internal functions */
|
|
JSValue JS_Array (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3);
|
|
JSValue JS_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);
|
|
|
|
/* Cell intrinsic functions - C API wrappers */
|
|
|
|
/* Core 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 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 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 functions */
|
|
JSValue JS_CellObject (JSContext *ctx, JSValue proto, JSValue props);
|
|
|
|
/* Format function */
|
|
JSValue JS_CellFormat (JSContext *ctx, JSValue text, JSValue collection, JSValue transformer);
|
|
|
|
/* Helper functions */
|
|
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values);
|
|
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);
|
|
|
|
JSValue JS_GetProperty (JSContext *ctx, JSValue this_obj, JSValue prop);
|
|
|
|
// For records
|
|
JSValue JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop);
|
|
int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSValue val);
|
|
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val);
|
|
JSValue JS_GetPrototype (JSContext *ctx, JSValue val);
|
|
|
|
// Must be an array
|
|
JSValue JS_GetPropertyNumber (JSContext *ctx, JSValue this_obj, int idx);
|
|
JSValue JS_SetPropertyNumber (JSContext *ctx, JSValue obj, int idx, JSValue val);
|
|
|
|
// Indexed property access (works with arrays and objects)
|
|
JSValue JS_GetPropertyUint32 (JSContext *ctx, JSValue this_obj, uint32_t idx);
|
|
int JS_SetPropertyUint32 (JSContext *ctx, JSValue this_obj, uint32_t idx, JSValue val);
|
|
int JS_SetPropertyInt64 (JSContext *ctx, JSValue this_obj, int64_t idx, JSValue val);
|
|
|
|
/* Get property keys as array of text */
|
|
JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj);
|
|
|
|
JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
|
|
|
|
/* Compile source code to bytecode without executing.
|
|
'input' must be zero terminated i.e. input[input_len] = '\0'.
|
|
Returns unlinked bytecode on success, JS_EXCEPTION on error. */
|
|
JSValue JS_Compile (JSContext *ctx, const char *input, size_t input_len,
|
|
const char *filename);
|
|
|
|
/* Link compiled bytecode with environment and execute.
|
|
env should be stoned record or null.
|
|
Variables resolve: env first, then global intrinsics. */
|
|
JSValue JS_Integrate (JSContext *ctx, JSValue bytecode, JSValue env);
|
|
void JS_SetOpaque (JSValue obj, void *opaque);
|
|
void *JS_GetOpaque (JSValue obj, JSClassID class_id);
|
|
void *JS_GetOpaque2 (JSContext *ctx, JSValue obj, JSClassID class_id);
|
|
void *JS_GetAnyOpaque (JSValue obj, JSClassID *class_id);
|
|
|
|
/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
|
|
JSValue JS_ParseJSON (JSContext *ctx, const char *buf, size_t buf_len,
|
|
const char *filename);
|
|
#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */
|
|
JSValue JS_ParseJSON2 (JSContext *ctx, const char *buf, size_t buf_len,
|
|
const char *filename, int flags);
|
|
JSValue JS_JSONStringify (JSContext *ctx, JSValue obj,
|
|
JSValue replacer, JSValue space0);
|
|
|
|
/* return != 0 if the JS code needs to be interrupted */
|
|
typedef int JSInterruptHandler (JSRuntime *rt, void *opaque);
|
|
void JS_SetInterruptHandler (JSContext *ctx, JSInterruptHandler *cb,
|
|
void *opaque);
|
|
/* select which debug info is stripped from the compiled code */
|
|
#define JS_STRIP_SOURCE (1 << 0) /* strip source code */
|
|
#define JS_STRIP_DEBUG \
|
|
(1 << 1) /* strip all debug info including source code */
|
|
void JS_SetStripInfo (JSRuntime *rt, int flags);
|
|
int JS_GetStripInfo (JSRuntime *rt);
|
|
|
|
/* Object Writer/Reader (currently only used to handle precompiled code) */
|
|
#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
|
|
#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */
|
|
#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
|
|
#define JS_WRITE_OBJ_REFERENCE \
|
|
(1 << 3) /* allow object references to \
|
|
encode arbitrary object \
|
|
graph */
|
|
uint8_t *JS_WriteObject (JSContext *ctx, size_t *psize, JSValue obj,
|
|
int flags);
|
|
uint8_t *JS_WriteObject2 (JSContext *ctx, size_t *psize, JSValue obj,
|
|
int flags, uint8_t ***psab_tab,
|
|
size_t *psab_tab_len);
|
|
|
|
#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
|
|
#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */
|
|
#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
|
|
#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */
|
|
JSValue JS_ReadObject (JSContext *ctx, const uint8_t *buf, size_t buf_len,
|
|
int flags);
|
|
/* Dump bytecode of a compiled function (for debugging) */
|
|
void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val);
|
|
|
|
/* 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 value output */
|
|
|
|
typedef struct {
|
|
JS_BOOL show_hidden : 8; /* only show enumerable properties */
|
|
JS_BOOL raw_dump : 8; /* avoid doing autoinit and avoid any malloc() call
|
|
(for internal use) */
|
|
uint32_t max_depth; /* recurse up to this depth, 0 = no limit */
|
|
uint32_t max_string_length; /* print no more than this length for
|
|
strings, 0 = no limit */
|
|
uint32_t max_item_count; /* print no more than this count for
|
|
arrays or objects, 0 = no limit */
|
|
} JSPrintValueOptions;
|
|
|
|
typedef void JSPrintValueWrite (void *opaque, const char *buf, size_t len);
|
|
|
|
void JS_PrintValueSetDefaultOptions (JSPrintValueOptions *options);
|
|
void JS_PrintValueRT (JSRuntime *rt, JSPrintValueWrite *write_func,
|
|
void *write_opaque, JSValue val,
|
|
const JSPrintValueOptions *options);
|
|
void JS_PrintValue (JSContext *ctx, JSPrintValueWrite *write_func,
|
|
void *write_opaque, JSValue val,
|
|
const JSPrintValueOptions *options);
|
|
|
|
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;
|
|
|
|
void js_debug_info (JSContext *js, JSValue fn, js_debug *dbg);
|
|
|
|
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);
|
|
|
|
uint32_t js_debugger_stack_depth (JSContext *ctx);
|
|
JSValue js_debugger_backtrace_fns (JSContext *ctx, const uint8_t *cur_pc);
|
|
JSValue js_debugger_closure_variables (JSContext *ctx, JSValue fn);
|
|
JSValue js_debugger_local_variables (JSContext *ctx, int stack_index);
|
|
void js_debugger_set_closure_variable (JSContext *js, JSValue fn,
|
|
JSValue var_name, JSValue val);
|
|
JSValue js_debugger_build_backtrace (JSContext *ctx, const uint8_t *cur_pc);
|
|
JSValue js_debugger_fn_info (JSContext *ctx, JSValue fn);
|
|
JSValue js_debugger_fn_bytecode (JSContext *js, JSValue fn);
|
|
void *js_debugger_val_address (JSContext *js, JSValue val);
|
|
|
|
/* Memory allocation functions (bump allocator) */
|
|
void *js_malloc (JSContext *ctx, size_t size);
|
|
void *js_mallocz (JSContext *ctx, size_t size);
|
|
void *js_realloc (JSContext *ctx, void *ptr, size_t size);
|
|
void js_free (JSContext *ctx, void *ptr);
|
|
char *js_strdup (JSContext *ctx, const char *str);
|
|
|
|
/* Runtime-level memory functions */
|
|
void *js_malloc_rt (size_t size);
|
|
void *js_mallocz_rt (size_t size);
|
|
void js_free_rt (void *ptr);
|
|
|
|
/* ============================================================================
|
|
Context-Neutral Module Format (CellModule)
|
|
============================================================================ */
|
|
|
|
/* Capture descriptor - what a nested function closes over */
|
|
typedef enum {
|
|
CAP_FROM_PARENT_LOCAL = 1, /* capture local from parent function */
|
|
CAP_FROM_PARENT_UPVALUE = 2 /* forward upvalue from parent's upvalues */
|
|
} CellCapKind;
|
|
|
|
typedef struct CellCapDesc {
|
|
uint8_t kind; /* CAP_FROM_PARENT_LOCAL or CAP_FROM_PARENT_UPVALUE */
|
|
uint16_t index; /* local index in parent, or upvalue index in parent */
|
|
} CellCapDesc;
|
|
|
|
/* External relocation - for integrate-time patching */
|
|
typedef enum {
|
|
EXT_GET = 1, /* OP_get_var -> OP_get_env_slot or OP_get_global_slot */
|
|
EXT_SET = 2 /* OP_put_var -> OP_set_env_slot or OP_set_global_slot */
|
|
} CellExtKind;
|
|
|
|
typedef struct CellExternalReloc {
|
|
uint32_t pc_offset; /* where operand lives in bytecode */
|
|
uint32_t name_sid; /* string id of the external name */
|
|
uint8_t kind; /* EXT_GET or EXT_SET */
|
|
} CellExternalReloc;
|
|
|
|
/* Constant types in cpool */
|
|
typedef enum {
|
|
CELL_CONST_NULL = 0,
|
|
CELL_CONST_INT = 1,
|
|
CELL_CONST_FLOAT = 2,
|
|
CELL_CONST_STRING = 3, /* string_sid into module string table */
|
|
CELL_CONST_UNIT = 4 /* unit_id for nested function */
|
|
} CellConstType;
|
|
|
|
typedef struct CellConst {
|
|
uint8_t type; /* CellConstType */
|
|
union {
|
|
int32_t i32;
|
|
double f64;
|
|
uint32_t string_sid;
|
|
uint32_t unit_id;
|
|
};
|
|
} CellConst;
|
|
|
|
/* Per-unit structure (context-neutral, flattened) */
|
|
typedef struct CellUnit {
|
|
/* Constant pool */
|
|
uint32_t const_count;
|
|
CellConst *constants;
|
|
|
|
/* Bytecode */
|
|
uint32_t bytecode_len;
|
|
uint8_t *bytecode;
|
|
|
|
/* Stack requirements */
|
|
uint16_t arg_count;
|
|
uint16_t var_count;
|
|
uint16_t stack_size;
|
|
|
|
/* Upvalue (capture) descriptors */
|
|
uint16_t upvalue_count;
|
|
CellCapDesc *upvalues;
|
|
|
|
/* External relocations */
|
|
uint32_t external_count;
|
|
CellExternalReloc *externals;
|
|
|
|
/* Debug info (optional) */
|
|
uint32_t pc2line_len;
|
|
uint8_t *pc2line;
|
|
uint32_t name_sid; /* unit name for stack traces */
|
|
} CellUnit;
|
|
|
|
/* Module-level structure (context-neutral) */
|
|
#define CELL_MODULE_MAGIC 0x4C4C4543 /* "CELL" */
|
|
#define CELL_MODULE_VERSION 1
|
|
|
|
typedef struct CellModule {
|
|
uint32_t magic; /* CELL_MODULE_MAGIC */
|
|
uint8_t version; /* CELL_MODULE_VERSION */
|
|
uint8_t flags;
|
|
|
|
/* Shared string table (module-global) */
|
|
uint32_t string_count;
|
|
uint32_t string_data_size;
|
|
uint8_t *string_data; /* concatenated UTF-8 strings */
|
|
uint32_t *string_offsets; /* offset for each string */
|
|
|
|
/* Unit table (entry 0 is the main/entry unit) */
|
|
uint32_t unit_count;
|
|
CellUnit *units;
|
|
|
|
/* Debug: source stored once at module level */
|
|
uint32_t source_len;
|
|
char *source;
|
|
} CellModule;
|
|
|
|
/* Free a CellModule and all its contents */
|
|
void cell_module_free (CellModule *mod);
|
|
|
|
/* Write a CellModule to a byte buffer.
|
|
Returns allocated buffer (caller must free with pjs_free), or NULL on error. */
|
|
uint8_t *cell_module_write (CellModule *mod, size_t *out_len);
|
|
|
|
/* Read a CellModule from a byte buffer.
|
|
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
|
|
CellModule *cell_module_read (const uint8_t *buf, size_t buf_len);
|
|
|
|
/* Convert compiled JSFunctionBytecode to CellModule.
|
|
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
|
|
CellModule *cell_module_from_bytecode (JSContext *ctx, JSFunctionBytecode *main_func);
|
|
|
|
/* Compile source code directly to CellModule.
|
|
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
|
|
CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_len, const char *filename);
|
|
|
|
/* Parse source code and return AST as cJSON tree.
|
|
Caller must call cJSON_Delete() on result. */
|
|
struct cJSON *JS_ASTTree (const char *source, size_t len, const char *filename);
|
|
|
|
/* Parse source code and return AST as JSON string.
|
|
Returns malloc'd JSON string (caller must free), or NULL on error. */
|
|
char *JS_AST (const char *source, size_t len, const char *filename);
|
|
|
|
/* Tokenize source code and return token array as JSON string.
|
|
Returns malloc'd JSON string (caller must free), or NULL on error. */
|
|
char *JS_Tokenize (const char *source, size_t len, const char *filename);
|
|
|
|
/* Compiled bytecode (context-free, serializable) */
|
|
typedef struct MachCode MachCode;
|
|
|
|
/* Compile AST cJSON tree to context-free MachCode. */
|
|
MachCode *JS_CompileMachTree(struct cJSON *ast);
|
|
|
|
/* Compile AST JSON string to context-free MachCode. */
|
|
MachCode *JS_CompileMach(const char *ast_json);
|
|
|
|
/* Free a compiled MachCode tree. */
|
|
void JS_FreeMachCode(MachCode *mc);
|
|
|
|
/* Load compiled MachCode into a JSContext, materializing JSValues. */
|
|
struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env);
|
|
|
|
/* Dump MACH bytecode to stdout. Takes AST cJSON tree. */
|
|
void JS_DumpMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
|
|
|
|
/* Dump MACH bytecode to stdout. Takes AST JSON string. */
|
|
void JS_DumpMach (JSContext *ctx, const char *ast_json, JSValue env);
|
|
|
|
/* Compile and execute MACH bytecode from AST cJSON tree. */
|
|
JSValue JS_RunMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
|
|
|
|
/* Compile and execute MACH bytecode from AST JSON string. */
|
|
JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
|
|
|
|
/* Compile AST cJSON tree to MCODE cJSON tree.
|
|
Caller must call cJSON_Delete() on result. */
|
|
struct cJSON *JS_McodeTree (struct cJSON *ast);
|
|
|
|
/* Compile AST JSON string to MCODE JSON string.
|
|
Returns malloc'd JSON string, or NULL on error. Caller must free. */
|
|
char *JS_Mcode (const char *ast_json);
|
|
|
|
/* Execute MCODE from cJSON tree. Takes ownership of root. */
|
|
JSValue JS_CallMcodeTree (JSContext *ctx, struct cJSON *root);
|
|
|
|
/* Parse and execute MCODE JSON string.
|
|
Returns result of execution, or JS_EXCEPTION on error. */
|
|
JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);
|
|
|
|
/* Get stack trace as cJSON array of frame objects.
|
|
Returns NULL if no register VM frame is active.
|
|
Caller must call cJSON_Delete() on the result. */
|
|
struct cJSON *JS_GetStack (JSContext *ctx);
|
|
|
|
/* Integrate a CellModule with an environment and execute.
|
|
Returns callable function value, or JS_EXCEPTION on error. */
|
|
JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);
|
|
|
|
#undef js_unlikely
|
|
#undef inline
|
|
|
|
#ifdef __cplusplus
|
|
} /* extern "C" { */
|
|
#endif
|
|
|
|
#endif /* QUICKJS_H */
|