MIRROR: javascript for 🐜's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 379 lines 13 kB view raw
1#include <math.h> 2#include <stdint.h> 3#include <stdlib.h> 4#include <string.h> 5#include <uv.h> 6 7#include "arena.h" 8#include "errors.h" 9#include "internal.h" 10#include "modules/buffer.h" 11 12// serialize / deserialize 13// 14// wire format: 15// [0xAB 0xCD] — magic header (2 bytes) 16// <value> — recursively encoded 17// 18// value tags (1 byte): 19// 'U' undefined 20// 'N' null 21// 'T' true 22// 'F' false 23// 'Z' NaN 24// 'D' double (8 bytes little-endian) 25// 'S' string (uint32 length LE + UTF-8 bytes) 26// 'A' array (uint32 count LE + <value>…) 27// 'O' object (uint32 count LE + (uint32 klen + key bytes + <value>)…) 28 29#define SER_MAGIC_0 0xAB 30#define SER_MAGIC_1 0xCD 31 32typedef struct { 33 uint8_t *data; 34 size_t len; 35 size_t cap; 36} enc_t; 37 38static bool enc_grow(enc_t *e, size_t need) { 39 if (e->len + need <= e->cap) return true; 40 size_t nc = e->cap ? e->cap * 2 : 64; 41 while (nc < e->len + need) nc *= 2; 42 uint8_t *p = realloc(e->data, nc); 43 if (!p) return false; 44 e->data = p; 45 e->cap = nc; 46 return true; 47} 48 49static bool enc_u8(enc_t *e, uint8_t b) { 50 if (!enc_grow(e, 1)) return false; 51 e->data[e->len++] = b; 52 return true; 53} 54 55static bool enc_u32(enc_t *e, uint32_t v) { 56 if (!enc_grow(e, 4)) return false; 57 e->data[e->len++] = (uint8_t)(v); 58 e->data[e->len++] = (uint8_t)(v >> 8); 59 e->data[e->len++] = (uint8_t)(v >> 16); 60 e->data[e->len++] = (uint8_t)(v >> 24); 61 return true; 62} 63 64static bool enc_bytes(enc_t *e, const void *src, size_t n) { 65 if (!enc_grow(e, n)) return false; 66 memcpy(e->data + e->len, src, n); 67 e->len += n; 68 return true; 69} 70 71static bool ser_val(ant_t *js, enc_t *e, ant_value_t val, int depth) { 72 if (depth > 64) return false; 73 uint8_t t = vtype(val); 74 75 if (t == T_UNDEF) return enc_u8(e, 'U'); 76 if (t == T_NULL) return enc_u8(e, 'N'); 77 if (t == T_BOOL) return enc_u8(e, (val == js_true) ? 'T' : 'F'); 78 79 if (t == T_NUM) { 80 double d = js_getnum(val); 81 if (isnan(d)) return enc_u8(e, 'Z'); 82 if (!enc_u8(e, 'D')) return false; 83 return enc_bytes(e, &d, 8); 84 } 85 86 if (t == T_STR) { 87 size_t slen; 88 const char *s = js_getstr(js, val, &slen); 89 if (!s) { s = ""; slen = 0; } 90 if (!enc_u8(e, 'S')) return false; 91 if (!enc_u32(e, (uint32_t)slen)) return false; 92 return enc_bytes(e, s, slen); 93 } 94 95 if (t == T_ARR) { 96 ant_offset_t n = js_arr_len(js, val); 97 if (!enc_u8(e, 'A')) return false; 98 if (!enc_u32(e, (uint32_t)n)) return false; 99 for (ant_offset_t i = 0; i < n; i++) { 100 if (!ser_val(js, e, js_arr_get(js, val, i), depth + 1)) return false; 101 } 102 return true; 103 } 104 105 if (t == T_OBJ) { 106 uint32_t count = 0; 107 ant_iter_t it = js_prop_iter_begin(js, val); 108 const char *k; size_t klen; ant_value_t v; 109 while (js_prop_iter_next(&it, &k, &klen, &v)) count++; 110 js_prop_iter_end(&it); 111 112 if (!enc_u8(e, 'O')) return false; 113 if (!enc_u32(e, count)) return false; 114 115 it = js_prop_iter_begin(js, val); 116 while (js_prop_iter_next(&it, &k, &klen, &v)) { 117 if (!enc_u32(e, (uint32_t)klen) || !enc_bytes(e, k, klen)) { 118 js_prop_iter_end(&it); 119 return false; 120 } 121 if (!ser_val(js, e, v, depth + 1)) { 122 js_prop_iter_end(&it); 123 return false; 124 } 125 } 126 js_prop_iter_end(&it); 127 return true; 128 } 129 130 return enc_u8(e, 'U'); 131} 132 133static ant_value_t v8_serialize(ant_t *js, ant_value_t *args, int nargs) { 134 if (nargs < 1) return js_mkerr(js, "serialize: value required"); 135 136 enc_t e = {0}; 137 if (!enc_u8(&e, SER_MAGIC_0) || !enc_u8(&e, SER_MAGIC_1)) goto oom; 138 if (!ser_val(js, &e, args[0], 0)) goto oom; 139 140 ArrayBufferData *ab = create_array_buffer_data(e.len); 141 if (!ab) goto oom; 142 memcpy(ab->data, e.data, e.len); 143 free(e.data); 144 return create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, e.len, "Buffer"); 145 146oom: 147 free(e.data); 148 return js_mkerr(js, "serialize: out of memory"); 149} 150 151typedef struct { 152 const uint8_t *data; 153 size_t len; 154 size_t pos; 155} dec_t; 156 157static bool dec_u8(dec_t *d, uint8_t *out) { 158 if (d->pos >= d->len) return false; 159 *out = d->data[d->pos++]; 160 return true; 161} 162 163static bool dec_u32(dec_t *d, uint32_t *out) { 164 if (d->pos + 4 > d->len) return false; 165 *out = (uint32_t)d->data[d->pos] 166 | ((uint32_t)d->data[d->pos+1] << 8) 167 | ((uint32_t)d->data[d->pos+2] << 16) 168 | ((uint32_t)d->data[d->pos+3] << 24); 169 d->pos += 4; 170 return true; 171} 172 173static ant_value_t des_val(ant_t *js, dec_t *d, int depth) { 174 if (depth > 64) return js_mkerr(js, "deserialize: maximum depth exceeded"); 175 176 uint8_t tag; 177 if (!dec_u8(d, &tag)) return js_mkundef(); 178 179 switch (tag) { 180 case 'U': return js_mkundef(); 181 case 'N': return js_mknull(); 182 case 'T': return js_true; 183 case 'F': return js_false; 184 case 'Z': return js_mknum((double)NAN); 185 case 'D': { 186 if (d->pos + 8 > d->len) return js_mkundef(); 187 double v; 188 memcpy(&v, d->data + d->pos, 8); 189 d->pos += 8; 190 return js_mknum(v); 191 } 192 case 'S': { 193 uint32_t n; 194 if (!dec_u32(d, &n) || d->pos + n > d->len) return js_mkundef(); 195 ant_value_t s = js_mkstr(js, (const char *)(d->data + d->pos), n); 196 d->pos += n; 197 return s; 198 } 199 case 'A': { 200 uint32_t n; 201 if (!dec_u32(d, &n)) return js_mkundef(); 202 ant_value_t arr = js_mkarr(js); 203 for (uint32_t i = 0; i < n; i++) { 204 ant_value_t elem = des_val(js, d, depth + 1); 205 if (is_err(elem)) return elem; 206 js_arr_push(js, arr, elem); 207 } 208 return arr; 209 } 210 case 'O': { 211 uint32_t n; 212 if (!dec_u32(d, &n)) return js_mkundef(); 213 ant_value_t obj = js_mkobj(js); 214 char kbuf[4096]; 215 for (uint32_t i = 0; i < n; i++) { 216 uint32_t klen; 217 if (!dec_u32(d, &klen) || klen >= sizeof(kbuf) || d->pos + klen > d->len) 218 break; 219 memcpy(kbuf, d->data + d->pos, klen); 220 kbuf[klen] = '\0'; 221 d->pos += klen; 222 ant_value_t v = des_val(js, d, depth + 1); 223 if (is_err(v)) return v; 224 js_set(js, obj, kbuf, v); 225 } 226 return obj; 227 } 228 default: 229 return js_mkundef(); 230 } 231} 232 233static ant_value_t v8_deserialize(ant_t *js, ant_value_t *args, int nargs) { 234 if (nargs < 1) return js_mkerr(js, "deserialize: Buffer required"); 235 236 TypedArrayData *ta = buffer_get_typedarray_data(args[0]); 237 if (!ta || !ta->buffer) return js_mkerr(js, "deserialize: expected a Buffer"); 238 239 const uint8_t *data = ta->buffer->data + ta->byte_offset; 240 size_t len = ta->byte_length; 241 242 if (len < 2 || data[0] != SER_MAGIC_0 || data[1] != SER_MAGIC_1) 243 return js_mkerr(js, "deserialize: invalid or incompatible buffer"); 244 245 dec_t d = { .data = data, .len = len, .pos = 2 }; 246 return des_val(js, &d, 0); 247} 248 249static ant_value_t v8_get_heap_statistics(ant_t *js, ant_value_t *args, int nargs) { 250 size_t rss = 0; 251 uv_resident_set_memory(&rss); 252 253 size_t arena_committed = js->obj_arena.committed; 254 size_t arena_reserved = js->obj_arena.reserved; 255 size_t arena_live_bytes = js->obj_arena.live_count * js->obj_arena.elem_size; 256 257 size_t closure_committed = js->closure_arena.committed; 258 size_t closure_reserved = js->closure_arena.reserved; 259 size_t closure_live = js->closure_arena.live_count * js->closure_arena.elem_size; 260 261 size_t pool_live = js->gc_pool_last_live; 262 size_t pool_alloc = js->gc_pool_alloc; 263 size_t pool_total = pool_live + pool_alloc; 264 265 size_t extra_alloc = js->alloc_bytes.closures + js->alloc_bytes.upvalues; 266 267 size_t used_heap = arena_live_bytes + closure_live + pool_total + extra_alloc; 268 size_t total_heap = arena_committed + closure_committed + pool_total + extra_alloc; 269 size_t heap_limit = arena_reserved + closure_reserved; 270 271 ant_value_t obj = js_mkobj(js); 272 js_set(js, obj, "total_heap_size", js_mknum((double)total_heap)); 273 js_set(js, obj, "total_heap_size_executable", js_mknum(0)); 274 js_set(js, obj, "total_physical_size", js_mknum((double)rss)); 275 js_set(js, obj, "total_available_size", js_mknum((double)(heap_limit > used_heap ? heap_limit - used_heap : 0))); 276 js_set(js, obj, "total_global_handles_size", js_mknum(0)); 277 js_set(js, obj, "used_global_handles_size", js_mknum(0)); 278 js_set(js, obj, "used_heap_size", js_mknum((double)used_heap)); 279 js_set(js, obj, "heap_size_limit", js_mknum((double)heap_limit)); 280 js_set(js, obj, "malloced_memory", js_mknum((double)extra_alloc)); 281 js_set(js, obj, "peak_malloced_memory", js_mknum((double)extra_alloc)); 282 js_set(js, obj, "does_zap_garbage", js_mknum(0)); 283 js_set(js, obj, "number_of_native_contexts", js_mknum(1)); 284 js_set(js, obj, "number_of_detached_contexts", js_mknum(0)); 285 js_set(js, obj, "total_heap_blinded_size", js_mknum(0)); 286 287 return obj; 288} 289 290static ant_value_t v8_get_heap_space_statistics(ant_t *js, ant_value_t *args, int nargs) { 291 ant_value_t nursery = js_mkobj(js); 292 size_t arena_committed = js->obj_arena.committed; 293 size_t arena_live_bytes = js->obj_arena.live_count * js->obj_arena.elem_size; 294 double arena_available = (double)(arena_committed / 2 > arena_live_bytes / 2 ? arena_committed / 2 - arena_live_bytes / 2 : 0); 295 296 js_set(js, nursery, "space_name", js_mkstr(js, "new_space", 9)); 297 js_set(js, nursery, "space_size", js_mknum((double)arena_committed / 2)); 298 js_set(js, nursery, "space_used_size", js_mknum((double)arena_live_bytes / 2)); 299 js_set(js, nursery, "space_available_size", js_mknum(arena_available)); 300 js_set(js, nursery, "physical_space_size", js_mknum((double)arena_committed / 2)); 301 302 ant_value_t oldspace = js_mkobj(js); 303 size_t old_live = js->old_live_count * js->obj_arena.elem_size; 304 double old_size = (double)(arena_committed / 2 > old_live ? arena_committed / 2 - old_live : 0); 305 306 js_set(js, oldspace, "space_name", js_mkstr(js, "old_space", 9)); 307 js_set(js, oldspace, "space_size", js_mknum((double)arena_committed / 2)); 308 js_set(js, oldspace, "space_used_size", js_mknum((double)old_live)); 309 js_set(js, oldspace, "space_available_size", js_mknum(old_size)); 310 js_set(js, oldspace, "physical_space_size", js_mknum((double)arena_committed / 2)); 311 312 ant_value_t arr = js_mkarr(js); 313 js_arr_push(js, arr, nursery); 314 js_arr_push(js, arr, oldspace); 315 316 return arr; 317} 318 319static ant_value_t v8_noop(ant_t *js, ant_value_t *args, int nargs) { 320 return js_mkundef(); 321} 322 323static ant_value_t v8_noop_false(ant_t *js, ant_value_t *args, int nargs) { 324 return js_false; 325} 326 327static ant_value_t v8_write_heap_snapshot(ant_t *js, ant_value_t *args, int nargs) { 328 return js_mkstr(js, "", 0); 329} 330 331static ant_value_t v8_get_heap_snapshot(ant_t *js, ant_value_t *args, int nargs) { 332 return js_mkundef(); 333} 334 335static ant_value_t v8_get_heap_code_statistics(ant_t *js, ant_value_t *args, int nargs) { 336 size_t closure_structs = js->closure_arena.live_count * js->closure_arena.elem_size; 337 size_t bytecode_alloc = js->alloc_bytes.closures; 338 339 ant_pool_stats_t rope_stats = js_pool_stats(&js->pool.rope); 340 ant_value_t obj = js_mkobj(js); 341 342 js_set(js, obj, "code_and_metadata_size", js_mknum((double)(closure_structs + bytecode_alloc))); 343 js_set(js, obj, "bytecode_and_metadata_size", js_mknum((double)bytecode_alloc)); 344 js_set(js, obj, "external_script_source_size", js_mknum((double)rope_stats.used)); 345 js_set(js, obj, "cpu_profiler_metadata_size", js_mknum(0)); 346 347 return obj; 348} 349 350static ant_value_t v8_cached_data_version_tag(ant_t *js, ant_value_t *args, int nargs) { 351 return js_mknum(0xA0A0A0); 352} 353 354ant_value_t v8_library(ant_t *js) { 355 ant_value_t lib = js_mkobj(js); 356 357 js_set(js, lib, "serialize", js_mkfun(v8_serialize)); 358 js_set(js, lib, "deserialize", js_mkfun(v8_deserialize)); 359 js_set(js, lib, "writeHeapSnapshot", js_mkfun(v8_write_heap_snapshot)); 360 js_set(js, lib, "getHeapSnapshot", js_mkfun(v8_get_heap_snapshot)); 361 js_set(js, lib, "getHeapStatistics", js_mkfun(v8_get_heap_statistics)); 362 js_set(js, lib, "getHeapSpaceStatistics", js_mkfun(v8_get_heap_space_statistics)); 363 js_set(js, lib, "getHeapCodeStatistics", js_mkfun(v8_get_heap_code_statistics)); 364 js_set(js, lib, "cachedDataVersionTag", js_mkfun(v8_cached_data_version_tag)); 365 js_set(js, lib, "setFlagsFromString", js_mkfun(v8_noop)); 366 js_set(js, lib, "stopCoverage", js_mkfun(v8_noop)); 367 js_set(js, lib, "takeCoverage", js_mkfun(v8_noop)); 368 js_set(js, lib, "promiseEvents", js_mkfun(v8_noop)); 369 370 ant_value_t snapshot = js_mkobj(js); 371 js_set(js, snapshot, "isBuildingSnapshot", js_mkfun(v8_noop_false)); 372 js_set(js, snapshot, "addSerializeCallback", js_mkfun(v8_noop)); 373 js_set(js, snapshot, "addDeserializeCallback", js_mkfun(v8_noop)); 374 375 js_set(js, lib, "startupSnapshot", snapshot); 376 js_set(js, lib, "constants", js_mkobj(js)); 377 378 return lib; 379}