#include <stdint.h>
#include <windows.h>
#define UNDEFINED 0x7FFF800000000001ULL
#define NULL_VAL 0x7FFF800000000002ULL
#define TRUE_VAL 0x7FFF000000000001ULL
#define FALSE_VAL 0x7FFF000000000000ULL
#define STRING_TAG 0x7FFC000000000000ULL
#define ARRAY_TAG 0x7FFB000000000000ULL
#define OBJECT_TAG 0x7FFA000000000000ULL
#define TAG_MASK 0xFFFF000000000000ULL
#define PTR_MASK 0x0000FFFFFFFFFFFFULL
typedef struct { uint32_t len; uint32_t hash; uint8_t data[]; } JsString;
typedef struct { uint32_t len; uint32_t capacity; uint64_t data[]; } JsArray;
typedef struct { uint64_t key; uint64_t value; } ObjectEntry;
typedef struct { uint32_t size; uint32_t capacity; ObjectEntry entries[]; } JsObject;
static double bits_to_val(uint64_t bits) {
double result;
__asm__ volatile ("movq %1, %0" : "=x"(result) : "r"(bits));
return result;
}
static uint64_t val_to_bits(double val) {
uint64_t bits;
__asm__ volatile ("movq %1, %0" : "=r"(bits) : "x"(val));
return bits;
}
static void* js_malloc(size_t size) {
return HeapAlloc(GetProcessHeap(), 0, size);
}
static void* js_realloc(void* ptr, size_t size) {
return HeapReAlloc(GetProcessHeap(), 0, ptr, size);
}
double js_string_new(const char* data, uint32_t len);
double js_string_from_static(const char* data);
static void write_str(const char* s) {
int len = 0;
while (s[len]) len++;
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), s, len, NULL, NULL);
}
static void write_buf(const char* buf, int len) {
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, len, NULL, NULL);
}
static int int_to_str(int num, char* buf) {
if (num == 0) {
buf[0] = '0';
return 1;
}
int neg = 0;
if (num < 0) {
neg = 1;
num = -num;
}
char tmp[32];
int i = 0;
while (num > 0) {
tmp[i++] = '0' + (num % 10);
num /= 10;
}
int len = 0;
if (neg) buf[len++] = '-';
while (i > 0) {
buf[len++] = tmp[--i];
}
return len;
}
double js_print(double val) {
char buf[256];
uint64_t bits = val_to_bits(val);
uint64_t tag = bits & TAG_MASK;
int len = 0;
if (bits == UNDEFINED) {
write_str("undefined\n");
}
else if (bits == NULL_VAL) {
write_str("null\n");
}
else if (bits == TRUE_VAL) {
write_str("true\n");
}
else if (bits == FALSE_VAL) {
write_str("false\n");
}
else if (tag == STRING_TAG) {
JsString* s = (JsString*)(bits & PTR_MASK);
if (s) {
write_buf((const char*)s->data, s->len);
write_str("\n");
} else {
write_str("null\n");
}
}
else if (tag == ARRAY_TAG) {
JsArray* arr = (JsArray*)(bits & PTR_MASK);
if (arr) {
buf[len++] = '[';
write_buf(buf, len);
len = 0;
for (uint32_t i = 0; i < arr->len; i++) {
if (i > 0) {
write_str(", ");
}
uint64_t elem_bits = arr->data[i];
if ((elem_bits & TAG_MASK) == STRING_TAG) {
JsString* s = (JsString*)(elem_bits & PTR_MASK);
if (s) {
write_buf((const char*)s->data, s->len);
} else {
write_str("null");
}
}
else if (elem_bits == UNDEFINED) {
write_str("undefined");
} else if (elem_bits == NULL_VAL) {
write_str("null");
} else if (elem_bits == TRUE_VAL) {
write_str("true");
} else if (elem_bits == FALSE_VAL) {
write_str("false");
} else {
double elem_val = bits_to_val(elem_bits);
int num = (int)elem_val;
len = int_to_str(num, buf);
write_buf(buf, len);
len = 0;
}
}
write_str("]\n");
} else {
write_str("null\n");
}
}
else if (tag == OBJECT_TAG) {
JsObject* obj = (JsObject*)(bits & PTR_MASK);
if (obj) {
write_str("{");
for (uint32_t i = 0; i < obj->size; i++) {
if (i > 0) {
write_str(", ");
}
write_str("\"key\": ");
uint64_t val_bits = obj->entries[i].value;
if (val_bits == UNDEFINED) {
write_str("undefined");
} else if (val_bits == NULL_VAL) {
write_str("null");
} else if (val_bits == TRUE_VAL) {
write_str("true");
} else if (val_bits == FALSE_VAL) {
write_str("false");
} else {
double val = bits_to_val(val_bits);
int num = (int)val;
len = int_to_str(num, buf);
write_buf(buf, len);
len = 0;
}
}
write_str("}\n");
} else {
write_str("null\n");
}
}
else {
int num = (int)val;
len = int_to_str(num, buf);
buf[len++] = '\n';
write_buf(buf, len);
}
return val;
}
double js_array_new(uint32_t capacity) {
if (capacity == 0) capacity = 8;
JsArray* arr = (JsArray*)js_malloc(sizeof(JsArray) + capacity * 8);
if (!arr) return bits_to_val(UNDEFINED);
arr->len = 0;
arr->capacity = capacity;
return bits_to_val(ARRAY_TAG | (uint64_t)arr);
}
double js_array_push(double arr_val, double value) {
uint64_t arr_bits = val_to_bits(arr_val);
if ((arr_bits & TAG_MASK) != ARRAY_TAG) return arr_val;
JsArray* arr = (JsArray*)(arr_bits & PTR_MASK);
if (!arr) return arr_val;
if (arr->len >= arr->capacity) {
uint32_t new_cap = arr->capacity * 2;
JsArray* new_arr = (JsArray*)js_realloc(arr, sizeof(JsArray) + new_cap * 8);
if (!new_arr) return arr_val;
new_arr->capacity = new_cap;
arr = new_arr;
}
arr->data[arr->len++] = val_to_bits(value);
return bits_to_val(ARRAY_TAG | (uint64_t)arr);
}
double js_array_get(double arr_val, double idx_val) {
uint64_t arr_bits = val_to_bits(arr_val);
if ((arr_bits & TAG_MASK) != ARRAY_TAG) return bits_to_val(UNDEFINED);
JsArray* arr = (JsArray*)(arr_bits & PTR_MASK);
int idx = (int)idx_val;
if (!arr || idx < 0 || (uint32_t)idx >= arr->len) return bits_to_val(UNDEFINED);
return bits_to_val(arr->data[idx]);
}
double js_array_set(double arr_val, double idx_val, double value) {
uint64_t arr_bits = val_to_bits(arr_val);
if ((arr_bits & TAG_MASK) != ARRAY_TAG) return value;
JsArray* arr = (JsArray*)(arr_bits & PTR_MASK);
uint32_t idx = (uint32_t)idx_val;
if (!arr) return value;
if (idx >= arr->len) {
while (arr->len <= idx) {
if (arr->len >= arr->capacity) {
uint32_t new_cap = arr->capacity * 2;
JsArray* new_arr = (JsArray*)js_realloc(arr, sizeof(JsArray) + new_cap * 8);
if (!new_arr) return value;
new_arr->capacity = new_cap;
arr = new_arr;
}
arr->data[arr->len++] = UNDEFINED;
}
}
arr->data[idx] = val_to_bits(value);
return value;
}
double js_object_new() {
JsObject* obj = (JsObject*)js_malloc(sizeof(JsObject) + 8 * sizeof(ObjectEntry));
if (!obj) return bits_to_val(UNDEFINED);
obj->size = 0;
obj->capacity = 8;
return bits_to_val(OBJECT_TAG | (uint64_t)obj);
}
double js_object_get(double obj_val, double key_val) {
uint64_t obj_bits = val_to_bits(obj_val);
uint64_t key_bits = val_to_bits(key_val);
if ((obj_bits & TAG_MASK) != OBJECT_TAG) return bits_to_val(UNDEFINED);
JsObject* obj = (JsObject*)(obj_bits & PTR_MASK);
if (!obj) return bits_to_val(UNDEFINED);
for (uint32_t i = 0; i < obj->size; i++) {
if (obj->entries[i].key == key_bits) {
return bits_to_val(obj->entries[i].value);
}
}
return bits_to_val(UNDEFINED);
}
double js_object_set(double obj_val, double key_val, double value_val) {
uint64_t obj_bits = val_to_bits(obj_val);
uint64_t key_bits = val_to_bits(key_val);
uint64_t value_bits = val_to_bits(value_val);
if ((obj_bits & TAG_MASK) != OBJECT_TAG) return value_val;
JsObject* obj = (JsObject*)(obj_bits & PTR_MASK);
if (!obj) return value_val;
for (uint32_t i = 0; i < obj->size; i++) {
if (obj->entries[i].key == key_bits) {
obj->entries[i].value = value_bits;
return value_val;
}
}
if (obj->size >= obj->capacity) {
uint32_t new_cap = obj->capacity * 2;
JsObject* new_obj = (JsObject*)js_realloc(obj, sizeof(JsObject) + new_cap * sizeof(ObjectEntry));
if (!new_obj) return value_val;
new_obj->capacity = new_cap;
obj = new_obj;
}
obj->entries[obj->size].key = key_bits;
obj->entries[obj->size].value = value_bits;
obj->size++;
return value_val;
}
double js_typeof(double val) {
uint64_t bits = val_to_bits(val);
uint64_t tag = bits & TAG_MASK;
static char* type_undefined = "undefined";
static char* type_object = "object";
static char* type_boolean = "boolean";
static char* type_string = "string";
static char* type_number = "number";
if (bits == UNDEFINED) return js_string_from_static(type_undefined);
if (bits == NULL_VAL) return js_string_from_static(type_object);
if (bits == TRUE_VAL || bits == FALSE_VAL) return js_string_from_static(type_boolean);
if (tag == STRING_TAG) return js_string_from_static(type_string);
if (tag == ARRAY_TAG) return js_string_from_static(type_object);
if (tag == OBJECT_TAG) return js_string_from_static(type_object);
return js_string_from_static(type_number);
}
double js_string_new(const char* data, uint32_t len) {
if (!data || len == 0) return bits_to_val(UNDEFINED);
JsString* str = (JsString*)js_malloc(sizeof(JsString) + len);
if (!str) return bits_to_val(UNDEFINED);
str->len = len;
str->hash = 0;
for (uint32_t i = 0; i < len; i++) {
str->data[i] = data[i];
str->hash = str->hash * 31 + data[i];
}
return bits_to_val(STRING_TAG | (uint64_t)str);
}
double js_string_from_static(const char* data) {
if (!data) return bits_to_val(UNDEFINED);
int len = 0;
while (data[len]) len++;
return js_string_new(data, (uint32_t)len);
}
double js_string_concat(double a_val, double b_val) {
uint64_t a_bits = val_to_bits(a_val);
uint64_t b_bits = val_to_bits(b_val);
if ((a_bits & TAG_MASK) != STRING_TAG || (b_bits & TAG_MASK) != STRING_TAG) {
return bits_to_val(UNDEFINED);
}
JsString* a_str = (JsString*)(a_bits & PTR_MASK);
JsString* b_str = (JsString*)(b_bits & PTR_MASK);
if (!a_str || !b_str) return bits_to_val(UNDEFINED);
uint32_t new_len = a_str->len + b_str->len;
JsString* result = (JsString*)js_malloc(sizeof(JsString) + new_len);
if (!result) return bits_to_val(UNDEFINED);
result->len = new_len;
result->hash = 0;
for (uint32_t i = 0; i < a_str->len; i++) {
result->data[i] = a_str->data[i];
result->hash = result->hash * 31 + a_str->data[i];
}
for (uint32_t i = 0; i < b_str->len; i++) {
result->data[a_str->len + i] = b_str->data[i];
result->hash = result->hash * 31 + b_str->data[i];
}
return bits_to_val(STRING_TAG | (uint64_t)result);
}
double js_add(double a, double b) {
uint64_t a_bits = val_to_bits(a);
uint64_t b_bits = val_to_bits(b);
if ((a_bits & TAG_MASK) == STRING_TAG && (b_bits & TAG_MASK) == STRING_TAG) {
return js_string_concat(a, b);
}
return a + b;
}
double js_sub(double a, double b) { return a - b; }
double js_mul(double a, double b) { return a * b; }
double js_div(double a, double b) { return b != 0 ? a / b : bits_to_val(UNDEFINED); }
double js_mod(double a, double b) { return b != 0 ? (double)((int64_t)a % (int64_t)b) : bits_to_val(UNDEFINED); }
double js_eq(double a, double b) { return bits_to_val(a == b ? TRUE_VAL : FALSE_VAL); }
double js_ne(double a, double b) { return bits_to_val(a != b ? TRUE_VAL : FALSE_VAL); }
double js_lt(double a, double b) { return bits_to_val(a < b ? TRUE_VAL : FALSE_VAL); }
double js_le(double a, double b) { return bits_to_val(a <= b ? TRUE_VAL : FALSE_VAL); }
double js_gt(double a, double b) { return bits_to_val(a > b ? TRUE_VAL : FALSE_VAL); }
double js_ge(double a, double b) { return bits_to_val(a >= b ? TRUE_VAL : FALSE_VAL); }