Skip to content

Instantly share code, notes, and snippets.

@piotrzarycki
Created October 12, 2025 13:56
Show Gist options
  • Select an option

  • Save piotrzarycki/a3713de4e63fd275216900a74c8521e2 to your computer and use it in GitHub Desktop.

Select an option

Save piotrzarycki/a3713de4e63fd275216900a74c8521e2 to your computer and use it in GitHub Desktop.
typeof null // object netscape issue
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef long prword;
typedef prword jsval;
typedef unsigned long pruword;
typedef unsigned short uint16;
typedef uint16 jschar;
/*
* JSVAL TAG ENCODING (lowest 3 bits)
* ===================================
* 000 (0x0) - OBJECT : untagged pointer to JSObject
* 001 (0x1) - INT : tagged 31-bit signed integer
* 010 (0x2) - DOUBLE : tagged pointer to double
* 100 (0x4) - STRING : tagged pointer to JSString
* 110 (0x6) - BOOLEAN : tagged boolean value
* 111 (0x8) - VOID : represents undefined/void
*/
#define JSVAL_OBJECT 0x0 // Tag: 000
#define JSVAL_INT 0x1 // Tag: 001
#define JSVAL_DOUBLE 0x2 // Tag: 010
#define JSVAL_STRING 0x4 // Tag: 100
#define JSVAL_BOOLEAN 0x6 // Tag: 110
#define JSVAL_VOID 0x8 // Tag: 111 (implementation uses 0x8)
#define JSVAL_TAGBITS 3 // 3 bits for tag
#define JSVAL_TAGMASK PR_BITMASK(JSVAL_TAGBITS) // 0b111 = 0x7
// Extract tag: value & 0x7 gives lowest 3 bits
#define JSVAL_TAG(v) ((v) & JSVAL_TAGMASK)
// Clear tag: value & ~0x7 removes lowest 3 bits
#define JSVAL_CLRTAG(v) ((v) & ~(jsval)JSVAL_TAGMASK)
#define PR_BIT(n) ((pruword)1 << (n))
#define PR_BITMASK(n) (PR_BIT(n) - 1)
// Remove tag to get pointer
#define JSVAL_TO_GCTHING(v) ((void *)JSVAL_CLRTAG(v))
#define JSVAL_TO_OBJECT(v) ((JSObject *)JSVAL_TO_GCTHING(v))
#define JSVAL_TO_STRING(v) ((JSString *)JSVAL_TO_GCTHING(v))
// Add tags to create jsval
#define OBJECT_TO_JSVAL(obj) ((jsval)(obj)) // ptr | 0x0 (no-op)
#define STRING_TO_JSVAL(str) ((jsval)(str) | JSVAL_STRING) // ptr | 0x4
#define BOOLEAN_TO_JSVAL(b) ((jsval)((b) << JSVAL_TAGBITS) | JSVAL_BOOLEAN) // (b << 3) | 0x6
// Check tag value
#define JSVAL_IS_VOID(v) (JSVAL_TAG(v) == JSVAL_VOID) // tag == 0x8
#define JSVAL_IS_OBJECT(v) (JSVAL_TAG(v) == JSVAL_OBJECT) // tag == 0x0
#define JSVAL_IS_INT(v) (JSVAL_TAG(v) == JSVAL_INT) // tag == 0x1
#define JSVAL_IS_DOUBLE(v) (JSVAL_TAG(v) == JSVAL_DOUBLE) // tag == 0x2
#define JSVAL_IS_STRING(v) (JSVAL_TAG(v) == JSVAL_STRING) // tag == 0x4
#define JSVAL_IS_BOOLEAN(v) (JSVAL_TAG(v) == JSVAL_BOOLEAN) // tag == 0x6
#define JSVAL_IS_NUMBER(v) (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v))
#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL)
#define JSVAL_NULL OBJECT_TO_JSVAL(0) // 0x0 with tag 0x0
typedef enum JSType {
JSTYPE_VOID,
JSTYPE_OBJECT,
JSTYPE_FUNCTION,
JSTYPE_STRING,
JSTYPE_NUMBER,
JSTYPE_BOOLEAN,
JSTYPE_NULL,
} JSType;
typedef struct JSString {
size_t length;
jschar *chars;
} JSString;
typedef struct JSClass {
void (*call)(void);
} JSClass;
typedef struct JSObjectMap {
struct JSClass *classp;
} JSObjectMap;
typedef struct JSObject {
struct JSObjectMap *map;
} JSObject;
JSType JS_TypeOfValue(jsval v)
{
JSType type;
JSObject *obj;
JSClass *clasp;
if(JSVAL_IS_NULL(v)) {
type = JSTYPE_NULL;
} else if (JSVAL_IS_VOID(v)) {
type = JSTYPE_VOID;
} else if (JSVAL_IS_OBJECT(v)) {
obj = JSVAL_TO_OBJECT(v);
if (obj && obj->map && obj->map->classp && obj->map->classp->call != NULL) {
type = JSTYPE_FUNCTION;
} else {
type = JSTYPE_OBJECT;
}
} else if (JSVAL_IS_NUMBER(v)) {
type = JSTYPE_NUMBER;
} else if (JSVAL_IS_STRING(v)) {
type = JSTYPE_STRING;
} else if (JSVAL_IS_BOOLEAN(v)) {
type = JSTYPE_BOOLEAN;
} else {
type = JSTYPE_VOID;
}
return type;
}
const char* JSTypeToString(JSType type) {
switch(type) {
case JSTYPE_VOID: return "void";
case JSTYPE_OBJECT: return "object";
case JSTYPE_FUNCTION: return "function";
case JSTYPE_STRING: return "string";
case JSTYPE_NUMBER: return "number";
case JSTYPE_BOOLEAN: return "boolean";
case JSTYPE_NULL: return "null";
}
}
void printValueWithTag(const char* label, jsval value) {
unsigned long tag = JSVAL_TAG(value);
printf("%s:\n", label);
printf(" Full value: 0x%016lx\n", value);
printf(" Tag (bits 0-2): 0x%lx = %03lb\n", tag, tag);
printf(" Untagged: 0x%016lx\n\n", JSVAL_CLRTAG(value));
}
int main() {
printf("=== JSVAL Tag Demonstration ===\n\n");
// STRING: pointer | 0x4 (tag 100)
JSString* str = malloc(sizeof(JSString));
str->chars = malloc(5 * sizeof(jschar));
str->chars[0] = 't';
str->chars[1] = 'e';
str->chars[2] = 's';
str->chars[3] = 't';
str->chars[4] = '\0';
str->length = 4;
jsval jstring = STRING_TO_JSVAL(str); // ptr | 0x4
printValueWithTag("STRING", jstring);
// OBJECT: pointer | 0x0 (tag 000)
JSObject* obj = malloc(sizeof(JSObject));
JSObjectMap* map = malloc(sizeof(JSObjectMap));
map->classp = NULL;
obj->map = map;
jsval objectVal = OBJECT_TO_JSVAL(obj); // ptr | 0x0
printValueWithTag("OBJECT", objectVal);
// BOOLEAN: (value << 3) | 0x6 (tag 110)
jsval boolTrue = BOOLEAN_TO_JSVAL(1); // (1 << 3) | 0x6 = 0x0E
jsval boolFalse = BOOLEAN_TO_JSVAL(0); // (0 << 3) | 0x6 = 0x06
printValueWithTag("BOOLEAN (true)", boolTrue);
printValueWithTag("BOOLEAN (false)", boolFalse);
// NULL: 0x0 with tag 0x0 (tag 000)
jsval nullVal = JSVAL_NULL; // 0x0
printValueWithTag("NULL", nullVal);
// Tag verification
printf("=== Tag Verification ===\n");
printf("STRING tag: 0x%lx (expected 0x4)\n", JSVAL_TAG(jstring));
printf("OBJECT tag: 0x%lx (expected 0x0)\n", JSVAL_TAG(objectVal));
printf("BOOLEAN tag: 0x%lx (expected 0x6)\n", JSVAL_TAG(boolTrue));
printf("NULL tag: 0x%lx (expected 0x0)\n", JSVAL_TAG(nullVal));
// Type detection
printf("\n=== Type Detection ===\n");
printf("String: %s\n", JSTypeToString(JS_TypeOfValue(jstring)));
printf("Object: %s\n", JSTypeToString(JS_TypeOfValue(objectVal)));
printf("Boolean: %s\n", JSTypeToString(JS_TypeOfValue(boolTrue)));
printf("Null: %s\n", JSTypeToString(JS_TypeOfValue(nullVal)));
// Cleanup
free(str->chars);
free(str);
free(map);
free(obj);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment