Created
October 12, 2025 13:56
-
-
Save piotrzarycki/a3713de4e63fd275216900a74c8521e2 to your computer and use it in GitHub Desktop.
typeof null // object netscape issue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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