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 511 lines 17 kB view raw
1#include <stdlib.h> 2#include <string.h> 3#include <strings.h> 4#include <stdint.h> 5#include <time.h> 6 7#include "ant.h" 8#include "errors.h" 9#include "runtime.h" 10#include "internal.h" 11#include "descriptors.h" 12 13#include "silver/engine.h" 14#include "modules/formdata.h" 15#include "modules/blob.h" 16#include "modules/symbol.h" 17 18typedef struct { 19 size_t index; 20 int kind; 21} fd_iter_t; 22 23enum { 24 FD_ITER_ENTRIES = 0, 25 FD_ITER_KEYS = 1, 26 FD_ITER_VALUES = 2 27}; 28 29static ant_value_t g_formdata_proto = 0; 30static ant_value_t g_formdata_iter_proto = 0; 31static fd_data_t *get_fd_data(ant_value_t obj); 32 33bool formdata_is_formdata(ant_t *js, ant_value_t obj) { 34 return js_check_brand(obj, BRAND_FORMDATA); 35} 36 37bool formdata_is_empty(ant_value_t fd) { 38 fd_data_t *d = get_fd_data(fd); 39 return d ? d->count == 0 : true; 40} 41 42static fd_data_t *fd_data_new(void) { 43 fd_data_t *d = calloc(1, sizeof(fd_data_t)); 44 if (!d) return NULL; 45 d->tail = &d->head; 46 return d; 47} 48 49static void fd_entry_free(fd_entry_t *e) { 50 if (!e) return; 51 free(e->name); 52 free(e->str_value); 53 free(e); 54} 55 56static void fd_data_free(fd_data_t *d) { 57 if (!d) return; 58 for (fd_entry_t *e = d->head; e; ) { 59 fd_entry_t *n = e->next; 60 fd_entry_free(e); 61 e = n; 62 } 63 free(d); 64} 65 66static fd_data_t *get_fd_data(ant_value_t obj) { 67 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 68 if (vtype(slot) != T_NUM) return NULL; 69 return (fd_data_t *)(uintptr_t)(size_t)js_getnum(slot); 70} 71 72static ant_value_t get_fd_values(ant_value_t obj) { 73 return js_get_slot(obj, SLOT_ENTRIES); 74} 75 76static void formdata_finalize(ant_t *js, ant_object_t *obj) { 77 if (!obj->extra_slots) return; 78 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 79 80 for (uint8_t i = 0; i < obj->extra_count; i++) { 81 if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 82 fd_data_t *d = (fd_data_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 83 fd_data_free(d); 84 return; 85 }} 86} 87 88static bool fd_append_str(fd_data_t *d, const char *name, const char *value) { 89 fd_entry_t *e = calloc(1, sizeof(fd_entry_t)); 90 if (!e) return false; 91 92 e->name = strdup(name); 93 e->str_value = strdup(value); 94 95 if (!e->name || !e->str_value) { fd_entry_free(e); return false; } 96 97 *d->tail = e; 98 d->tail = &e->next; 99 d->count++; 100 return true; 101} 102 103static bool fd_append_file(fd_data_t *d, const char *name, size_t val_idx) { 104 fd_entry_t *e = calloc(1, sizeof(fd_entry_t)); 105 if (!e) return false; 106 107 e->is_file = true; 108 e->name = strdup(name); 109 e->val_idx = val_idx; 110 111 if (!e->name) { free(e); return false; } 112 113 *d->tail = e; 114 d->tail = &e->next; 115 d->count++; 116 return true; 117} 118 119static void fd_delete_name(fd_data_t *d, const char *name) { 120 fd_entry_t **pp = &d->head; 121 d->tail = &d->head; 122 123 while (*pp) { 124 if (strcmp((*pp)->name, name) == 0) { 125 fd_entry_t *dead = *pp; 126 *pp = dead->next; 127 d->count--; 128 fd_entry_free(dead); 129 } else { 130 d->tail = &(*pp)->next; 131 pp = &(*pp)->next; 132 }} 133} 134 135ant_value_t formdata_create_empty(ant_t *js) { 136 fd_data_t *d = fd_data_new(); 137 if (!d) return js_mkerr(js, "out of memory"); 138 139 ant_value_t obj = js_mkobj(js); 140 js_set_proto_init(obj, g_formdata_proto); 141 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FORMDATA)); 142 js_set_slot(obj, SLOT_DATA, ANT_PTR(d)); 143 144 ant_value_t vals = js_mkarr(js); 145 js_set_slot_wb(js, obj, SLOT_ENTRIES, vals); 146 js_set_finalizer(obj, formdata_finalize); 147 148 return obj; 149} 150 151static ant_value_t entry_to_js_value(ant_t *js, ant_value_t values_arr, fd_entry_t *e) { 152 if (!e->is_file) 153 return js_mkstr(js, e->str_value ? e->str_value : "", strlen(e->str_value ? e->str_value : "")); 154 return js_arr_get(js, values_arr, (ant_offset_t)e->val_idx); 155} 156 157static const char *resolve_name(ant_t *js, ant_value_t *name_v) { 158 if (vtype(*name_v) != T_STR) { 159 *name_v = js_tostring_val(js, *name_v); 160 if (is_err(*name_v)) return NULL; 161 } 162 return js_getstr(js, *name_v, NULL); 163} 164 165static ant_value_t extract_file_entry( 166 ant_t *js, fd_data_t *d, ant_value_t values_arr, 167 const char *name, ant_value_t val, ant_value_t filename_v, bool is_set 168) { 169 blob_data_t *bd = blob_get_data(val); 170 if (!bd) return js_mkerr_typed(js, JS_ERR_TYPE, "FormData value must be a string, Blob, or File"); 171 172 bool is_file = (bd->name != NULL); 173 bool no_filename_override = (vtype(filename_v) == T_UNDEF); 174 175 ant_value_t stored_val; 176 177 if (is_file && no_filename_override) { 178 stored_val = val; 179 } else { 180 const char *fname = NULL; 181 char *fname_owned = NULL; 182 183 if (!no_filename_override) { 184 ant_value_t fv = filename_v; 185 if (vtype(fv) != T_STR) { fv = js_tostring_val(js, fv); if (is_err(fv)) return fv; } 186 fname_owned = strdup(js_getstr(js, fv, NULL)); 187 fname = fname_owned; 188 } 189 190 else if (bd->name && bd->name[0]) fname = bd->name; 191 else fname = "blob"; 192 193 int64_t last_modified = bd->last_modified; 194 if (!last_modified) { 195 struct timespec ts; 196 clock_gettime(CLOCK_REALTIME, &ts); 197 last_modified = (int64_t)ts.tv_sec * 1000LL + (int64_t)(ts.tv_nsec / 1000000); 198 } 199 200 ant_value_t file_obj = blob_create(js, bd->data, bd->size, bd->type); 201 if (is_err(file_obj)) { free(fname_owned); return file_obj; } 202 203 blob_data_t *nbd = blob_get_data(file_obj); 204 if (nbd) { 205 nbd->name = strdup(fname); 206 nbd->last_modified = last_modified; 207 } 208 209 free(fname_owned); 210 js_set_proto_init(file_obj, g_file_proto); 211 stored_val = file_obj; 212 } 213 214 if (is_set) fd_delete_name(d, name); 215 size_t idx = (size_t)js_arr_len(js, values_arr); 216 js_arr_push(js, values_arr, stored_val); 217 218 return fd_append_file(d, name, idx) ? js_mkundef() : js_mkerr(js, "out of memory"); 219} 220 221ant_value_t formdata_append_string(ant_t *js, ant_value_t fd, ant_value_t name_v, ant_value_t value_v) { 222 fd_data_t *d = get_fd_data(fd); 223 if (!d) return js_mkerr(js, "Invalid FormData object"); 224 225 const char *name = resolve_name(js, &name_v); 226 if (!name) return name_v; 227 228 if (vtype(value_v) != T_STR) { 229 value_v = js_tostring_val(js, value_v); 230 if (is_err(value_v)) return value_v; 231 } 232 233 return fd_append_str(d, name, js_getstr(js, value_v, NULL)) 234 ? js_mkundef() 235 : js_mkerr(js, "out of memory" 236 ); 237} 238 239ant_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) { 240 fd_data_t *d = get_fd_data(fd); 241 if (!d) return js_mkerr(js, "Invalid FormData object"); 242 const char *name = resolve_name(js, &name_v); 243 if (!name) return name_v; 244 ant_value_t values_arr = get_fd_values(fd); 245 return extract_file_entry(js, d, values_arr, name, blob_v, filename_v, false); 246} 247 248static ant_value_t js_formdata_append(ant_t *js, ant_value_t *args, int nargs) { 249 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "FormData.append requires 2 arguments"); 250 fd_data_t *d = get_fd_data(js->this_val); 251 if (!d) return js_mkerr(js, "Invalid FormData object"); 252 253 ant_value_t name_v = args[0]; 254 const char *name = resolve_name(js, &name_v); 255 if (!name) return name_v; 256 257 ant_value_t val = args[1]; 258 if (is_object_type(val) && blob_get_data(val)) { 259 ant_value_t fname = (nargs >= 3) ? args[2] : js_mkundef(); 260 ant_value_t values_arr = get_fd_values(js->this_val); 261 return extract_file_entry(js, d, values_arr, name, val, fname, false); 262 } 263 264 if (vtype(val) != T_STR) { val = js_tostring_val(js, val); if (is_err(val)) return val; } 265 return fd_append_str(d, name, js_getstr(js, val, NULL)) ? js_mkundef() : js_mkerr(js, "out of memory"); 266} 267 268static ant_value_t js_formdata_set(ant_t *js, ant_value_t *args, int nargs) { 269 if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "FormData.set requires 2 arguments"); 270 fd_data_t *d = get_fd_data(js->this_val); 271 if (!d) return js_mkerr(js, "Invalid FormData object"); 272 273 ant_value_t name_v = args[0]; 274 const char *name = resolve_name(js, &name_v); 275 if (!name) return name_v; 276 277 ant_value_t val = args[1]; 278 if (is_object_type(val) && blob_get_data(val)) { 279 ant_value_t fname = (nargs >= 3) ? args[2] : js_mkundef(); 280 ant_value_t values_arr = get_fd_values(js->this_val); 281 return extract_file_entry(js, d, values_arr, name, val, fname, true); 282 } 283 284 if (vtype(val) != T_STR) { val = js_tostring_val(js, val); if (is_err(val)) return val; } 285 fd_delete_name(d, name); 286 return fd_append_str(d, name, js_getstr(js, val, NULL)) ? js_mkundef() : js_mkerr(js, "out of memory"); 287} 288 289static ant_value_t js_formdata_get(ant_t *js, ant_value_t *args, int nargs) { 290 if (nargs < 1) return js_mknull(); 291 fd_data_t *d = get_fd_data(js->this_val); 292 if (!d) return js_mknull(); 293 294 ant_value_t name_v = args[0]; 295 const char *name = resolve_name(js, &name_v); 296 if (!name) return name_v; 297 298 ant_value_t values_arr = get_fd_values(js->this_val); 299 for (fd_entry_t *e = d->head; e; e = e->next) { 300 if (strcmp(e->name, name) == 0) 301 return entry_to_js_value(js, values_arr, e); 302 } 303 return js_mknull(); 304} 305 306static ant_value_t js_formdata_get_all(ant_t *js, ant_value_t *args, int nargs) { 307 ant_value_t result = js_mkarr(js); 308 if (nargs < 1) return result; 309 fd_data_t *d = get_fd_data(js->this_val); 310 if (!d) return result; 311 312 ant_value_t name_v = args[0]; 313 const char *name = resolve_name(js, &name_v); 314 if (!name) return name_v; 315 316 ant_value_t values_arr = get_fd_values(js->this_val); 317 for (fd_entry_t *e = d->head; e; e = e->next) { 318 if (strcmp(e->name, name) == 0) { 319 ant_value_t v = entry_to_js_value(js, values_arr, e); 320 if (is_err(v)) return v; 321 js_arr_push(js, result, v); 322 }} 323 return result; 324} 325 326static ant_value_t js_formdata_has(ant_t *js, ant_value_t *args, int nargs) { 327 if (nargs < 1) return js_false; 328 fd_data_t *d = get_fd_data(js->this_val); 329 if (!d) return js_false; 330 331 ant_value_t name_v = args[0]; 332 const char *name = resolve_name(js, &name_v); 333 if (!name) return name_v; 334 335 for (fd_entry_t *e = d->head; e; e = e->next) { 336 if (strcmp(e->name, name) == 0) return js_true; 337 } 338 return js_false; 339} 340 341static ant_value_t js_formdata_delete(ant_t *js, ant_value_t *args, int nargs) { 342 if (nargs < 1) return js_mkundef(); 343 fd_data_t *d = get_fd_data(js->this_val); 344 if (!d) return js_mkundef(); 345 346 ant_value_t name_v = args[0]; 347 const char *name = resolve_name(js, &name_v); 348 if (!name) return name_v; 349 350 fd_delete_name(d, name); 351 return js_mkundef(); 352} 353 354static ant_value_t js_formdata_foreach(ant_t *js, ant_value_t *args, int nargs) { 355 if (nargs < 1 || !is_callable(args[0])) 356 return js_mkerr_typed(js, JS_ERR_TYPE, "FormData.forEach requires a function"); 357 fd_data_t *d = get_fd_data(js->this_val); 358 if (!d) return js_mkundef(); 359 360 ant_value_t fn = args[0]; 361 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef(); 362 ant_value_t self = js->this_val; 363 ant_value_t values_arr = get_fd_values(self); 364 365 for (fd_entry_t *e = d->head; e; e = e->next) { 366 ant_value_t val = entry_to_js_value(js, values_arr, e); 367 if (is_err(val)) return val; 368 ant_value_t name = js_mkstr(js, e->name, strlen(e->name)); 369 ant_value_t cb_args[3] = { val, name, self }; 370 ant_value_t r = sv_vm_call(js->vm, js, fn, this_arg, cb_args, 3, NULL, false); 371 if (is_err(r)) return r; 372 } 373 return js_mkundef(); 374} 375 376static ant_value_t formdata_iter_next(ant_t *js, ant_value_t *args, int nargs) { 377 ant_value_t state_v = js_get_slot(js->this_val, SLOT_ITER_STATE); 378 if (vtype(state_v) != T_NUM) return js_iter_result(js, false, js_mkundef()); 379 380 fd_iter_t *st = (fd_iter_t *)(uintptr_t)(size_t)js_getnum(state_v); 381 ant_value_t fd_obj = js_get_slot(js->this_val, SLOT_DATA); 382 fd_data_t *d = get_fd_data(fd_obj); 383 if (!d) return js_iter_result(js, false, js_mkundef()); 384 385 ant_value_t values_arr = get_fd_values(fd_obj); 386 387 size_t idx = 0; 388 for (fd_entry_t *e = d->head; e; e = e->next, idx++) { 389 if (idx == st->index) { 390 st->index++; 391 ant_value_t out; 392 switch (st->kind) { 393 case FD_ITER_KEYS: 394 out = js_mkstr(js, e->name, strlen(e->name)); 395 break; 396 case FD_ITER_VALUES: 397 out = entry_to_js_value(js, values_arr, e); 398 break; 399 default: { 400 ant_value_t v = entry_to_js_value(js, values_arr, e); 401 if (is_err(v)) return v; 402 out = js_mkarr(js); 403 js_arr_push(js, out, js_mkstr(js, e->name, strlen(e->name))); 404 js_arr_push(js, out, v); 405 break; 406 }} 407 if (is_err(out)) return out; 408 return js_iter_result(js, true, out); 409 }} 410 411 return js_iter_result(js, false, js_mkundef()); 412} 413 414static void formdata_iter_finalize(ant_t *js, ant_object_t *obj) { 415 if (!obj->extra_slots) return; 416 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 417 418 for (uint8_t i = 0; i < obj->extra_count; i++) { 419 if (entries[i].slot == SLOT_ITER_STATE && vtype(entries[i].value) == T_NUM) { 420 fd_iter_t *st = (fd_iter_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 421 free(st); 422 return; 423 }} 424} 425 426static ant_value_t make_formdata_iter(ant_t *js, ant_value_t fd_obj, int kind) { 427 fd_iter_t *st = calloc(1, sizeof(fd_iter_t)); 428 if (!st) return js_mkerr(js, "out of memory"); 429 st->kind = kind; 430 431 ant_value_t iter = js_mkobj(js); 432 js_set_proto_init(iter, g_formdata_iter_proto); 433 js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(st)); 434 js_set_slot_wb(js, iter, SLOT_DATA, fd_obj); 435 js_set_finalizer(iter, formdata_iter_finalize); 436 return iter; 437} 438 439static ant_value_t js_formdata_entries(ant_t *js, ant_value_t *args, int nargs) { 440 return make_formdata_iter(js, js->this_val, FD_ITER_ENTRIES); 441} 442 443static ant_value_t js_formdata_keys(ant_t *js, ant_value_t *args, int nargs) { 444 return make_formdata_iter(js, js->this_val, FD_ITER_KEYS); 445} 446 447static ant_value_t js_formdata_values(ant_t *js, ant_value_t *args, int nargs) { 448 return make_formdata_iter(js, js->this_val, FD_ITER_VALUES); 449} 450 451static ant_value_t js_formdata_ctor(ant_t *js, ant_value_t *args, int nargs) { 452 if (vtype(js->new_target) == T_UNDEF) 453 return js_mkerr_typed(js, JS_ERR_TYPE, "FormData constructor requires 'new'"); 454 if (nargs >= 1 && vtype(args[0]) != T_UNDEF) 455 return js_mkerr_typed(js, JS_ERR_TYPE, "FormData does not support a form element argument"); 456 457 fd_data_t *d = fd_data_new(); 458 if (!d) return js_mkerr(js, "out of memory"); 459 460 ant_value_t obj = js_mkobj(js); 461 ant_value_t proto = js_instance_proto_from_new_target(js, g_formdata_proto); 462 463 if (is_object_type(proto)) js_set_proto_init(obj, proto); 464 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FORMDATA)); 465 js_set_slot(obj, SLOT_DATA, ANT_PTR(d)); 466 467 ant_value_t vals = js_mkarr(js); 468 js_set_slot_wb(js, obj, SLOT_ENTRIES, vals); 469 js_set_finalizer(obj, formdata_finalize); 470 471 return obj; 472} 473 474void init_formdata_module(void) { 475 ant_t *js = rt->js; 476 ant_value_t g = js_glob(js); 477 g_formdata_proto = js_mkobj(js); 478 479 js_set(js, g_formdata_proto, "append", js_mkfun(js_formdata_append)); 480 js_set(js, g_formdata_proto, "set", js_mkfun(js_formdata_set)); 481 js_set(js, g_formdata_proto, "get", js_mkfun(js_formdata_get)); 482 js_set(js, g_formdata_proto, "getAll", js_mkfun(js_formdata_get_all)); 483 js_set(js, g_formdata_proto, "has", js_mkfun(js_formdata_has)); 484 js_set(js, g_formdata_proto, "delete", js_mkfun(js_formdata_delete)); 485 js_set(js, g_formdata_proto, "forEach", js_mkfun(js_formdata_foreach)); 486 js_set(js, g_formdata_proto, "entries", js_mkfun(js_formdata_entries)); 487 js_set(js, g_formdata_proto, "keys", js_mkfun(js_formdata_keys)); 488 js_set(js, g_formdata_proto, "values", js_mkfun(js_formdata_values)); 489 490 js_set_sym(js, g_formdata_proto, get_iterator_sym(), js_get(js, g_formdata_proto, "entries")); 491 js_set_sym(js, g_formdata_proto, get_toStringTag_sym(), js_mkstr(js, "FormData", 8)); 492 493 ant_value_t ctor_obj = js_mkobj(js); 494 js_set_slot(ctor_obj, SLOT_CFUNC, js_mkfun(js_formdata_ctor)); 495 js_mkprop_fast(js, ctor_obj, "prototype", 9, g_formdata_proto); 496 js_mkprop_fast(js, ctor_obj, "name", 4, js_mkstr(js, "FormData", 8)); 497 js_set_descriptor(js, ctor_obj, "name", 4, 0); 498 499 ant_value_t ctor = js_obj_to_func(ctor_obj); 500 js_set(js, g_formdata_proto, "constructor", ctor); 501 js_set_descriptor(js, g_formdata_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 502 503 js_set(js, g, "FormData", ctor); 504 js_set_descriptor(js, g, "FormData", 8, JS_DESC_W | JS_DESC_C); 505 506 g_formdata_iter_proto = js_mkobj(js); 507 js_set_proto_init(g_formdata_iter_proto, js->sym.iterator_proto); 508 js_set(js, g_formdata_iter_proto, "next", js_mkfun(formdata_iter_next)); 509 js_set_descriptor(js, g_formdata_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); 510 js_set_sym(js, g_formdata_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb)); 511}