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 459 lines 16 kB view raw
1#include <stdlib.h> 2#include <string.h> 3#include <stdint.h> 4#include <inttypes.h> 5#include <time.h> 6#include <ctype.h> 7#include <stdio.h> 8 9#include "ant.h" 10#include "errors.h" 11#include "runtime.h" 12#include "internal.h" 13#include "descriptors.h" 14 15#include "modules/blob.h" 16#include "modules/buffer.h" 17#include "modules/symbol.h" 18#include "streams/readable.h" 19 20ant_value_t g_blob_proto = 0; 21ant_value_t g_file_proto = 0; 22 23bool blob_is_blob(ant_t *js, ant_value_t obj) { 24 int id = js_brand_id(obj); 25 return id == BRAND_BLOB || id == BRAND_FILE; 26} 27 28blob_data_t *blob_get_data(ant_value_t obj) { 29 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 30 if (vtype(slot) != T_NUM) return NULL; 31 return (blob_data_t *)(uintptr_t)(size_t)js_getnum(slot); 32} 33 34static blob_data_t *blob_data_new(const uint8_t *data, size_t size, const char *type) { 35 blob_data_t *bd = calloc(1, sizeof(blob_data_t)); 36 37 if (!bd) return NULL; 38 if (size > 0 && data) { 39 bd->data = malloc(size); 40 if (!bd->data) { free(bd); return NULL; } 41 memcpy(bd->data, data, size); 42 } 43 44 bd->size = size; 45 bd->type = type ? strdup(type) : strdup(""); 46 47 return bd; 48} 49 50static char *normalize_mime_type(const char *s) { 51 if (!s) return strdup(""); 52 for (const unsigned char *p = (const unsigned char *)s; *p; p++) { 53 if (*p < 0x20 || *p > 0x7E) return strdup(""); 54 } 55 56 size_t len = strlen(s); 57 char *out = malloc(len + 1); 58 59 if (!out) return strdup(""); 60 for (size_t i = 0; i <= len; i++) out[i] = (char)tolower((unsigned char)s[i]); 61 62 return out; 63} 64 65typedef struct { 66 uint8_t *buf; 67 size_t size; 68 size_t cap; 69} byte_buf_t; 70 71static bool byte_buf_grow(byte_buf_t *b, size_t extra) { 72 size_t needed = b->size + extra; 73 if (needed <= b->cap) return true; 74 size_t new_cap = b->cap ? b->cap * 2 : 64; 75 while (new_cap < needed) new_cap *= 2; 76 uint8_t *p = realloc(b->buf, new_cap); 77 if (!p) return false; 78 b->buf = p; 79 b->cap = new_cap; 80 return true; 81} 82 83static bool byte_buf_append(byte_buf_t *b, const uint8_t *data, size_t len) { 84 if (!byte_buf_grow(b, len)) return false; 85 memcpy(b->buf + b->size, data, len); 86 b->size += len; 87 return true; 88} 89 90static ant_value_t process_blob_part(ant_t *js, byte_buf_t *buf, ant_value_t part) { 91 uint8_t t = vtype(part); 92 93 if (t == T_TYPEDARRAY) { 94 TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(part); 95 if (!ta || !ta->buffer) return js_mkundef(); 96 if (!byte_buf_append(buf, ta->buffer->data + ta->byte_offset, ta->byte_length)) 97 return js_mkerr(js, "out of memory"); 98 return js_mkundef(); 99 } 100 101 if (t == T_OBJ) { 102 TypedArrayData *ta = buffer_get_typedarray_data(part); 103 if (ta && ta->buffer && !ta->buffer->is_detached) { 104 if (!byte_buf_append(buf, ta->buffer->data + ta->byte_offset, ta->byte_length)) return js_mkerr(js, "out of memory"); 105 return js_mkundef(); 106 } 107 ArrayBufferData *abd = buffer_get_arraybuffer_data(part); 108 if (abd && !abd->is_detached) { 109 if (!byte_buf_append(buf, abd->data, abd->length)) return js_mkerr(js, "out of memory"); 110 return js_mkundef(); 111 } 112 blob_data_t *bd = blob_get_data(part); 113 if (bd && bd->size > 0) { 114 if (!byte_buf_append(buf, bd->data, bd->size)) return js_mkerr(js, "out of memory"); 115 return js_mkundef(); 116 } 117 } 118 119 ant_value_t str = (t == T_STR) ? part : js_tostring_val(js, part); 120 if (is_err(str)) return str; 121 122 size_t len; 123 char *s = js_getstr(js, str, &len); 124 if (s && len > 0) { 125 if (!byte_buf_append(buf, (const uint8_t *)s, len)) 126 return js_mkerr(js, "out of memory"); 127 } 128 return js_mkundef(); 129} 130 131static ant_value_t process_blob_parts(ant_t *js, byte_buf_t *buf, ant_value_t parts) { 132 uint8_t t = vtype(parts); 133 if (t == T_UNDEF || t == T_NULL) return js_mkundef(); 134 135 if (t != T_OBJ && t != T_ARR && t != T_FUNC) 136 return js_mkerr_typed(js, JS_ERR_TYPE, 137 "Failed to construct 'Blob': The provided value cannot be converted to a sequence."); 138 139 js_iter_t it; 140 if (!js_iter_open(js, parts, &it)) 141 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'Blob': The provided value is not of type 'BlobPart'"); 142 143 ant_value_t value; 144 while (js_iter_next(js, &it, &value)) { 145 ant_value_t r = process_blob_part(js, buf, value); 146 if (is_err(r)) { js_iter_close(js, &it); return r; } 147 } 148 149 return js_mkundef(); 150} 151 152static void blob_finalize(ant_t *js, ant_object_t *obj) { 153 if (!obj->extra_slots) return; 154 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 155 for (uint8_t i = 0; i < obj->extra_count; i++) { 156 if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 157 blob_data_t *bd = (blob_data_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 158 if (bd) { free(bd->data); free(bd->type); free(bd->name); free(bd); } 159 return; 160 }} 161} 162 163ant_value_t blob_create(ant_t *js, const uint8_t *data, size_t size, const char *type) { 164 blob_data_t *bd = blob_data_new(data, size, type); 165 if (!bd) return js_mkerr(js, "out of memory"); 166 ant_value_t obj = js_mkobj(js); 167 168 js_set_proto_init(obj, g_blob_proto); 169 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_BLOB)); 170 js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 171 js_set_finalizer(obj, blob_finalize); 172 173 return obj; 174} 175 176static ant_value_t blob_get_size(ant_t *js, ant_value_t *args, int nargs) { 177 (void)args; (void)nargs; 178 blob_data_t *bd = blob_get_data(js->this_val); 179 return js_mknum(bd ? (double)bd->size : 0); 180} 181 182static ant_value_t blob_get_type(ant_t *js, ant_value_t *args, int nargs) { 183 (void)args; (void)nargs; 184 blob_data_t *bd = blob_get_data(js->this_val); 185 if (!bd || !bd->type) return js_mkstr(js, "", 0); 186 return js_mkstr(js, bd->type, strlen(bd->type)); 187} 188 189static ant_value_t file_get_name(ant_t *js, ant_value_t *args, int nargs) { 190 (void)args; (void)nargs; 191 blob_data_t *bd = blob_get_data(js->this_val); 192 if (!bd || !bd->name) return js_mkstr(js, "", 0); 193 return js_mkstr(js, bd->name, strlen(bd->name)); 194} 195 196static ant_value_t file_get_last_modified(ant_t *js, ant_value_t *args, int nargs) { 197 (void)args; (void)nargs; 198 blob_data_t *bd = blob_get_data(js->this_val); 199 return js_mknum(bd ? (double)bd->last_modified : 0); 200} 201 202static ant_value_t js_blob_text(ant_t *js, ant_value_t *args, int nargs) { 203 (void)args; (void)nargs; 204 blob_data_t *bd = blob_get_data(js->this_val); 205 ant_value_t promise = js_mkpromise(js); 206 ant_value_t str = (!bd || bd->size == 0) 207 ? js_mkstr(js, "", 0) 208 : js_mkstr(js, (const char *)bd->data, bd->size); 209 js_resolve_promise(js, promise, str); 210 return promise; 211} 212 213static ant_value_t js_blob_array_buffer(ant_t *js, ant_value_t *args, int nargs) { 214 (void)args; (void)nargs; 215 blob_data_t *bd = blob_get_data(js->this_val); 216 ant_value_t promise = js_mkpromise(js); 217 218 size_t sz = (bd && bd->data) ? bd->size : 0; 219 ArrayBufferData *abd = create_array_buffer_data(sz); 220 if (!abd) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); return promise; } 221 if (sz > 0 && bd) memcpy(abd->data, bd->data, sz); 222 223 js_resolve_promise(js, promise, create_arraybuffer_obj(js, abd)); 224 return promise; 225} 226 227static ant_value_t js_blob_bytes(ant_t *js, ant_value_t *args, int nargs) { 228 (void)args; (void)nargs; 229 blob_data_t *bd = blob_get_data(js->this_val); 230 ant_value_t promise = js_mkpromise(js); 231 232 size_t sz = (bd && bd->data) ? bd->size : 0; 233 ArrayBufferData *abd = create_array_buffer_data(sz); 234 if (!abd) { js_reject_promise(js, promise, js_mkerr(js, "out of memory")); return promise; } 235 if (sz > 0 && bd) memcpy(abd->data, bd->data, sz); 236 237 js_resolve_promise(js, promise, 238 create_typed_array(js, TYPED_ARRAY_UINT8, abd, 0, sz, "Uint8Array")); 239 return promise; 240} 241 242static ant_value_t js_blob_slice(ant_t *js, ant_value_t *args, int nargs) { 243 blob_data_t *bd = blob_get_data(js->this_val); 244 size_t blob_size = bd ? bd->size : 0; 245 246 ssize_t start = 0; 247 if (nargs >= 1 && vtype(args[0]) != T_UNDEF) { 248 double d = js_to_number(js, args[0]); 249 start = (ssize_t)d; 250 if (start < 0) start = (ssize_t)blob_size + start; 251 if (start < 0) start = 0; 252 if ((size_t)start > blob_size) start = (ssize_t)blob_size; 253 } 254 255 ssize_t end = (ssize_t)blob_size; 256 if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { 257 double d = js_to_number(js, args[1]); 258 end = (ssize_t)d; 259 if (end < 0) end = (ssize_t)blob_size + end; 260 if (end < 0) end = 0; 261 if ((size_t)end > blob_size) end = (ssize_t)blob_size; 262 } 263 264 if (end < start) end = start; 265 266 size_t new_size = (size_t)(end - start); 267 const uint8_t *src = (bd && bd->data && new_size > 0) ? (bd->data + start) : NULL; 268 269 const char *new_type = (bd && bd->type) ? bd->type : ""; 270 char *type_owned = NULL; 271 if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { 272 ant_value_t tv = args[2]; 273 if (vtype(tv) != T_STR) { tv = js_tostring_val(js, tv); if (is_err(tv)) return tv; } 274 type_owned = normalize_mime_type(js_getstr(js, tv, NULL)); 275 new_type = type_owned; 276 } 277 278 ant_value_t result = blob_create(js, src, new_size, new_type); 279 free(type_owned); 280 return result; 281} 282 283static ant_value_t blob_stream_pull(ant_t *js, ant_value_t *args, int nargs) { 284 ant_value_t blob_obj = js_get_slot(js->current_func, SLOT_DATA); 285 blob_data_t *bd = blob_get_data(blob_obj); 286 ant_value_t ctrl = (nargs > 0) ? args[0] : js_mkundef(); 287 288 if (bd && bd->size > 0 && bd->data) { 289 ArrayBufferData *ab = create_array_buffer_data(bd->size); 290 if (ab) { 291 memcpy(ab->data, bd->data, bd->size); 292 rs_controller_enqueue(js, ctrl, create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, bd->size, "Uint8Array")); 293 }} 294 295 rs_controller_close(js, ctrl); 296 return js_mkundef(); 297} 298 299static ant_value_t js_blob_stream(ant_t *js, ant_value_t *args, int nargs) { 300 ant_value_t pull_fn = js_heavy_mkfun(js, blob_stream_pull, js->this_val); 301 return rs_create_stream(js, pull_fn, js_mkundef(), 1); 302} 303 304static ant_value_t js_blob_ctor(ant_t *js, ant_value_t *args, int nargs) { 305 if (vtype(js->new_target) == T_UNDEF) 306 return js_mkerr_typed(js, JS_ERR_TYPE, "Blob constructor requires 'new'"); 307 308 byte_buf_t buf = {NULL, 0, 0}; 309 310 if (nargs >= 1 && vtype(args[0]) != T_UNDEF) { 311 uint8_t pt = vtype(args[0]); 312 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) 313 return js_mkerr_typed(js, JS_ERR_TYPE, 314 "Failed to construct 'Blob': The provided value cannot be converted to a sequence."); 315 ant_value_t r = process_blob_parts(js, &buf, args[0]); 316 if (is_err(r)) { free(buf.buf); return r; } 317 } 318 319 const char *type_str = ""; 320 char *type_owned = NULL; 321 322 if (nargs >= 2 && vtype(args[1]) != T_UNDEF && vtype(args[1]) != T_NULL) { 323 uint8_t ot = vtype(args[1]); 324 if (ot != T_OBJ && ot != T_ARR && ot != T_FUNC && ot != T_CFUNC) { 325 free(buf.buf); 326 return js_mkerr_typed(js, JS_ERR_TYPE, 327 "Failed to construct 'Blob': The 'options' argument is not an object."); 328 } 329 // access "endings" before "type" per lexicographic order (WPT requirement) 330 (void)js_get(js, args[1], "endings"); 331 ant_value_t type_v = js_get(js, args[1], "type"); 332 if (vtype(type_v) != T_UNDEF) { 333 if (vtype(type_v) != T_STR) { 334 type_v = js_tostring_val(js, type_v); 335 if (is_err(type_v)) { free(buf.buf); return type_v; } 336 } 337 type_owned = normalize_mime_type(js_getstr(js, type_v, NULL)); 338 type_str = type_owned; 339 } 340 } 341 342 blob_data_t *bd = blob_data_new(buf.buf, buf.size, type_str); 343 free(buf.buf); free(type_owned); 344 if (!bd) return js_mkerr(js, "out of memory"); 345 346 ant_value_t obj = js_mkobj(js); 347 ant_value_t proto = js_instance_proto_from_new_target(js, g_blob_proto); 348 if (is_object_type(proto)) js_set_proto_init(obj, proto); 349 350 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_BLOB)); 351 js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 352 js_set_finalizer(obj, blob_finalize); 353 354 return obj; 355} 356 357static ant_value_t js_file_ctor(ant_t *js, ant_value_t *args, int nargs) { 358 if (vtype(js->new_target) == T_UNDEF) 359 return js_mkerr_typed(js, JS_ERR_TYPE, "File constructor requires 'new'"); 360 if (nargs < 2) 361 return js_mkerr_typed(js, JS_ERR_TYPE, "File constructor requires at least 2 arguments"); 362 363 byte_buf_t buf = {NULL, 0, 0}; 364 365 if (vtype(args[0]) != T_UNDEF) { 366 uint8_t pt = vtype(args[0]); 367 if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) { 368 return js_mkerr_typed(js, JS_ERR_TYPE, 369 "Failed to construct 'File': The provided value cannot be converted to a sequence."); 370 } 371 ant_value_t r = process_blob_parts(js, &buf, args[0]); 372 if (is_err(r)) { free(buf.buf); return r; } 373 } 374 375 ant_value_t name_v = args[1]; 376 if (vtype(name_v) != T_STR) { 377 name_v = js_tostring_val(js, name_v); 378 if (is_err(name_v)) { free(buf.buf); return name_v; } 379 } 380 const char *name_str = js_getstr(js, name_v, NULL); 381 382 const char *type_str = ""; 383 char *type_owned = NULL; 384 385 struct timespec ts; 386 clock_gettime(CLOCK_REALTIME, &ts); 387 int64_t last_modified = (int64_t)ts.tv_sec * 1000LL + (int64_t)(ts.tv_nsec / 1000000); 388 389 if (nargs >= 3 && vtype(args[2]) != T_UNDEF && vtype(args[2]) != T_NULL) { 390 ant_value_t opts = args[2]; 391 uint8_t ot = vtype(opts); 392 if (ot == T_OBJ || ot == T_ARR) { 393 ant_value_t type_v = js_get(js, opts, "type"); 394 if (vtype(type_v) != T_UNDEF) { 395 if (vtype(type_v) != T_STR) { 396 type_v = js_tostring_val(js, type_v); 397 if (is_err(type_v)) { free(buf.buf); return type_v; } 398 } 399 type_owned = normalize_mime_type(js_getstr(js, type_v, NULL)); 400 type_str = type_owned; 401 } 402 ant_value_t lm_v = js_get(js, opts, "lastModified"); 403 if (vtype(lm_v) != T_UNDEF) { 404 double d = js_to_number(js, lm_v); 405 if (d == d) last_modified = (int64_t)d; 406 } 407 } 408 } 409 410 blob_data_t *bd = blob_data_new(buf.buf, buf.size, type_str); 411 free(buf.buf); free(type_owned); 412 if (!bd) return js_mkerr(js, "out of memory"); 413 414 bd->name = strdup(name_str ? name_str : ""); 415 bd->last_modified = last_modified; 416 417 ant_value_t obj = js_mkobj(js); 418 ant_value_t proto = js_instance_proto_from_new_target(js, g_file_proto); 419 if (is_object_type(proto)) js_set_proto_init(obj, proto); 420 421 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_FILE)); 422 js_set_slot(obj, SLOT_DATA, ANT_PTR(bd)); 423 js_set_finalizer(obj, blob_finalize); 424 425 return obj; 426} 427 428void init_blob_module(void) { 429 ant_t *js = rt->js; 430 ant_value_t g = js_glob(js); 431 g_blob_proto = js_mkobj(js); 432 433 js_set_getter_desc(js, g_blob_proto, "size", 4, js_mkfun(blob_get_size), JS_DESC_C); 434 js_set_getter_desc(js, g_blob_proto, "type", 4, js_mkfun(blob_get_type), JS_DESC_C); 435 436 js_set(js, g_blob_proto, "text", js_mkfun(js_blob_text)); 437 js_set(js, g_blob_proto, "arrayBuffer", js_mkfun(js_blob_array_buffer)); 438 js_set(js, g_blob_proto, "bytes", js_mkfun(js_blob_bytes)); 439 js_set(js, g_blob_proto, "slice", js_mkfun(js_blob_slice)); 440 js_set(js, g_blob_proto, "stream", js_mkfun(js_blob_stream)); 441 442 js_set_sym(js, g_blob_proto, get_toStringTag_sym(), js_mkstr(js, "Blob", 4)); 443 ant_value_t blob_ctor = js_make_ctor(js, js_blob_ctor, g_blob_proto, "Blob", 4); 444 445 js_set(js, g, "Blob", blob_ctor); 446 js_set_descriptor(js, g, "Blob", 4, JS_DESC_W | JS_DESC_C); 447 448 g_file_proto = js_mkobj(js); 449 js_set_proto_init(g_file_proto, g_blob_proto); 450 451 js_set_getter_desc(js, g_file_proto, "name", 4, js_mkfun(file_get_name), JS_DESC_C); 452 js_set_getter_desc(js, g_file_proto, "lastModified", 12, js_mkfun(file_get_last_modified), JS_DESC_C); 453 454 js_set_sym(js, g_file_proto, get_toStringTag_sym(), js_mkstr(js, "File", 4)); 455 ant_value_t file_ctor = js_make_ctor(js, js_file_ctor, g_file_proto, "File", 4); 456 457 js_set(js, g, "File", file_ctor); 458 js_set_descriptor(js, g, "File", 4, JS_DESC_W | JS_DESC_C); 459}