]> git.kaiwu.me - quickjs.git/commitdiff
enabled nan-boxing for JSValue on common 64 bit targets (faster and reduced memory...
authorFabrice Bellard <fabrice@bellard.org>
Sat, 31 Oct 2026 13:10:26 +0000 (14:10 +0100)
committerFabrice Bellard <fabrice@bellard.org>
Sat, 31 Oct 2026 13:10:26 +0000 (14:10 +0100)
quickjs.c
quickjs.h

index 4c72563e648082a3be9bb8a651131f82b7b3424b..0d6c1475e462e9a5bdc639ddd6a9a59c860b6518 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
@@ -954,21 +954,25 @@ typedef struct JSJobEntry {
     JSValue argv[0];
 } JSJobEntry;
 
+typedef struct {
+    JSObject *getter; /* NULL if undefined */
+    JSObject *setter; /* NULL if undefined */
+} JSPropertyGetSet;
+
+typedef struct {
+    /* in order to use only 2 pointers, we compress the realm
+       and the init function pointer */
+    uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
+                               in the 2 low bits */
+    void *opaque;
+} JSPropertyAutoInit;
+
 typedef struct JSProperty {
     union {
         JSValue value;      /* JS_PROP_NORMAL */
-        struct {            /* JS_PROP_GETSET */
-            JSObject *getter; /* NULL if undefined */
-            JSObject *setter; /* NULL if undefined */
-        } getset;
+        JSPropertyGetSet *getset;            /* JS_PROP_GETSET */
         JSVarRef *var_ref;  /* JS_PROP_VARREF */
-        struct {            /* JS_PROP_AUTOINIT */
-            /* in order to use only 2 pointers, we compress the realm
-               and the init function pointer */
-            uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
-                                       in the 2 low bits */
-            void *opaque;
-        } init;
+        JSPropertyAutoInit *init;            /* JS_PROP_AUTOINIT */
     } u;
 } JSProperty;
 
@@ -6188,17 +6192,18 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
 
 static JSContext *js_autoinit_get_realm(JSProperty *pr)
 {
-    return (JSContext *)(pr->u.init.realm_and_id & ~3);
+    return (JSContext *)(pr->u.init->realm_and_id & ~3);
 }
 
 static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
 {
-    return pr->u.init.realm_and_id & 3;
+    return pr->u.init->realm_and_id & 3;
 }
 
 static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
 {
     JS_FreeContext(js_autoinit_get_realm(pr));
+    js_free_rt(rt, pr->u.init);
 }
 
 static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
@@ -6211,10 +6216,12 @@ static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
 {
     if (unlikely(prop_flags & JS_PROP_TMASK)) {
         if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
-            if (pr->u.getset.getter)
-                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
-            if (pr->u.getset.setter)
-                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+            JSPropertyGetSet *getset = pr->u.getset;
+            if (getset->getter)
+                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->getter));
+            if (getset->setter)
+                JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->setter));
+            js_free_rt(rt, getset);
         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
             free_var_ref(rt, pr->u.var_ref);
         } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
