Page MenuHomePhorge

structured-clone.c
No OneTemporary

Size
11 KB
Referenced Files
None
Subscribers
None

structured-clone.c

#include <string.h>
#include <stdlib.h>
#include <uthash.h>
#include "ant.h"
#include "errors.h"
#include "runtime.h"
#include "internal.h"
#include "descriptors.h"
#include "modules/buffer.h"
#include "modules/collections.h"
#include "modules/domexception.h"
#include "modules/blob.h"
#include "modules/structured-clone.h"
typedef struct sc_entry {
ant_value_t key;
ant_value_t value;
UT_hash_handle hh;
} sc_entry_t;
static void sc_add(sc_entry_t **table, ant_value_t key, ant_value_t value) {
sc_entry_t *e = calloc(1, sizeof(sc_entry_t));
if (!e) return;
e->key = key;
e->value = value;
HASH_ADD(hh, *table, key, sizeof(ant_value_t), e);
}
static ant_value_t sc_lookup(sc_entry_t **table, ant_value_t key) {
sc_entry_t *e;
HASH_FIND(hh, *table, &key, sizeof(ant_value_t), e);
return e ? e->value : js_mkundef();
}
static bool sc_has(sc_entry_t **table, ant_value_t key) {
sc_entry_t *e;
HASH_FIND(hh, *table, &key, sizeof(ant_value_t), e);
return e != NULL;
}
static void sc_free(sc_entry_t **table) {
sc_entry_t *e, *tmp;
HASH_ITER(hh, *table, e, tmp) {
HASH_DEL(*table, e);
free(e);
}
}
static ant_value_t sc_clone_typed_array(
ant_t *js, ant_value_t key, TypedArrayData *ta_data, sc_entry_t **seen
) {
ant_value_t existing = sc_lookup(seen, key);
if (vtype(existing) != T_UNDEF) return existing;
if (!ta_data || !ta_data->buffer) return js_throw(
js, make_dom_exception(js, "TypedArray could not be cloned", "DataCloneError")
);
ArrayBufferData *new_buf = create_array_buffer_data(ta_data->byte_length);
if (!new_buf) return js_mkerr(js, "out of memory");
if (ta_data->byte_length > 0) {
memcpy(new_buf->data, ta_data->buffer->data + ta_data->byte_offset, ta_data->byte_length);
}
ant_value_t clone = create_typed_array(
js, ta_data->type, new_buf, 0,
ta_data->length, buffer_typedarray_type_name(ta_data->type)
);
sc_add(seen, key, clone);
return clone;
}
static ant_value_t sc_clone_rec(ant_t *js, ant_value_t val, sc_entry_t **seen, sc_entry_t **transfer) {
uint8_t t = vtype(val);
ant_value_t slot = js_mkundef();
TypedArrayData *ta_data = NULL;
if (t == T_UNDEF || t == T_NULL || t == T_BOOL || t == T_NUM || t == T_BIGINT || t == T_STR) return val;
if (t == T_SYMBOL) return js_throw(js, make_dom_exception(js, "Symbol cannot be serialized", "DataCloneError"));
if (is_object_type(val)) {
slot = js_get_slot(val, SLOT_BUFFER);
ta_data = (TypedArrayData *)js_gettypedarray(slot);
}
if (ta_data) {
return sc_clone_typed_array(js, val, ta_data, seen);
}
if (t == T_TYPEDARRAY) {
return sc_clone_typed_array(js, val, (TypedArrayData *)js_gettypedarray(val), seen);
}
if (t == T_FUNC || t == T_CFUNC || t == T_CLOSURE)
return js_throw(js, make_dom_exception(js, "() => {} could not be cloned", "DataCloneError"));
if (t == T_PROMISE || t == T_GENERATOR)
return js_throw(js, make_dom_exception(js, "Value could not be cloned", "DataCloneError"));
if (!is_object_type(val))
return js_throw(js, make_dom_exception(js, "Value could not be cloned", "DataCloneError"));
ant_value_t existing = sc_lookup(seen, val);
if (vtype(existing) != T_UNDEF) return existing;
ant_value_t buf_slot_v = js_get_slot(val, SLOT_BUFFER);
if (vtype(buf_slot_v) == T_NUM) {
ArrayBufferData *abd = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(buf_slot_v);
if (abd && !abd->is_detached) {
if (transfer && sc_has(transfer, val)) {
ArrayBufferData *new_abd = calloc(1, sizeof(ArrayBufferData));
if (!new_abd) return js_mkerr(js, "out of memory");
new_abd->data = abd->data;
new_abd->length = abd->length;
new_abd->capacity = abd->capacity;
new_abd->ref_count = 1;
abd->data = NULL;
abd->length = 0;
abd->capacity = 0;
abd->is_detached = 1;
js_set(js, val, "byteLength", js_mknum(0));
ant_value_t clone = create_arraybuffer_obj(js, new_abd);
sc_add(seen, val, clone);
return clone;
}
ArrayBufferData *new_abd = create_array_buffer_data(abd->length);
if (!new_abd) return js_mkerr(js, "out of memory");
if (abd->length > 0) memcpy(new_abd->data, abd->data, abd->length);
ant_value_t clone = create_arraybuffer_obj(js, new_abd);
sc_add(seen, val, clone);
return clone;
}}
if (buffer_is_dataview(val)) {
ant_value_t dv_data_val = js_get_slot(val, SLOT_DATA);
if (vtype(dv_data_val) != T_NUM)
return js_throw(js, make_dom_exception(js, "DataView could not be cloned", "DataCloneError"));
DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val);
if (!dv || !dv->buffer)
return js_throw(js, make_dom_exception(js, "DataView could not be cloned", "DataCloneError"));
ArrayBufferData *new_buf = create_array_buffer_data(dv->buffer->length);
if (!new_buf) return js_mkerr(js, "out of memory");
if (dv->buffer->length > 0) memcpy(new_buf->data, dv->buffer->data, dv->buffer->length);
ant_value_t ab_obj = create_arraybuffer_obj(js, new_buf);
ant_value_t clone = create_dataview_with_buffer(
js, new_buf, dv->byte_offset, dv->byte_length, ab_obj
);
if (is_err(clone)) {
free_array_buffer_data(new_buf);
return clone;
}
js_set(js, clone, "byteOffset", js_mknum((double)dv->byte_offset));
js_set(js, clone, "byteLength", js_mknum((double)dv->byte_length));
sc_add(seen, val, clone);
free_array_buffer_data(new_buf);
return clone;
}
if (t == T_ARR) {
ant_value_t clone = js_mkarr(js);
sc_add(seen, val, clone);
ant_offset_t len = js_arr_len(js, val);
for (ant_offset_t i = 0; i < len; i++) {
ant_value_t ic = sc_clone_rec(js, js_arr_get(js, val, i), seen, transfer);
if (is_err(ic)) return ic;
js_arr_push(js, clone, ic);
}
return clone;
}
ant_object_t *obj_ptr = js_obj_ptr(val);
if (!obj_ptr)
return js_throw(js, make_dom_exception(js, "Value could not be cloned", "DataCloneError"));
if (obj_ptr->type_tag == T_WEAKMAP || obj_ptr->type_tag == T_WEAKSET)
return js_throw(js, make_dom_exception(js, "WeakMap/WeakSet could not be cloned", "DataCloneError"));
if (obj_ptr->type_tag == T_MAP) {
ant_value_t clone = js_mkobj(js);
js_obj_ptr(clone)->type_tag = T_MAP;
ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3);
if (is_special_object(map_proto)) js_set_proto_init(clone, map_proto);
map_entry_t **new_head = ant_calloc(sizeof(map_entry_t *));
if (!new_head) return js_mkerr(js, "out of memory");
*new_head = NULL;
js_set_slot(clone, SLOT_DATA, ANT_PTR(new_head));
sc_add(seen, val, clone);
map_entry_t **src_head = get_map_from_obj(js, val);
if (src_head && *src_head) {
map_entry_t *e, *tmp;
HASH_ITER(hh, *src_head, e, tmp) {
ant_value_t vc = sc_clone_rec(js, e->value, seen, transfer);
if (is_err(vc)) return vc;
map_entry_t *ne = ant_calloc(sizeof(map_entry_t));
if (!ne) return js_mkerr(js, "out of memory");
ne->key = (unsigned char *)strdup((const char *)e->key);
ne->key_val = e->key_val;
ne->value = vc;
HASH_ADD_KEYPTR(
hh, *new_head, ne->key,
(unsigned)strlen((const char *)ne->key), ne
);
}}
return clone;
}
if (obj_ptr->type_tag == T_SET) {
ant_value_t clone = js_mkobj(js);
js_obj_ptr(clone)->type_tag = T_SET;
ant_value_t set_proto = js_get_ctor_proto(js, "Set", 3);
if (is_special_object(set_proto)) js_set_proto_init(clone, set_proto);
set_entry_t **new_head = ant_calloc(sizeof(set_entry_t *));
if (!new_head) return js_mkerr(js, "out of memory");
*new_head = NULL;
js_set_slot(clone, SLOT_DATA, ANT_PTR(new_head));
sc_add(seen, val, clone);
set_entry_t **src_head = get_set_from_obj(js, val);
if (src_head && *src_head) {
set_entry_t *e, *tmp;
HASH_ITER(hh, *src_head, e, tmp) {
ant_value_t vc = sc_clone_rec(js, e->value, seen, transfer);
if (is_err(vc)) return vc;
set_entry_t *ne = ant_calloc(sizeof(set_entry_t));
if (!ne) return js_mkerr(js, "out of memory");
ne->key = (unsigned char *)strdup((const char *)e->key);
ne->value = vc;
HASH_ADD_KEYPTR(
hh, *new_head, ne->key,
(unsigned)strlen((const char *)ne->key), ne
);
}}
return clone;
}
if (js_get_slot(val, SLOT_ERROR_BRAND) == js_true) {
ant_value_t clone = js_mkobj(js);
sc_add(seen, val, clone);
const char *msg = get_str_prop(js, val, "message", 7, NULL);
const char *name = get_str_prop(js, val, "name", 4, NULL);
const char *stack = get_str_prop(js, val, "stack", 5, NULL);
if (msg) js_set(js, clone, "message", js_mkstr(js, msg, strlen(msg)));
if (name) js_set(js, clone, "name", js_mkstr(js, name, strlen(name)));
if (stack) js_set(js, clone, "stack", js_mkstr(js, stack, strlen(stack)));
js_set_slot(clone, SLOT_ERROR_BRAND, js_true);
ant_value_t err_type = js_get_slot(val, SLOT_ERR_TYPE);
if (vtype(err_type) != T_UNDEF) js_set_slot(clone, SLOT_ERR_TYPE, err_type);
return clone;
}
ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4);
if (
vtype(date_proto) != T_UNDEF
&& is_object_type(date_proto)
&& js_is_prototype_of(js, date_proto, val)
) {
ant_value_t clone = js_mkobj(js);
js_set_proto_init(clone, date_proto);
js_set_slot(clone, SLOT_DATA, js_get_slot(val, SLOT_DATA));
sc_add(seen, val, clone);
return clone;
}
blob_data_t *bd = js_is_prototype_of(js, g_blob_proto, val) ? blob_get_data(val) : NULL;
if (bd) {
ant_value_t clone = blob_create(js, bd->data, bd->size, bd->type);
if (is_err(clone)) return clone;
sc_add(seen, val, clone);
if (bd->name) {
blob_data_t *nbd = blob_get_data(clone);
if (nbd) {
nbd->name = strdup(bd->name);
nbd->last_modified = bd->last_modified;
}
js_set_proto_init(clone, g_file_proto);
}
return clone;
}
ant_value_t clone = js_mkobj(js);
sc_add(seen, val, clone);
ant_iter_t iter = js_prop_iter_begin(js, val);
const char *key;
size_t key_len;
ant_value_t pval;
while (js_prop_iter_next(&iter, &key, &key_len, &pval)) {
ant_value_t pc = sc_clone_rec(js, pval, seen, transfer);
if (is_err(pc)) { js_prop_iter_end(&iter); return pc; }
js_set(js, clone, key, pc);
}
js_prop_iter_end(&iter);
return clone;
}
ant_value_t js_structured_clone(ant_t *js, ant_value_t *args, int nargs) {
if (nargs < 1) return js_mkundef();
sc_entry_t *transfer = NULL;
if (nargs < 2 || !is_object_type(args[1])) goto clone;
ant_value_t xfer_arr = js_get(js, args[1], "transfer");
if (vtype(xfer_arr) != T_ARR) goto clone;
ant_offset_t xfer_len = js_arr_len(js, xfer_arr);
for (ant_offset_t i = 0; i < xfer_len; i++) {
ant_value_t item = js_arr_get(js, xfer_arr, i);
if (is_object_type(item)) sc_add(&transfer, item, js_true);
}
clone:;
sc_entry_t *seen = NULL;
ant_value_t result = sc_clone_rec(js, args[0], &seen, &transfer);
sc_free(&seen);
sc_free(&transfer);
return result;
}
void init_structured_clone_module(void) {
ant_t *js = rt->js;
ant_value_t global = js_glob(js);
js_set(js, global, "structuredClone", js_mkfun(js_structured_clone));
js_set_descriptor(js, global, "structuredClone", 15, JS_DESC_W | JS_DESC_C);
}

File Metadata

Mime Type
text/x-c
Expires
Fri, Apr 3, 3:06 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
521542
Default Alt Text
structured-clone.c (11 KB)

Event Timeline