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