@@ -6697,10 +6704,10 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
                 if (prs->atom != JS_ATOM_NULL) {
                     if (prs->flags & JS_PROP_TMASK) {
                         if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
-                            if (pr->u.getset.getter)
-                                mark_func(rt, &pr->u.getset.getter->header);
-                            if (pr->u.getset.setter)
-                                mark_func(rt, &pr->u.getset.setter->header);
+                            if (pr->u.getset->getter)
+                                mark_func(rt, &pr->u.getset->getter->header);
+                            if (pr->u.getset->setter)
+                                mark_func(rt, &pr->u.getset->setter->header);
                         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
                             /* Note: the tag does not matter
                                provided it is a GC object */
@@ -8306,7 +8313,7 @@ static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
     id = js_autoinit_get_id(pr);
     func = js_autoinit_func_table[id];
     /* 'func' shall not modify the object properties 'pr' */
-    val = func(realm, p, prop, pr->u.init.opaque);
+    val = func(realm, p, prop, pr->u.init->opaque);
     if (JS_IsException(val))
         return -1;
     if (id == JS_AUTOINIT_ID_MODULE_NS &&
@@ -8403,10 +8410,10 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
             /* found */
             if (unlikely(prs->flags & JS_PROP_TMASK)) {
                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
-                    if (unlikely(!pr->u.getset.getter)) {
+                    if (unlikely(!pr->u.getset->getter)) {
                         return JS_UNDEFINED;
                     } else {
-                        JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
+                        JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->getter);
                         /* Note: the field could be removed in the getter */
                         func = JS_DupValue(ctx, func);
                         return JS_CallFree(ctx, func, this_obj, 0, NULL);
@@ -8975,10 +8982,10 @@ retry:
             if (unlikely(prs->flags & JS_PROP_TMASK)) {
                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
                     desc->flags |= JS_PROP_GETSET;
-                    if (pr->u.getset.getter)
-                        desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
-                    if (pr->u.getset.setter)
-                        desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+                    if (pr->u.getset->getter)
+                        desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->getter));
+                    if (pr->u.getset->setter)
+                        desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->setter));
                 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
                     JSValue val = *pr->u.var_ref->pvalue;
                     if (unlikely(JS_IsUninitialized(val))) {
@@ -9860,7 +9867,7 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
             assert(prop == JS_ATOM_length);
             return set_array_length(ctx, p, val, flags);
         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
-            return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
+            return call_setter(ctx, pr->u.getset->setter, this_obj, val, flags);
         } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
             /* XXX: already use var_ref->is_const. Cannot simplify use the
                writable flag for JS_CLASS_MODULE_NS. */
@@ -9984,7 +9991,7 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
         prs = find_own_property(&pr, p1, prop);
         if (prs) {
             if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
-                return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
+                return call_setter(ctx, pr->u.getset->setter, this_obj, val, flags);
             } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
                 /* Instantiate property and retry (potentially useless) */
                 if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) {
@@ -10282,7 +10289,8 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
     int ret, prop_flags;
     JSVarRef *var_ref;
     JSObject *delete_obj;
-    
+    JSPropertyGetSet *getset = NULL;
+
     /* add a new property or modify an existing exotic one */
     if (p->is_exotic) {
         if (p->class_id == JS_CLASS_ARRAY) {
@@ -10382,21 +10390,31 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
             prop_flags |= JS_PROP_VARREF;
         }
     }
+
+    if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+        /* allocate before to avoid problems in case of memory error */
+        getset = js_malloc(ctx, sizeof(JSPropertyGetSet));
+        if (!getset)
+            return -1;
+    }
+    
     pr = add_property(ctx, p, prop, prop_flags);
     if (unlikely(!pr)) {
         if (var_ref)
             free_var_ref(ctx->rt, var_ref);
+        js_free(ctx, getset);
         return -1;
     }
     if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
-        pr->u.getset.getter = NULL;
+        pr->u.getset = getset; /* fail safe if malloc error */
+        getset->getter = NULL;
         if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
-            pr->u.getset.getter =
+            getset->getter =
                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
         }
-        pr->u.getset.setter = NULL;
+        getset->setter = NULL;
         if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
-            pr->u.getset.setter =
+            getset->setter =
                 JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
         }
     } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) {
@@ -10558,6 +10576,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
                 }
 
                 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
+                    JSPropertyGetSet *getset;
                     if (js_shape_prepare_update(ctx, p, &prs))
                         return -1;
                     /* convert to getset */
@@ -10570,36 +10589,40 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
                     } else {
                         JS_FreeValue(ctx, pr->u.value);
                     }
+                    getset = js_malloc(ctx, sizeof(JSPropertyGetSet));
+                    if (!getset)
+                        return -1;
                     prs->flags = (prs->flags &
                                   (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
                         JS_PROP_GETSET;
-                    pr->u.getset.getter = NULL;
-                    pr->u.getset.setter = NULL;
+                    pr->u.getset = getset;
+                    getset->getter = NULL;
+                    getset->setter = NULL;
                 } else {
                     if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
                         if ((flags & JS_PROP_HAS_GET) &&
-                            new_getter != pr->u.getset.getter) {
+                            new_getter != pr->u.getset->getter) {
                             goto not_configurable;
                         }
                         if ((flags & JS_PROP_HAS_SET) &&
-                            new_setter != pr->u.getset.setter) {
+                            new_setter != pr->u.getset->setter) {
                             goto not_configurable;
                         }
                     }
                 }
                 if (flags & JS_PROP_HAS_GET) {
-                    if (pr->u.getset.getter)
-                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+                    if (pr->u.getset->getter)
+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->getter));
                     if (new_getter)
                         JS_DupValue(ctx, getter);
