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 349 lines 11 kB view raw
1#include <string.h> 2#include <stdlib.h> 3 4#include <uthash.h> 5 6#include "ant.h" 7#include "errors.h" 8#include "runtime.h" 9#include "internal.h" 10#include "descriptors.h" 11 12#include "modules/date.h" 13#include "modules/buffer.h" 14#include "modules/collections.h" 15#include "modules/domexception.h" 16#include "modules/blob.h" 17#include "modules/structured-clone.h" 18 19typedef struct sc_entry { 20 ant_value_t key; 21 ant_value_t value; 22 UT_hash_handle hh; 23} sc_entry_t; 24 25static void sc_add(sc_entry_t **table, ant_value_t key, ant_value_t value) { 26 sc_entry_t *e = calloc(1, sizeof(sc_entry_t)); 27 if (!e) return; 28 e->key = key; 29 e->value = value; 30 HASH_ADD(hh, *table, key, sizeof(ant_value_t), e); 31} 32 33static ant_value_t sc_lookup(sc_entry_t **table, ant_value_t key) { 34 sc_entry_t *e; 35 HASH_FIND(hh, *table, &key, sizeof(ant_value_t), e); 36 return e ? e->value : js_mkundef(); 37} 38 39static bool sc_has(sc_entry_t **table, ant_value_t key) { 40 sc_entry_t *e; 41 HASH_FIND(hh, *table, &key, sizeof(ant_value_t), e); 42 return e != NULL; 43} 44 45static void sc_free(sc_entry_t **table) { 46 sc_entry_t *e, *tmp; 47 HASH_ITER(hh, *table, e, tmp) { 48 HASH_DEL(*table, e); 49 free(e); 50 } 51} 52 53static ant_value_t sc_clone_typed_array( 54 ant_t *js, ant_value_t key, TypedArrayData *ta_data, sc_entry_t **seen 55) { 56 ant_value_t existing = sc_lookup(seen, key); 57 if (vtype(existing) != T_UNDEF) return existing; 58 59 if (!ta_data || !ta_data->buffer) return js_throw( 60 js, make_dom_exception(js, "TypedArray could not be cloned", "DataCloneError") 61 ); 62 63 ArrayBufferData *new_buf = create_array_buffer_data(ta_data->byte_length); 64 if (!new_buf) return js_mkerr(js, "out of memory"); 65 if (ta_data->byte_length > 0) { 66 memcpy(new_buf->data, ta_data->buffer->data + ta_data->byte_offset, ta_data->byte_length); 67 } 68 69 ant_value_t clone = create_typed_array( 70 js, ta_data->type, new_buf, 0, 71 ta_data->length, buffer_typedarray_type_name(ta_data->type) 72 ); 73 74 sc_add(seen, key, clone); 75 return clone; 76} 77 78static ant_value_t sc_clone_rec(ant_t *js, ant_value_t val, sc_entry_t **seen, sc_entry_t **transfer) { 79 uint8_t t = vtype(val); 80 TypedArrayData *ta_data = NULL; 81 82 if (t == T_UNDEF || t == T_NULL || t == T_BOOL || t == T_NUM || t == T_BIGINT || t == T_STR) return val; 83 if (t == T_SYMBOL) return js_throw(js, make_dom_exception(js, "Symbol cannot be serialized", "DataCloneError")); 84 85 if (is_object_type(val)) { 86 ta_data = buffer_get_typedarray_data(val); 87 } 88 89 if (ta_data) { 90 return sc_clone_typed_array(js, val, ta_data, seen); 91 } 92 93 if (t == T_TYPEDARRAY) { 94 return sc_clone_typed_array(js, val, (TypedArrayData *)js_gettypedarray(val), seen); 95 } 96 97 if (t == T_FUNC || t == T_CFUNC) 98 return js_throw(js, make_dom_exception(js, "() => {} could not be cloned", "DataCloneError")); 99 if (t == T_PROMISE || t == T_GENERATOR) 100 return js_throw(js, make_dom_exception(js, "Value could not be cloned", "DataCloneError")); 101 if (!is_object_type(val)) 102 return js_throw(js, make_dom_exception(js, "Value could not be cloned", "DataCloneError")); 103 104 ant_value_t existing = sc_lookup(seen, val); 105 if (vtype(existing) != T_UNDEF) return existing; 106 107 ArrayBufferData *abd = buffer_get_arraybuffer_data(val); 108 if (abd) { 109 if (abd && !abd->is_detached) { 110 if (transfer && sc_has(transfer, val)) { 111 ArrayBufferData *new_abd = calloc(1, sizeof(ArrayBufferData)); 112 if (!new_abd) return js_mkerr(js, "out of memory"); 113 new_abd->data = abd->data; 114 new_abd->length = abd->length; 115 new_abd->capacity = abd->capacity; 116 new_abd->ref_count = 1; 117 abd->data = NULL; 118 abd->length = 0; 119 abd->capacity = 0; 120 abd->is_detached = 1; 121 js_set(js, val, "byteLength", js_mknum(0)); 122 ant_value_t clone = create_arraybuffer_obj(js, new_abd); 123 sc_add(seen, val, clone); 124 return clone; 125 } 126 ArrayBufferData *new_abd = create_array_buffer_data(abd->length); 127 if (!new_abd) return js_mkerr(js, "out of memory"); 128 if (abd->length > 0) memcpy(new_abd->data, abd->data, abd->length); 129 ant_value_t clone = create_arraybuffer_obj(js, new_abd); 130 sc_add(seen, val, clone); 131 return clone; 132 }} 133 134 if (buffer_is_dataview(val)) { 135 DataViewData *dv = buffer_get_dataview_data(val); 136 if (!dv || !dv->buffer) 137 return js_throw(js, make_dom_exception(js, "DataView could not be cloned", "DataCloneError")); 138 139 ArrayBufferData *new_buf = create_array_buffer_data(dv->buffer->length); 140 if (!new_buf) return js_mkerr(js, "out of memory"); 141 if (dv->buffer->length > 0) memcpy(new_buf->data, dv->buffer->data, dv->buffer->length); 142 143 ant_value_t ab_obj = create_arraybuffer_obj(js, new_buf); 144 ant_value_t clone = create_dataview_with_buffer( 145 js, new_buf, dv->byte_offset, dv->byte_length, ab_obj 146 ); 147 148 if (is_err(clone)) { 149 free_array_buffer_data(new_buf); 150 return clone; 151 } 152 153 js_set(js, clone, "byteOffset", js_mknum((double)dv->byte_offset)); 154 js_set(js, clone, "byteLength", js_mknum((double)dv->byte_length)); 155 156 sc_add(seen, val, clone); 157 free_array_buffer_data(new_buf); 158 return clone; 159 } 160 161 if (t == T_ARR) { 162 ant_value_t clone = js_mkarr(js); 163 sc_add(seen, val, clone); 164 165 ant_offset_t len = js_arr_len(js, val); 166 for (ant_offset_t i = 0; i < len; i++) { 167 ant_value_t ic = sc_clone_rec(js, js_arr_get(js, val, i), seen, transfer); 168 if (is_err(ic)) return ic; 169 js_arr_push(js, clone, ic); 170 } 171 172 return clone; 173 } 174 175 ant_object_t *obj_ptr = js_obj_ptr(val); 176 if (!obj_ptr) 177 return js_throw(js, make_dom_exception(js, "Value could not be cloned", "DataCloneError")); 178 179 if (obj_ptr->type_tag == T_WEAKMAP || obj_ptr->type_tag == T_WEAKSET) 180 return js_throw(js, make_dom_exception(js, "WeakMap/WeakSet could not be cloned", "DataCloneError")); 181 182 if (obj_ptr->type_tag == T_MAP) { 183 ant_value_t clone = js_mkobj(js); 184 js_obj_ptr(clone)->type_tag = T_MAP; 185 186 ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3); 187 if (is_special_object(map_proto)) js_set_proto_init(clone, map_proto); 188 189 map_entry_t **new_head = ant_calloc(sizeof(map_entry_t *)); 190 if (!new_head) return js_mkerr(js, "out of memory"); 191 192 *new_head = NULL; 193 js_set_slot(clone, SLOT_DATA, ANT_PTR(new_head)); 194 sc_add(seen, val, clone); 195 196 map_entry_t **src_head = get_map_from_obj(val); 197 if (src_head && *src_head) { 198 map_entry_t *e, *tmp; 199 HASH_ITER(hh, *src_head, e, tmp) { 200 ant_value_t vc = sc_clone_rec(js, e->value, seen, transfer); 201 if (is_err(vc)) return vc; 202 203 map_entry_t *ne = ant_calloc(sizeof(map_entry_t)); 204 if (!ne) return js_mkerr(js, "out of memory"); 205 206 ne->key = (unsigned char *)strdup((const char *)e->key); 207 ne->key_val = e->key_val; 208 ne->value = vc; 209 210 HASH_ADD_KEYPTR( 211 hh, *new_head, ne->key, 212 (unsigned)strlen((const char *)ne->key), ne 213 ); 214 }} 215 216 return clone; 217 } 218 219 if (obj_ptr->type_tag == T_SET) { 220 ant_value_t clone = js_mkobj(js); 221 js_obj_ptr(clone)->type_tag = T_SET; 222 223 ant_value_t set_proto = js_get_ctor_proto(js, "Set", 3); 224 if (is_special_object(set_proto)) js_set_proto_init(clone, set_proto); 225 226 set_entry_t **new_head = ant_calloc(sizeof(set_entry_t *)); 227 if (!new_head) return js_mkerr(js, "out of memory"); 228 *new_head = NULL; 229 js_set_slot(clone, SLOT_DATA, ANT_PTR(new_head)); 230 sc_add(seen, val, clone); 231 232 set_entry_t **src_head = get_set_from_obj(val); 233 if (src_head && *src_head) { 234 set_entry_t *e, *tmp; 235 HASH_ITER(hh, *src_head, e, tmp) { 236 ant_value_t vc = sc_clone_rec(js, e->value, seen, transfer); 237 if (is_err(vc)) return vc; 238 239 set_entry_t *ne = ant_calloc(sizeof(set_entry_t)); 240 if (!ne) return js_mkerr(js, "out of memory"); 241 242 ne->key = (unsigned char *)strdup((const char *)e->key); 243 ne->value = vc; 244 245 HASH_ADD_KEYPTR( 246 hh, *new_head, ne->key, 247 (unsigned)strlen((const char *)ne->key), ne 248 ); 249 }} 250 251 return clone; 252 } 253 254 if (js_get_slot(val, SLOT_ERROR_BRAND) == js_true) { 255 ant_value_t clone = js_mkobj(js); 256 sc_add(seen, val, clone); 257 258 const char *msg = get_str_prop(js, val, "message", 7, NULL); 259 const char *name = get_str_prop(js, val, "name", 4, NULL); 260 const char *stack = get_str_prop(js, val, "stack", 5, NULL); 261 262 if (msg) js_set(js, clone, "message", js_mkstr(js, msg, strlen(msg))); 263 if (name) js_set(js, clone, "name", js_mkstr(js, name, strlen(name))); 264 if (stack) js_set(js, clone, "stack", js_mkstr(js, stack, strlen(stack))); 265 js_set_slot(clone, SLOT_ERROR_BRAND, js_true); 266 267 ant_value_t err_type = js_get_slot(val, SLOT_ERR_TYPE); 268 if (vtype(err_type) != T_UNDEF) js_set_slot(clone, SLOT_ERR_TYPE, err_type); 269 270 return clone; 271 } 272 273 if (is_date_instance(val)) { 274 ant_value_t clone = js_mkobj(js); 275 ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); 276 if (is_object_type(date_proto)) js_set_proto_init(clone, date_proto); 277 278 js_set_slot(clone, SLOT_DATA, js_get_slot(val, SLOT_DATA)); 279 js_set_slot(clone, SLOT_BRAND, js_mknum(BRAND_DATE)); 280 sc_add(seen, val, clone); 281 282 return clone; 283 } 284 285 blob_data_t *bd = js_is_prototype_of(js, g_blob_proto, val) ? blob_get_data(val) : NULL; 286 if (bd) { 287 ant_value_t clone = blob_create(js, bd->data, bd->size, bd->type); 288 if (is_err(clone)) return clone; 289 sc_add(seen, val, clone); 290 if (bd->name) { 291 blob_data_t *nbd = blob_get_data(clone); 292 if (nbd) { 293 nbd->name = strdup(bd->name); 294 nbd->last_modified = bd->last_modified; 295 } 296 js_set_proto_init(clone, g_file_proto); 297 } 298 return clone; 299 } 300 301 ant_value_t clone = js_mkobj(js); 302 sc_add(seen, val, clone); 303 304 ant_iter_t iter = js_prop_iter_begin(js, val); 305 const char *key; 306 size_t key_len; 307 ant_value_t pval; 308 309 while (js_prop_iter_next(&iter, &key, &key_len, &pval)) { 310 ant_value_t pc = sc_clone_rec(js, pval, seen, transfer); 311 if (is_err(pc)) { js_prop_iter_end(&iter); return pc; } 312 js_set(js, clone, key, pc); 313 } 314 315 js_prop_iter_end(&iter); 316 return clone; 317} 318 319ant_value_t js_structured_clone(ant_t *js, ant_value_t *args, int nargs) { 320 if (nargs < 1) return js_mkundef(); 321 322 sc_entry_t *transfer = NULL; 323 324 if (nargs < 2 || !is_object_type(args[1])) goto clone; 325 ant_value_t xfer_arr = js_get(js, args[1], "transfer"); 326 if (vtype(xfer_arr) != T_ARR) goto clone; 327 328 ant_offset_t xfer_len = js_arr_len(js, xfer_arr); 329 for (ant_offset_t i = 0; i < xfer_len; i++) { 330 ant_value_t item = js_arr_get(js, xfer_arr, i); 331 if (is_object_type(item)) sc_add(&transfer, item, js_true); 332 } 333 334clone:; 335 336 sc_entry_t *seen = NULL; 337 ant_value_t result = sc_clone_rec(js, args[0], &seen, &transfer); 338 sc_free(&seen); 339 sc_free(&transfer); 340 return result; 341} 342 343void init_structured_clone_module(void) { 344 ant_t *js = rt->js; 345 ant_value_t global = js_glob(js); 346 347 js_set(js, global, "structuredClone", js_mkfun(js_structured_clone)); 348 js_set_descriptor(js, global, "structuredClone", 15, JS_DESC_W | JS_DESC_C); 349}