Page MenuHomePhorge

formdata.c
No OneTemporary

Size
16 KB
Referenced Files
None
Subscribers
None

formdata.c

#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdint.h>
#include <time.h>
#include "ant.h"
#include "errors.h"
#include "runtime.h"
#include "internal.h"
#include "descriptors.h"
#include "silver/engine.h"
#include "modules/formdata.h"
#include "modules/blob.h"
#include "modules/symbol.h"
typedef struct {
size_t index;
int kind;
} fd_iter_t;
enum {
FD_ITER_ENTRIES = 0,
FD_ITER_KEYS = 1,
FD_ITER_VALUES = 2
};
static ant_value_t g_formdata_proto = 0;
static ant_value_t g_formdata_iter_proto = 0;
static fd_data_t *get_fd_data(ant_value_t obj);
bool formdata_is_formdata(ant_t *js, ant_value_t obj) {
return js_check_brand(obj, BRAND_FORMDATA);
}
bool formdata_is_empty(ant_value_t fd) {
fd_data_t *d = get_fd_data(fd);
return d ? d->count == 0 : true;
}
static fd_data_t *fd_data_new(void) {
fd_data_t *d = calloc(1, sizeof(fd_data_t));
if (!d) return NULL;
d->tail = &d->head;
return d;
}
static void fd_entry_free(fd_entry_t *e) {
if (!e) return;
free(e->name);
free(e->str_value);
free(e);
}
static void fd_data_free(fd_data_t *d) {
if (!d) return;
for (fd_entry_t *e = d->head; e; ) {
fd_entry_t *n = e->next;
fd_entry_free(e);
e = n;
}
free(d);
}
static fd_data_t *get_fd_data(ant_value_t obj) {
ant_value_t slot = js_get_slot(obj, SLOT_DATA);
if (vtype(slot) != T_NUM) return NULL;
return (fd_data_t *)(uintptr_t)(size_t)js_getnum(slot);
}
static ant_value_t get_fd_values(ant_value_t obj) {
return js_get_slot(obj, SLOT_ENTRIES);
}
static void formdata_finalize(ant_t *js, ant_object_t *obj) {
if (!obj->extra_slots) return;
ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
for (uint8_t i = 0; i < obj->extra_count; i++) {
if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) {
fd_data_t *d = (fd_data_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
fd_data_free(d);
return;
}}
}
static bool fd_append_str(fd_data_t *d, const char *name, const char *value) {
fd_entry_t *e = calloc(1, sizeof(fd_entry_t));
if (!e) return false;
e->name = strdup(name);
e->str_value = strdup(value);
if (!e->name || !e->str_value) { fd_entry_free(e); return false; }
*d->tail = e;
d->tail = &e->next;
d->count++;
return true;
}
static bool fd_append_file(fd_data_t *d, const char *name, size_t val_idx) {
fd_entry_t *e = calloc(1, sizeof(fd_entry_t));
if (!e) return false;
e->is_file = true;
e->name = strdup(name);
e->val_idx = val_idx;
if (!e->name) { free(e); return false; }
*d->tail = e;
d->tail = &e->next;
d->count++;
return true;
}
static void fd_delete_name(fd_data_t *d, const char *name) {
fd_entry_t **pp = &d->head;
d->tail = &d->head;
while (*pp) {
if (strcmp((*pp)->name, name) == 0) {
fd_entry_t *dead = *pp;
*pp = dead->next;
d->count--;
fd_entry_free(dead);
} else {
d->tail = &(*pp)->next;
pp = &(*pp)->next;
}}
}
ant_value_t formdata_create_empty(ant_t *js) {
fd_data_t *d = fd_data_new();
if (!d) return js_mkerr(js, "out of memory");
ant_value_t obj = js_mkobj(js);
js_set_proto_init(obj, g_formdata_proto);
js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FORMDATA));
js_set_slot(obj, SLOT_DATA, ANT_PTR(d));
ant_value_t vals = js_mkarr(js);
js_set_slot_wb(js, obj, SLOT_ENTRIES, vals);
js_set_finalizer(obj, formdata_finalize);
return obj;
}
static ant_value_t entry_to_js_value(ant_t *js, ant_value_t values_arr, fd_entry_t *e) {
if (!e->is_file)
return js_mkstr(js, e->str_value ? e->str_value : "", strlen(e->str_value ? e->str_value : ""));
return js_arr_get(js, values_arr, (ant_offset_t)e->val_idx);
}
static const char *resolve_name(ant_t *js, ant_value_t *name_v) {
if (vtype(*name_v) != T_STR) {
*name_v = js_tostring_val(js, *name_v);
if (is_err(*name_v)) return NULL;
}
return js_getstr(js, *name_v, NULL);
}
static ant_value_t extract_file_entry(
ant_t *js, fd_data_t *d, ant_value_t values_arr,
const char *name, ant_value_t val, ant_value_t filename_v, bool is_set
) {
blob_data_t *bd = blob_get_data(val);
if (!bd) return js_mkerr_typed(js, JS_ERR_TYPE, "FormData value must be a string, Blob, or File");
bool is_file = (bd->name != NULL);
bool no_filename_override = (vtype(filename_v) == T_UNDEF);
ant_value_t stored_val;
if (is_file && no_filename_override) {
stored_val = val;
} else {
const char *fname = NULL;
char *fname_owned = NULL;
if (!no_filename_override) {
ant_value_t fv = filename_v;
if (vtype(fv) != T_STR) { fv = js_tostring_val(js, fv); if (is_err(fv)) return fv; }
fname_owned = strdup(js_getstr(js, fv, NULL));
fname = fname_owned;
}
else if (bd->name && bd->name[0]) fname = bd->name;
else fname = "blob";
int64_t last_modified = bd->last_modified;
if (!last_modified) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
last_modified = (int64_t)ts.tv_sec * 1000LL + (int64_t)(ts.tv_nsec / 1000000);
}
ant_value_t file_obj = blob_create(js, bd->data, bd->size, bd->type);
if (is_err(file_obj)) { free(fname_owned); return file_obj; }
blob_data_t *nbd = blob_get_data(file_obj);
if (nbd) {
nbd->name = strdup(fname);
nbd->last_modified = last_modified;
}
free(fname_owned);
js_set_proto_init(file_obj, g_file_proto);
stored_val = file_obj;
}
if (is_set) fd_delete_name(d, name);
size_t idx = (size_t)js_arr_len(js, values_arr);
js_arr_push(js, values_arr, stored_val);
return fd_append_file(d, name, idx) ? js_mkundef() : js_mkerr(js, "out of memory");
}
ant_value_t formdata_append_string(ant_t *js, ant_value_t fd, ant_value_t name_v, ant_value_t value_v) {
fd_data_t *d = get_fd_data(fd);
if (!d) return js_mkerr(js, "Invalid FormData object");
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
if (vtype(value_v) != T_STR) {
value_v = js_tostring_val(js, value_v);
if (is_err(value_v)) return value_v;
}
return fd_append_str(d, name, js_getstr(js, value_v, NULL))
? js_mkundef()
: js_mkerr(js, "out of memory"
);
}
ant_value_t formdata_append_file(ant_t *js, ant_value_t fd, ant_value_t name_v, ant_value_t blob_v, ant_value_t filename_v) {
fd_data_t *d = get_fd_data(fd);
if (!d) return js_mkerr(js, "Invalid FormData object");
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
ant_value_t values_arr = get_fd_values(fd);
return extract_file_entry(js, d, values_arr, name, blob_v, filename_v, false);
}
static ant_value_t js_formdata_append(ant_t *js, ant_value_t *args, int nargs) {
if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "FormData.append requires 2 arguments");
fd_data_t *d = get_fd_data(js->this_val);
if (!d) return js_mkerr(js, "Invalid FormData object");
ant_value_t name_v = args[0];
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
ant_value_t val = args[1];
if (is_object_type(val) && blob_get_data(val)) {
ant_value_t fname = (nargs >= 3) ? args[2] : js_mkundef();
ant_value_t values_arr = get_fd_values(js->this_val);
return extract_file_entry(js, d, values_arr, name, val, fname, false);
}
if (vtype(val) != T_STR) { val = js_tostring_val(js, val); if (is_err(val)) return val; }
return fd_append_str(d, name, js_getstr(js, val, NULL)) ? js_mkundef() : js_mkerr(js, "out of memory");
}
static ant_value_t js_formdata_set(ant_t *js, ant_value_t *args, int nargs) {
if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "FormData.set requires 2 arguments");
fd_data_t *d = get_fd_data(js->this_val);
if (!d) return js_mkerr(js, "Invalid FormData object");
ant_value_t name_v = args[0];
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
ant_value_t val = args[1];
if (is_object_type(val) && blob_get_data(val)) {
ant_value_t fname = (nargs >= 3) ? args[2] : js_mkundef();
ant_value_t values_arr = get_fd_values(js->this_val);
return extract_file_entry(js, d, values_arr, name, val, fname, true);
}
if (vtype(val) != T_STR) { val = js_tostring_val(js, val); if (is_err(val)) return val; }
fd_delete_name(d, name);
return fd_append_str(d, name, js_getstr(js, val, NULL)) ? js_mkundef() : js_mkerr(js, "out of memory");
}
static ant_value_t js_formdata_get(ant_t *js, ant_value_t *args, int nargs) {
if (nargs < 1) return js_mknull();
fd_data_t *d = get_fd_data(js->this_val);
if (!d) return js_mknull();
ant_value_t name_v = args[0];
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
ant_value_t values_arr = get_fd_values(js->this_val);
for (fd_entry_t *e = d->head; e; e = e->next) {
if (strcmp(e->name, name) == 0)
return entry_to_js_value(js, values_arr, e);
}
return js_mknull();
}
static ant_value_t js_formdata_get_all(ant_t *js, ant_value_t *args, int nargs) {
ant_value_t result = js_mkarr(js);
if (nargs < 1) return result;
fd_data_t *d = get_fd_data(js->this_val);
if (!d) return result;
ant_value_t name_v = args[0];
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
ant_value_t values_arr = get_fd_values(js->this_val);
for (fd_entry_t *e = d->head; e; e = e->next) {
if (strcmp(e->name, name) == 0) {
ant_value_t v = entry_to_js_value(js, values_arr, e);
if (is_err(v)) return v;
js_arr_push(js, result, v);
}}
return result;
}
static ant_value_t js_formdata_has(ant_t *js, ant_value_t *args, int nargs) {
if (nargs < 1) return js_false;
fd_data_t *d = get_fd_data(js->this_val);
if (!d) return js_false;
ant_value_t name_v = args[0];
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
for (fd_entry_t *e = d->head; e; e = e->next) {
if (strcmp(e->name, name) == 0) return js_true;
}
return js_false;
}
static ant_value_t js_formdata_delete(ant_t *js, ant_value_t *args, int nargs) {
if (nargs < 1) return js_mkundef();
fd_data_t *d = get_fd_data(js->this_val);
if (!d) return js_mkundef();
ant_value_t name_v = args[0];
const char *name = resolve_name(js, &name_v);
if (!name) return name_v;
fd_delete_name(d, name);
return js_mkundef();
}
static ant_value_t js_formdata_foreach(ant_t *js, ant_value_t *args, int nargs) {
if (nargs < 1 || !is_callable(args[0]))
return js_mkerr_typed(js, JS_ERR_TYPE, "FormData.forEach requires a function");
fd_data_t *d = get_fd_data(js->this_val);
if (!d) return js_mkundef();
ant_value_t fn = args[0];
ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef();
ant_value_t self = js->this_val;
ant_value_t values_arr = get_fd_values(self);
for (fd_entry_t *e = d->head; e; e = e->next) {
ant_value_t val = entry_to_js_value(js, values_arr, e);
if (is_err(val)) return val;
ant_value_t name = js_mkstr(js, e->name, strlen(e->name));
ant_value_t cb_args[3] = { val, name, self };
ant_value_t r = sv_vm_call(js->vm, js, fn, this_arg, cb_args, 3, NULL, false);
if (is_err(r)) return r;
}
return js_mkundef();
}
static ant_value_t formdata_iter_next(ant_t *js, ant_value_t *args, int nargs) {
ant_value_t state_v = js_get_slot(js->this_val, SLOT_ITER_STATE);
if (vtype(state_v) != T_NUM) return js_iter_result(js, false, js_mkundef());
fd_iter_t *st = (fd_iter_t *)(uintptr_t)(size_t)js_getnum(state_v);
ant_value_t fd_obj = js_get_slot(js->this_val, SLOT_DATA);
fd_data_t *d = get_fd_data(fd_obj);
if (!d) return js_iter_result(js, false, js_mkundef());
ant_value_t values_arr = get_fd_values(fd_obj);
size_t idx = 0;
for (fd_entry_t *e = d->head; e; e = e->next, idx++) {
if (idx == st->index) {
st->index++;
ant_value_t out;
switch (st->kind) {
case FD_ITER_KEYS:
out = js_mkstr(js, e->name, strlen(e->name));
break;
case FD_ITER_VALUES:
out = entry_to_js_value(js, values_arr, e);
break;
default: {
ant_value_t v = entry_to_js_value(js, values_arr, e);
if (is_err(v)) return v;
out = js_mkarr(js);
js_arr_push(js, out, js_mkstr(js, e->name, strlen(e->name)));
js_arr_push(js, out, v);
break;
}}
if (is_err(out)) return out;
return js_iter_result(js, true, out);
}}
return js_iter_result(js, false, js_mkundef());
}
static void formdata_iter_finalize(ant_t *js, ant_object_t *obj) {
if (!obj->extra_slots) return;
ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots;
for (uint8_t i = 0; i < obj->extra_count; i++) {
if (entries[i].slot == SLOT_ITER_STATE && vtype(entries[i].value) == T_NUM) {
fd_iter_t *st = (fd_iter_t *)(uintptr_t)(size_t)js_getnum(entries[i].value);
free(st);
return;
}}
}
static ant_value_t make_formdata_iter(ant_t *js, ant_value_t fd_obj, int kind) {
fd_iter_t *st = calloc(1, sizeof(fd_iter_t));
if (!st) return js_mkerr(js, "out of memory");
st->kind = kind;
ant_value_t iter = js_mkobj(js);
js_set_proto_init(iter, g_formdata_iter_proto);
js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(st));
js_set_slot_wb(js, iter, SLOT_DATA, fd_obj);
js_set_finalizer(iter, formdata_iter_finalize);
return iter;
}
static ant_value_t js_formdata_entries(ant_t *js, ant_value_t *args, int nargs) {
return make_formdata_iter(js, js->this_val, FD_ITER_ENTRIES);
}
static ant_value_t js_formdata_keys(ant_t *js, ant_value_t *args, int nargs) {
return make_formdata_iter(js, js->this_val, FD_ITER_KEYS);
}
static ant_value_t js_formdata_values(ant_t *js, ant_value_t *args, int nargs) {
return make_formdata_iter(js, js->this_val, FD_ITER_VALUES);
}
static ant_value_t js_formdata_ctor(ant_t *js, ant_value_t *args, int nargs) {
if (vtype(js->new_target) == T_UNDEF)
return js_mkerr_typed(js, JS_ERR_TYPE, "FormData constructor requires 'new'");
if (nargs >= 1 && vtype(args[0]) != T_UNDEF)
return js_mkerr_typed(js, JS_ERR_TYPE, "FormData does not support a form element argument");
fd_data_t *d = fd_data_new();
if (!d) return js_mkerr(js, "out of memory");
ant_value_t obj = js_mkobj(js);
ant_value_t proto = js_instance_proto_from_new_target(js, g_formdata_proto);
if (is_object_type(proto)) js_set_proto_init(obj, proto);
js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FORMDATA));
js_set_slot(obj, SLOT_DATA, ANT_PTR(d));
ant_value_t vals = js_mkarr(js);
js_set_slot_wb(js, obj, SLOT_ENTRIES, vals);
js_set_finalizer(obj, formdata_finalize);
return obj;
}
void init_formdata_module(void) {
ant_t *js = rt->js;
ant_value_t g = js_glob(js);
g_formdata_proto = js_mkobj(js);
js_set(js, g_formdata_proto, "append", js_mkfun(js_formdata_append));
js_set(js, g_formdata_proto, "set", js_mkfun(js_formdata_set));
js_set(js, g_formdata_proto, "get", js_mkfun(js_formdata_get));
js_set(js, g_formdata_proto, "getAll", js_mkfun(js_formdata_get_all));
js_set(js, g_formdata_proto, "has", js_mkfun(js_formdata_has));
js_set(js, g_formdata_proto, "delete", js_mkfun(js_formdata_delete));
js_set(js, g_formdata_proto, "forEach", js_mkfun(js_formdata_foreach));
js_set(js, g_formdata_proto, "entries", js_mkfun(js_formdata_entries));
js_set(js, g_formdata_proto, "keys", js_mkfun(js_formdata_keys));
js_set(js, g_formdata_proto, "values", js_mkfun(js_formdata_values));
js_set_sym(js, g_formdata_proto, get_iterator_sym(), js_mkfun(js_formdata_entries));
js_set_sym(js, g_formdata_proto, get_toStringTag_sym(), js_mkstr(js, "FormData", 8));
ant_value_t ctor_obj = js_mkobj(js);
js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(js_formdata_ctor));
js_mkprop_fast(js, ctor_obj, "prototype", 9, g_formdata_proto);
js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, "FormData", 8));
js_set_descriptor(js, ctor_obj, "name", 4, 0);
ant_value_t ctor = js_obj_to_func(ctor_obj);
js_set(js, g_formdata_proto, "constructor", ctor);
js_set_descriptor(js, g_formdata_proto, "constructor", 11, JS_DESC_W | JS_DESC_C);
js_set(js, g, "FormData", ctor);
js_set_descriptor(js, g, "FormData", 8, JS_DESC_W | JS_DESC_C);
g_formdata_iter_proto = js_mkobj(js);
js_set_proto_init(g_formdata_iter_proto, js->sym.iterator_proto);
js_set(js, g_formdata_iter_proto, "next", js_mkfun(formdata_iter_next));
js_set_descriptor(js, g_formdata_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C);
js_set_sym(js, g_formdata_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb));
}

File Metadata

Mime Type
text/x-c
Expires
Fri, Apr 3, 6:52 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
520820
Default Alt Text
formdata.c (16 KB)

Event Timeline