-                    pr->u.getset.getter = new_getter;
+                    pr->u.getset->getter = new_getter;
                 }
                 if (flags & JS_PROP_HAS_SET) {
-                    if (pr->u.getset.setter)
-                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+                    if (pr->u.getset->setter)
+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->setter));
                     if (new_setter)
                         JS_DupValue(ctx, setter);
-                    pr->u.getset.setter = new_setter;
+                    pr->u.getset->setter = new_setter;
                 }
             } else {
                 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
@@ -10617,10 +10640,11 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
                             free_var_ref(ctx->rt, var_ref);
                         return -1;
                     }
-                    if (pr->u.getset.getter)
-                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
-                    if (pr->u.getset.setter)
-                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+                    if (pr->u.getset->getter)
+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->getter));
+                    if (pr->u.getset->setter)
+                        JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->setter));
+                    js_free(ctx, pr->u.getset);
                     if (var_ref) {
                         prs->flags = (prs->flags & ~JS_PROP_TMASK) |
                             JS_PROP_VARREF | JS_PROP_WRITABLE;
@@ -10801,7 +10825,8 @@ static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
 {
     JSObject *p;
     JSProperty *pr;
-
+    JSPropertyAutoInit *init;
+    
     if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
         return FALSE;
 
@@ -10813,15 +10838,19 @@ static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
         return FALSE;
     }
 
+    init = js_malloc(ctx, sizeof(*init));
+    if (!init)
+        return -1;
     /* Specialized CreateProperty */
     pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
     if (unlikely(!pr))
         return -1;
-    pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
-    assert((pr->u.init.realm_and_id & 3) == 0);
+    pr->u.init = init;
+    init->realm_and_id = (uintptr_t)JS_DupContext(ctx);
+    assert((init->realm_and_id & 3) == 0);
     assert(id <= 3);
-    pr->u.init.realm_and_id |= id;
-    pr->u.init.opaque = opaque;
+    init->realm_and_id |= id;
+    init->opaque = opaque;
     return TRUE;
 }
 
@@ -14396,11 +14425,11 @@ static void js_print_object(JSPrintValueState *s, JSObject *p)
                     if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
                         if (s->options.raw_dump) {
                             js_printf(s, "[Getter %p Setter %p]",
-                                    pr->u.getset.getter, pr->u.getset.setter);
+                                    pr->u.getset->getter, pr->u.getset->setter);
                         } else {
-                            if (pr->u.getset.getter && pr->u.getset.setter) {
+                            if (pr->u.getset->getter && pr->u.getset->setter) {
                                 js_printf(s, "[Getter/Setter]");
-                            } else if (pr->u.getset.setter) {
+                            } else if (pr->u.getset->setter) {
                                 js_printf(s, "[Setter]");
                             } else {
                                 js_printf(s, "[Getter]");
@@ -14417,7 +14446,7 @@ static void js_print_object(JSPrintValueState *s, JSObject *p)
                             js_printf(s, "[autoinit %p %d %p]",
                                     (void *)js_autoinit_get_realm(pr),
                                     js_autoinit_get_id(pr),
-                                    (void *)pr->u.init.opaque);
+                                    (void *)pr->u.init->opaque);
                         } else {
                             /* XXX: could autoinit but need to restart
                                the iteration */
@@ -16458,13 +16487,18 @@ static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
 {
     JSValue val, *tab;
     JSProperty props[3];
+    JSPropertyGetSet *getset;
     JSObject *p;
     int i;
 
+    getset = js_malloc(ctx, sizeof(*getset));
+    if (!getset)
+        return JS_EXCEPTION;
     props[0].u.value = JS_NewInt32(ctx, argc); /* length */
     props[1].u.value = JS_DupValue(ctx, ctx->array_proto_values); /* Symbol.iterator */
-    props[2].u.getset.getter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */
-    props[2].u.getset.setter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */
+    getset->getter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */
+    getset->setter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */
+    props[2].u.getset = getset;
     
     val = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->arguments_shape),
                                 JS_CLASS_ARGUMENTS, props);
@@ -51353,9 +51387,9 @@ static BOOL check_regexp_getter(JSContext *ctx,
         return FALSE;
     if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET)
         return FALSE;
-    if (!pr->u.getset.getter)
+    if (!pr->u.getset->getter)
         return FALSE;
-    return JS_IsCFunction(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter),
+    return JS_IsCFunction(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset->getter),
                           func, magic);
 }
 
