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