index 9dbb28ba06b834122e4b3cf53df8dd581a396074..0708e602aeb942553f58896ebcf906abd6d0af19 100644 (file)
--- a/quickjs.h
+++ b/quickjs.h
@@ -60,8 +60,13 @@ typedef uint32_t JSAtom;
 #define JS_PTR64_DEF(a)
 #endif
 
-#ifndef JS_PTR64
+#if !defined(JS_PTR64)
 #define JS_NAN_BOXING
+#define JS_NAN_BOX_TAG_SHIFT 32
+#elif defined(__x86_64__) || defined(__aarch64__) || defined(__riscv)
+/* we assume pointers fit on 47 bits */
+#define JS_NAN_BOXING
+#define JS_NAN_BOX_TAG_SHIFT 47
 #endif
 
 #if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX)
@@ -70,15 +75,20 @@ typedef uint32_t JSAtom;
 #define JS_LIMB_BITS 32
 #endif
 
+#if defined(JS_NAN_BOXING)
+#define JS_SHORT_BIG_INT_BITS 32
+#else
 #define JS_SHORT_BIG_INT_BITS JS_LIMB_BITS
-    
+#endif
+
 enum {
     /* all tags with a reference count are negative */
-    JS_TAG_FIRST       = -9, /* first negative tag */
-    JS_TAG_BIG_INT     = -9,
-    JS_TAG_SYMBOL      = -8,
-    JS_TAG_STRING      = -7,
-    JS_TAG_STRING_ROPE = -6,
+    JS_TAG_FIRST       = -8, /* first negative tag */
+    JS_TAG_UNUSED0     = -8, /* currently unused */
+    JS_TAG_BIG_INT     = -7,
+    JS_TAG_SYMBOL      = -6,
+    JS_TAG_STRING      = -5,
+    JS_TAG_STRING_ROPE = -4,
     JS_TAG_MODULE      = -3, /* used internally */
     JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */
     JS_TAG_OBJECT      = -1,
@@ -148,16 +158,16 @@ typedef uint64_t JSValue;
 
 #define JSValueConst JSValue
 
-#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
+#define JS_VALUE_GET_TAG(v) (int)((int64_t)(v) >> JS_NAN_BOX_TAG_SHIFT)
 #define JS_VALUE_GET_INT(v) (int)(v)
 #define JS_VALUE_GET_BOOL(v) (int)(v)
 #define JS_VALUE_GET_SHORT_BIG_INT(v) (int)(v)
-#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
+#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)((v) & (((uint64_t)1 << JS_NAN_BOX_TAG_SHIFT) - 1))
 
-#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
-#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr))
+#define JS_MKVAL(tag, val) (((uint64_t)(tag) << JS_NAN_BOX_TAG_SHIFT) | (uint32_t)(val))
+#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << JS_NAN_BOX_TAG_SHIFT) | (uintptr_t)(ptr))
 
-#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */
+#define JS_FLOAT64_ADDEND (0x7ff8000000000000 + ((uint64_t)(-JS_TAG_FIRST) << JS_NAN_BOX_TAG_SHIFT))
 
 static inline double JS_VALUE_GET_FLOAT64(JSValue v)
 {
@@ -166,11 +176,11 @@ static inline double JS_VALUE_GET_FLOAT64(JSValue v)
         double d;
     } u;
     u.v = v;
-    u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32;
+    u.v += JS_FLOAT64_ADDEND;
     return u.d;
 }
 
-#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32))
+#define JS_NAN (0xfff8000000000000 - JS_FLOAT64_ADDEND)
 
 static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
 {
@@ -184,7 +194,7 @@ static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
     if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000))
         v = JS_NAN;
     else
-        v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32);
+        v = u.u64 - JS_FLOAT64_ADDEND;
     return v;
 }
 
@@ -205,7 +215,7 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
 {
     uint32_t tag;
     tag = JS_VALUE_GET_TAG(v);
-    return tag == (JS_NAN >> 32);
+    return tag == (uint32_t)((int64_t)JS_NAN >> JS_NAN_BOX_TAG_SHIFT);
 }
 
 static inline JSValue __JS_NewShortBigInt(JSContext *ctx, int32_t d)