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 1790 lines 59 kB view raw
1#include <compat.h> // IWYU pragma: keep 2 3#include <stdlib.h> 4#include <stdio.h> 5#include <string.h> 6#include <ctype.h> 7#include <uriparser/Uri.h> 8 9#include "ant.h" 10#include "errors.h" 11#include "internal.h" 12#include "runtime.h" 13#include "descriptors.h" 14 15#include "silver/engine.h" 16#include "modules/url.h" 17#include "modules/symbol.h" 18 19static ant_value_t g_url_proto = 0; 20static ant_value_t g_usp_proto = 0; 21static ant_value_t g_usp_iter_proto = 0; 22 23enum { 24 USP_ITER_ENTRIES = 0, 25 USP_ITER_KEYS = 1, 26 USP_ITER_VALUES = 2 27}; 28 29url_state_t *url_get_state(ant_value_t obj) { 30 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 31 if (vtype(slot) != T_NUM) return NULL; 32 return (url_state_t *)(uintptr_t)(size_t)js_getnum(slot); 33} 34 35bool usp_is_urlsearchparams(ant_t *js, ant_value_t obj) { 36 return js_check_brand(obj, BRAND_URLSEARCHPARAMS); 37} 38 39void url_state_clear(url_state_t *s) { 40 free(s->protocol); free(s->username); free(s->password); 41 free(s->hostname); free(s->port); free(s->pathname); 42 free(s->search); free(s->hash); 43} 44 45void url_free_state(url_state_t *s) { 46 if (!s) return; 47 url_state_clear(s); 48 free(s); 49} 50 51static void url_finalize(ant_t *js, ant_object_t *obj) { 52 if (!obj->extra_slots) return; 53 ant_extra_slot_t *slots = (ant_extra_slot_t *)obj->extra_slots; 54 for (uint8_t i = 0; i < obj->extra_count; i++) { 55 if (slots[i].slot == SLOT_DATA && vtype(slots[i].value) == T_NUM) { 56 url_free_state((url_state_t *)(uintptr_t)(size_t)js_getnum(slots[i].value)); 57 return; 58 }} 59} 60 61static int default_port_for(const char *proto) { 62 if (!proto) return -1; 63 if (strcmp(proto, "http:") == 0 || strcmp(proto, "ws:") == 0) return 80; 64 if (strcmp(proto, "https:") == 0 || strcmp(proto, "wss:") == 0) return 443; 65 if (strcmp(proto, "ftp:") == 0) return 21; 66 return -1; 67} 68 69static bool is_special_scheme(const char *proto) { 70 if (!proto) return false; 71 return 72 strcmp(proto, "http:") == 0 || strcmp(proto, "https:") == 0 || 73 strcmp(proto, "ftp:") == 0 || strcmp(proto, "ws:") == 0 || 74 strcmp(proto, "wss:") == 0; 75} 76 77static bool uses_authority_syntax(const char *proto) { 78 if (!proto) return false; 79 return is_special_scheme(proto) || strcmp(proto, "file:") == 0; 80} 81 82static bool url_base_is_opaque(const char *base_str, const char *proto) { 83 const char *after_colon = NULL; 84 85 if (!base_str || is_special_scheme(proto)) return false; 86 after_colon = strchr(base_str, ':'); 87 if (!after_colon) return false; 88 after_colon++; 89 90 return *after_colon != '/' && *after_colon != '\0'; 91} 92 93char *form_urlencode_n(const char *str, size_t len) { 94 if (!str) return strdup(""); 95 char *out = malloc(len * 3 + 1); 96 97 if (!out) return strdup(""); 98 size_t j = 0; 99 100 for (size_t i = 0; i < len; i++) { 101 unsigned char c = (unsigned char)str[i]; 102 if (isalnum(c) || c == '*' || c == '-' || c == '.' || c == '_') out[j++] = (char)c; 103 else if (c == ' ') out[j++] = '+'; 104 else { 105 snprintf(out + j, 4, "%%%02X", c); 106 j += 3; 107 }} 108 109 out[j] = '\0'; 110 return out; 111} 112 113char *form_urlencode(const char *str) { 114 if (!str) return strdup(""); 115 return form_urlencode_n(str, strlen(str)); 116} 117 118char *form_urldecode(const char *str) { 119 if (!str) return strdup(""); 120 size_t len = strlen(str); 121 char *out = malloc(len + 1); 122 123 if (!out) return strdup(""); 124 size_t j = 0; 125 126 for (size_t i = 0; i < len; i++) { 127 if (str[i] == '+') out[j++] = ' '; 128 else if ( 129 str[i] == '%' && i + 2 < len && 130 isxdigit((unsigned char)str[i+1]) && 131 isxdigit((unsigned char)str[i+2]) 132 ) { 133 int hi = isdigit((unsigned char)str[i+1]) ? str[i+1]-'0' : tolower((unsigned char)str[i+1])-'a'+10; 134 int lo = isdigit((unsigned char)str[i+2]) ? str[i+2]-'0' : tolower((unsigned char)str[i+2])-'a'+10; 135 out[j++] = (char)((hi << 4) | lo); 136 i += 2; 137 } else out[j++] = str[i]; 138 } 139 140 out[j] = '\0'; 141 return out; 142} 143 144char *url_decode_component(const char *str) { 145 if (!str) return strdup(""); 146 size_t len = strlen(str); 147 char *out = malloc(len + 1); 148 149 if (!out) return strdup(""); 150 size_t j = 0; 151 152 for (size_t i = 0; i < len; i++) { 153 if ( 154 str[i] == '%' && i + 2 < len && 155 isxdigit((unsigned char)str[i+1]) && 156 isxdigit((unsigned char)str[i+2]) 157 ) { 158 int hi = isdigit((unsigned char)str[i+1]) ? str[i+1]-'0' : tolower((unsigned char)str[i+1])-'a'+10; 159 int lo = isdigit((unsigned char)str[i+2]) ? str[i+2]-'0' : tolower((unsigned char)str[i+2])-'a'+10; 160 out[j++] = (char)((hi << 4) | lo); 161 i += 2; 162 } else out[j++] = str[i]; 163 } 164 165 out[j] = '\0'; 166 return out; 167} 168 169static char *userinfo_encode(const char *str) { 170 if (!str) return strdup(""); 171 size_t len = strlen(str); 172 char *out = malloc(len * 3 + 1); 173 174 if (!out) return strdup(""); 175 size_t j = 0; 176 177 for (size_t i = 0; i < len; i++) { 178 unsigned char c = (unsigned char)str[i]; 179 if ( 180 isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~' || 181 c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' || 182 c == ')' || c == '*' || c == '+' || c == ',' || c == ';' || 183 c == '=' || c == ':' 184 ) out[j++] = (char)c; else { snprintf(out + j, 4, "%%%02X", c); j += 3; } 185 } 186 187 out[j] = '\0'; 188 return out; 189} 190 191static char *uri_range_dup(const UriTextRangeA *r) { 192 if (!r->first || !r->afterLast) return strdup(""); 193 return strndup(r->first, (size_t)(r->afterLast - r->first)); 194} 195 196static bool url_has_brackets_in_query_or_fragment(const char *url_str) { 197 size_t len = strlen(url_str); 198 bool in_query = false; 199 bool in_fragment = false; 200 201 for (size_t i = 0; i < len; i++) { 202 char c = url_str[i]; 203 if (c == '#' && !in_fragment) { 204 in_query = false; 205 in_fragment = true; 206 continue; 207 } 208 if (c == '?' && !in_query && !in_fragment) { 209 in_query = true; 210 continue; 211 } 212 if ((in_query || in_fragment) && (c == '[' || c == ']')) return true; 213 } 214 215 return false; 216} 217 218static char *url_escape_brackets_in_query_or_fragment(const char *url_str) { 219 size_t len = strlen(url_str); 220 size_t extra = 0; 221 bool in_query = false; 222 bool in_fragment = false; 223 224 for (size_t i = 0; i < len; i++) { 225 char c = url_str[i]; 226 if (c == '#' && !in_fragment) { 227 in_query = false; 228 in_fragment = true; 229 continue; 230 } 231 if (c == '?' && !in_query && !in_fragment) { 232 in_query = true; 233 continue; 234 } 235 if ((in_query || in_fragment) && (c == '[' || c == ']')) extra += 2; 236 } 237 238 char *escaped = malloc(len + extra + 1); 239 size_t pos = 0; 240 in_query = false; 241 in_fragment = false; 242 243 if (!escaped) return NULL; 244 245 for (size_t i = 0; i < len; i++) { 246 char c = url_str[i]; 247 if (c == '#' && !in_fragment) { 248 in_query = false; 249 in_fragment = true; 250 escaped[pos++] = c; 251 continue; 252 } 253 if (c == '?' && !in_query && !in_fragment) { 254 in_query = true; 255 escaped[pos++] = c; 256 continue; 257 } 258 if ((in_query || in_fragment) && c == '[') { 259 memcpy(escaped + pos, "%5B", 3); 260 pos += 3; 261 continue; 262 } 263 if ((in_query || in_fragment) && c == ']') { 264 memcpy(escaped + pos, "%5D", 3); 265 pos += 3; 266 continue; 267 } 268 escaped[pos++] = c; 269 } 270 271 escaped[pos] = '\0'; 272 return escaped; 273} 274 275static int url_parse_single_uri_relaxed( 276 UriUriA *uri, 277 const char *url_str, 278 const char **errpos, 279 char **owned_input_out, 280 bool *used_relaxed_out 281) { 282 char *escaped = NULL; 283 284 if (owned_input_out) *owned_input_out = NULL; 285 if (used_relaxed_out) *used_relaxed_out = false; 286 if (uriParseSingleUriA(uri, url_str, errpos) == URI_SUCCESS) return 0; 287 288 if (!url_has_brackets_in_query_or_fragment(url_str)) return -1; 289 escaped = url_escape_brackets_in_query_or_fragment(url_str); 290 291 if (!escaped) return -1; 292 if (owned_input_out) *owned_input_out = escaped; 293 if (used_relaxed_out) *used_relaxed_out = true; 294 295 return uriParseSingleUriA(uri, escaped, errpos) == URI_SUCCESS ? 0 : -1; 296} 297 298static void url_override_search_hash_from_input(url_state_t *s, const char *url_str) { 299 const char *hash = strchr(url_str, '#'); 300 const char *query = strchr(url_str, '?'); 301 size_t search_len = 0; 302 size_t hash_len = 0; 303 304 if (query && hash && hash < query) query = NULL; 305 306 if (query) { 307 const char *search_end = hash && hash > query ? hash : url_str + strlen(url_str); 308 search_len = (size_t)(search_end - query); 309 } 310 if (hash) hash_len = strlen(hash); 311 312 free(s->search); 313 s->search = search_len > 0 ? strndup(query, search_len) : strdup(""); 314 315 free(s->hash); 316 s->hash = hash_len > 0 ? strndup(hash, hash_len) : strdup(""); 317} 318 319static void url_override_search_hash_from_reference(url_state_t *s, const char *url_str) { 320 const char *hash = strchr(url_str, '#'); 321 const char *query = strchr(url_str, '?'); 322 size_t search_len = 0; 323 size_t hash_len = 0; 324 325 if (query && hash && hash < query) query = NULL; 326 327 if (query) { 328 const char *search_end = hash && hash > query ? hash : url_str + strlen(url_str); 329 search_len = (size_t)(search_end - query); 330 free(s->search); 331 s->search = strndup(query, search_len); 332 } 333 334 if (hash) { 335 hash_len = strlen(hash); 336 free(s->hash); 337 s->hash = strndup(hash, hash_len); 338 } 339} 340 341static void uri_to_state(const UriUriA *uri, url_state_t *s) { 342 char *scheme = uri_range_dup(&uri->scheme); 343 size_t slen = strlen(scheme); 344 for (size_t i = 0; i < slen; i++) scheme[i] = (char)tolower((unsigned char)scheme[i]); 345 346 s->protocol = malloc(slen + 2); 347 memcpy(s->protocol, scheme, slen); 348 s->protocol[slen] = ':'; 349 s->protocol[slen + 1] = '\0'; 350 free(scheme); 351 352 char *userinfo = uri_range_dup(&uri->userInfo); 353 char *colon = strchr(userinfo, ':'); 354 355 if (colon) { 356 *colon = '\0'; 357 s->username = strdup(userinfo); 358 s->password = strdup(colon + 1); 359 } else { 360 s->username = strdup(userinfo); 361 s->password = strdup(""); 362 } 363 364 free(userinfo); 365 s->hostname = uri_range_dup(&uri->hostText); 366 367 char *port = uri_range_dup(&uri->portText); 368 int def = default_port_for(s->protocol); 369 if (def > 0 && *port && atoi(port) == def) { 370 free(port); 371 port = strdup(""); 372 } s->port = port; 373 374 size_t path_cap = 2; 375 for (UriPathSegmentA *seg = uri->pathHead; seg; seg = seg->next) 376 path_cap += (size_t)(seg->text.afterLast - seg->text.first) + 1; 377 378 char *path = malloc(path_cap + 1); 379 size_t pos = 0; 380 381 for (UriPathSegmentA *seg = uri->pathHead; seg; seg = seg->next) { 382 path[pos++] = '/'; 383 size_t seglen = (size_t)(seg->text.afterLast - seg->text.first); 384 memcpy(path + pos, seg->text.first, seglen); 385 pos += seglen; 386 } 387 388 if (pos == 0) path[pos++] = '/'; 389 path[pos] = '\0'; 390 s->pathname = path; 391 392 char *query = uri_range_dup(&uri->query); 393 if (*query) { 394 size_t qlen = strlen(query); 395 s->search = malloc(qlen + 2); 396 s->search[0] = '?'; 397 memcpy(s->search + 1, query, qlen + 1); 398 } else s->search = strdup(""); 399 free(query); 400 401 char *frag = uri_range_dup(&uri->fragment); 402 if (*frag) { 403 size_t flen = strlen(frag); 404 s->hash = malloc(flen + 2); 405 s->hash[0] = '#'; 406 memcpy(s->hash + 1, frag, flen + 1); 407 } else s->hash = strdup(""); 408 free(frag); 409} 410 411int parse_url_to_state(const char *url_str, const char *base_str, url_state_t *s) { 412 memset(s, 0, sizeof(*s)); 413 const char *errpos; 414 415 if (base_str) { 416 UriUriA base_uri, ref_uri, resolved; 417 char *escaped_base = NULL; 418 char *escaped_ref = NULL; 419 bool used_relaxed_ref_parse = false; 420 421 if (url_parse_single_uri_relaxed(&base_uri, base_str, &errpos, &escaped_base, NULL) != 0) { 422 free(escaped_base); 423 return -1; 424 } 425 426 char *base_scheme = uri_range_dup(&base_uri.scheme); 427 size_t bslen = strlen(base_scheme); 428 for (size_t i = 0; i < bslen; i++) base_scheme[i] = (char)tolower((unsigned char)base_scheme[i]); 429 430 char proto_buf[bslen + 2]; 431 memcpy(proto_buf, base_scheme, bslen); 432 proto_buf[bslen] = ':'; 433 proto_buf[bslen + 1] = '\0'; 434 free(base_scheme); 435 436 if (url_base_is_opaque(base_str, proto_buf)) { 437 uriFreeUriMembersA(&base_uri); 438 free(escaped_base); 439 return -1; 440 } 441 442 if (url_parse_single_uri_relaxed(&ref_uri, url_str, &errpos, &escaped_ref, &used_relaxed_ref_parse) != 0) { 443 uriFreeUriMembersA(&base_uri); 444 free(escaped_base); 445 free(escaped_ref); 446 return -1; 447 } 448 449 if (uriAddBaseUriA(&resolved, &ref_uri, &base_uri) != URI_SUCCESS) { 450 uriFreeUriMembersA(&base_uri); 451 uriFreeUriMembersA(&ref_uri); 452 free(escaped_base); 453 free(escaped_ref); 454 return -1; 455 } 456 457 uriNormalizeSyntaxA(&resolved); 458 if (!resolved.scheme.first || resolved.scheme.first == resolved.scheme.afterLast) { 459 uriFreeUriMembersA(&resolved); 460 uriFreeUriMembersA(&ref_uri); 461 uriFreeUriMembersA(&base_uri); 462 free(escaped_base); 463 free(escaped_ref); 464 return -1; 465 } 466 467 uri_to_state(&resolved, s); 468 if (used_relaxed_ref_parse) url_override_search_hash_from_reference(s, url_str); 469 uriFreeUriMembersA(&resolved); 470 uriFreeUriMembersA(&ref_uri); 471 uriFreeUriMembersA(&base_uri); 472 free(escaped_ref); 473 free(escaped_base); 474 475 return 0; 476 } 477 478 UriUriA uri; 479 char *escaped_url = NULL; 480 bool used_relaxed_query_parse = false; 481 482 if (url_parse_single_uri_relaxed(&uri, url_str, &errpos, &escaped_url, &used_relaxed_query_parse) != 0) { 483 free(escaped_url); 484 return -1; 485 } 486 487 if (!uri.scheme.first || uri.scheme.first == uri.scheme.afterLast) { 488 uriFreeUriMembersA(&uri); 489 free(escaped_url); 490 return -1; 491 } 492 493 uriNormalizeSyntaxA(&uri); 494 uri_to_state(&uri, s); 495 if (used_relaxed_query_parse) url_override_search_hash_from_input(s, url_str); 496 497 uriFreeUriMembersA(&uri); 498 free(escaped_url); 499 500 return 0; 501} 502 503char *build_href(const url_state_t *s) { 504 bool has_authority = 505 (s->hostname && *s->hostname) || 506 (s->username && *s->username) || 507 (s->password && *s->password) || 508 (s->port && *s->port); 509 510 bool opaque_like = !has_authority && !uses_authority_syntax(s->protocol); 511 const char *pathname = s->pathname ? s->pathname : ""; 512 size_t len = strlen(s->protocol) + strlen(pathname) + strlen(s->search) + strlen(s->hash) + 32; 513 514 if (has_authority) len += strlen(s->hostname) + 2; 515 if (s->username && *s->username) len += strlen(s->username) + 1; 516 if (s->password && *s->password) len += strlen(s->password) + 1; 517 if (s->port && *s->port) len += strlen(s->port) + 1; 518 519 char *href = malloc(len); 520 if (!href) return strdup(""); 521 522 size_t pos = 0; 523 pos += (size_t)sprintf(href + pos, "%s", s->protocol); 524 525 if (opaque_like) { 526 if (pathname[0] == '/') pathname++; 527 pos += (size_t)sprintf(href + pos, "%s%s%s", pathname, s->search, s->hash); 528 href[pos] = '\0'; 529 return href; 530 } 531 532 pos += (size_t)sprintf(href + pos, "//"); 533 if (s->username && *s->username) { 534 pos += (size_t)sprintf(href + pos, "%s", s->username); 535 if (s->password && *s->password) 536 pos += (size_t)sprintf(href + pos, ":%s", s->password); 537 href[pos++] = '@'; 538 } 539 540 pos += (size_t)sprintf(href + pos, "%s", s->hostname); 541 if (s->port && *s->port) 542 pos += (size_t)sprintf(href + pos, ":%s", s->port); 543 544 pos += (size_t)sprintf(href + pos, "%s%s%s", pathname, s->search, s->hash); 545 href[pos] = '\0'; 546 return href; 547} 548 549static const char *coerce_to_string(ant_t *js, ant_value_t val, size_t *len) { 550 if (vtype(val) == T_STR) return js_getstr(js, val, len); 551 if (is_object_type(val)) { 552 ant_value_t href = js_getprop_fallback(js, val, "href"); 553 if (vtype(href) == T_STR) return js_getstr(js, href, len); 554 } 555 return NULL; 556} 557 558static ant_value_t parse_query_to_arr(ant_t *js, const char *query) { 559 ant_value_t arr = js_mkarr(js); 560 if (!query || !*query) return arr; 561 const char *p = query; 562 563 while (*p) { 564 const char *amp = strchr(p, '&'); 565 size_t plen = amp ? (size_t)(amp - p) : strlen(p); 566 if (plen == 0) { p = amp ? amp + 1 : p + 1; continue; } 567 char *pair = strndup(p, plen); 568 if (!pair) { p = amp ? amp + 1 : p + plen; continue; } 569 570 char *eq = strchr(pair, '='); 571 char *raw_v = eq ? ((*eq = '\0'), eq + 1) : ""; 572 char *k = form_urldecode(pair); 573 char *v = form_urldecode(raw_v); 574 575 ant_value_t entry = js_mkarr(js); 576 js_arr_push(js, entry, js_mkstr(js, k, strlen(k))); 577 js_arr_push(js, entry, js_mkstr(js, v, strlen(v))); 578 js_arr_push(js, arr, entry); 579 free(pair); free(k); free(v); 580 p = amp ? amp + 1 : p + plen; 581 } 582 583 return arr; 584} 585 586char *usp_serialize(ant_t *js, ant_value_t usp) { 587 ant_value_t entries = js_get_slot(usp, SLOT_ENTRIES); 588 ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; 589 590 size_t cap = 256; 591 char *buf = malloc(cap); 592 if (!buf) return strdup(""); 593 size_t pos = 0; 594 595 for (ant_offset_t i = 0; i < len; i++) { 596 ant_value_t entry = js_arr_get(js, entries, i); 597 size_t klen = 0, vlen = 0; 598 599 char *k = js_getstr(js, js_arr_get(js, entry, 0), &klen); 600 char *v = js_getstr(js, js_arr_get(js, entry, 1), &vlen); 601 char *ek = k ? form_urlencode_n(k, klen) : strdup(""); 602 char *ev = v ? form_urlencode_n(v, vlen) : strdup(""); 603 604 size_t needed = strlen(ek) + strlen(ev) + 3; 605 if (pos + needed >= cap) { 606 cap = cap * 2 + needed; 607 buf = realloc(buf, cap); 608 if (!buf) { free(ek); free(ev); return strdup(""); } 609 } 610 611 if (pos > 0) buf[pos++] = '&'; 612 pos += (size_t)sprintf(buf + pos, "%s=%s", ek, ev); 613 free(ek); free(ev); 614 } 615 616 buf[pos] = '\0'; 617 return buf; 618} 619 620static void usp_push_to_url(ant_t *js, ant_value_t usp) { 621 ant_value_t url_obj = js_get_slot(usp, SLOT_DATA); 622 if (!is_special_object(url_obj)) return; 623 url_state_t *s = url_get_state(url_obj); 624 625 if (!s) return; 626 char *qs = usp_serialize(js, usp); 627 free(s->search); 628 629 if (*qs) { 630 size_t qlen = strlen(qs); 631 s->search = malloc(qlen + 2); 632 s->search[0] = '?'; 633 memcpy(s->search + 1, qs, qlen + 1); 634 } else s->search = strdup(""); 635 636 free(qs); 637} 638 639static void url_sync_usp(ant_t *js, ant_value_t url_obj, const char *query) { 640 ant_value_t usp = js_get_slot(url_obj, SLOT_ENTRIES); 641 if (!is_special_object(usp)) return; 642 ant_value_t new_entries = parse_query_to_arr(js, query); 643 js_set_slot_wb(js, usp, SLOT_ENTRIES, new_entries); 644} 645 646static ant_value_t make_usp_for_url(ant_t *js, ant_value_t url_obj, const char *query) { 647 ant_value_t usp = js_mkobj(js); 648 js_set_proto_init(usp, g_usp_proto); 649 js_set_slot(usp, SLOT_BRAND, js_mknum(BRAND_URLSEARCHPARAMS)); 650 js_set_slot_wb(js, usp, SLOT_DATA, url_obj); 651 js_set_slot(usp, SLOT_ENTRIES, parse_query_to_arr(js, query)); 652 return usp; 653} 654 655static ant_value_t url_get_href(ant_t *js, ant_value_t *args, int nargs) { 656 url_state_t *s = url_get_state(js->this_val); 657 if (!s) return js_mkstr(js, "", 0); 658 char *href = build_href(s); 659 ant_value_t ret = js_mkstr(js, href, strlen(href)); 660 free(href); 661 return ret; 662} 663 664static ant_value_t url_get_protocol(ant_t *js, ant_value_t *args, int nargs) { 665 url_state_t *s = url_get_state(js->this_val); 666 if (!s) return js_mkstr(js, "", 0); 667 return js_mkstr(js, s->protocol, strlen(s->protocol)); 668} 669 670static ant_value_t url_get_username(ant_t *js, ant_value_t *args, int nargs) { 671 url_state_t *s = url_get_state(js->this_val); 672 if (!s) return js_mkstr(js, "", 0); 673 return js_mkstr(js, s->username, strlen(s->username)); 674} 675 676static ant_value_t url_get_password(ant_t *js, ant_value_t *args, int nargs) { 677 url_state_t *s = url_get_state(js->this_val); 678 if (!s) return js_mkstr(js, "", 0); 679 return js_mkstr(js, s->password, strlen(s->password)); 680} 681 682static ant_value_t url_get_host(ant_t *js, ant_value_t *args, int nargs) { 683 url_state_t *s = url_get_state(js->this_val); 684 if (!s) return js_mkstr(js, "", 0); 685 if (s->port && *s->port) { 686 size_t len = strlen(s->hostname) + strlen(s->port) + 2; 687 char *host = malloc(len); 688 snprintf(host, len, "%s:%s", s->hostname, s->port); 689 ant_value_t ret = js_mkstr(js, host, strlen(host)); 690 free(host); 691 return ret; 692 } 693 return js_mkstr(js, s->hostname, strlen(s->hostname)); 694} 695 696static ant_value_t url_get_hostname(ant_t *js, ant_value_t *args, int nargs) { 697 url_state_t *s = url_get_state(js->this_val); 698 if (!s) return js_mkstr(js, "", 0); 699 return js_mkstr(js, s->hostname, strlen(s->hostname)); 700} 701 702static ant_value_t url_get_port(ant_t *js, ant_value_t *args, int nargs) { 703 url_state_t *s = url_get_state(js->this_val); 704 if (!s) return js_mkstr(js, "", 0); 705 return js_mkstr(js, s->port, strlen(s->port)); 706} 707 708static ant_value_t url_get_pathname(ant_t *js, ant_value_t *args, int nargs) { 709 url_state_t *s = url_get_state(js->this_val); 710 if (!s) return js_mkstr(js, "/", 1); 711 return js_mkstr(js, s->pathname, strlen(s->pathname)); 712} 713 714static ant_value_t url_get_search(ant_t *js, ant_value_t *args, int nargs) { 715 url_state_t *s = url_get_state(js->this_val); 716 if (!s) return js_mkstr(js, "", 0); 717 return js_mkstr(js, s->search, strlen(s->search)); 718} 719 720static ant_value_t url_get_hash(ant_t *js, ant_value_t *args, int nargs) { 721 url_state_t *s = url_get_state(js->this_val); 722 if (!s) return js_mkstr(js, "", 0); 723 return js_mkstr(js, s->hash, strlen(s->hash)); 724} 725 726static ant_value_t url_get_origin(ant_t *js, ant_value_t *args, int nargs) { 727 url_state_t *s = url_get_state(js->this_val); 728 if (!s || !is_special_scheme(s->protocol)) return js_mkstr(js, "null", 4); 729 730 size_t proto_len = strlen(s->protocol) - 1; 731 size_t host_len = strlen(s->hostname); 732 size_t port_len = (s->port && *s->port) ? strlen(s->port) + 1 : 0; 733 size_t total = proto_len + 3 + host_len + port_len + 1; 734 char *origin = malloc(total); 735 736 size_t pos = 0; 737 memcpy(origin + pos, s->protocol, proto_len); pos += proto_len; 738 memcpy(origin + pos, "://", 3); pos += 3; 739 memcpy(origin + pos, s->hostname, host_len); pos += host_len; 740 741 if (s->port && *s->port) { 742 origin[pos++] = ':'; 743 memcpy(origin + pos, s->port, strlen(s->port)); 744 pos += strlen(s->port); 745 } 746 747 origin[pos] = '\0'; 748 ant_value_t ret = js_mkstr(js, origin, pos); 749 free(origin); 750 751 return ret; 752} 753 754static ant_value_t url_get_searchParams(ant_t *js, ant_value_t *args, int nargs) { 755 ant_value_t usp = js_get_slot(js->this_val, SLOT_ENTRIES); 756 if (vtype(usp) == T_OBJ) return usp; 757 return js_mkundef(); 758} 759 760static ant_value_t url_set_href(ant_t *js, ant_value_t *args, int nargs) { 761 if (nargs < 1) return js_mkundef(); 762 url_state_t *s = url_get_state(js->this_val); 763 if (!s) return js_mkundef(); 764 765 const char *val = js_getstr(js, args[0], NULL); 766 if (!val) return js_mkerr(js, "TypeError: Invalid URL"); 767 768 url_state_t tmp; 769 if (parse_url_to_state(val, NULL, &tmp) != 0) 770 return js_mkerr(js, "TypeError: Invalid URL"); 771 772 free(s->protocol); s->protocol = tmp.protocol; 773 free(s->username); s->username = tmp.username; 774 free(s->password); s->password = tmp.password; 775 free(s->hostname); s->hostname = tmp.hostname; 776 free(s->port); s->port = tmp.port; 777 free(s->pathname); s->pathname = tmp.pathname; 778 free(s->search); s->search = tmp.search; 779 free(s->hash); s->hash = tmp.hash; 780 781 const char *q = (s->search[0] == '?') ? s->search + 1 : ""; 782 url_sync_usp(js, js->this_val, q); 783 784 return js_mkundef(); 785} 786 787static ant_value_t url_set_protocol(ant_t *js, ant_value_t *args, int nargs) { 788 if (nargs < 1) return js_mkundef(); 789 url_state_t *s = url_get_state(js->this_val); 790 791 if (!s) return js_mkundef(); 792 const char *val = js_getstr(js, args[0], NULL); 793 794 if (!val || !*val) return js_mkundef(); 795 const char *colon = strchr(val, ':'); 796 797 size_t slen = colon ? (size_t)(colon - val) : strlen(val); 798 if (!slen || !isalpha((unsigned char)val[0])) return js_mkundef(); 799 800 for (size_t i = 1; i < slen; i++) { 801 unsigned char c = (unsigned char)val[i]; 802 if (!isalnum(c) && c != '+' && c != '-' && c != '.') return js_mkundef(); 803 } 804 805 free(s->protocol); 806 s->protocol = malloc(slen + 2); 807 for (size_t i = 0; i < slen; i++) s->protocol[i] = (char)tolower((unsigned char)val[i]); 808 s->protocol[slen] = ':'; 809 s->protocol[slen + 1] = '\0'; 810 811 if (s->port && *s->port) { 812 int def = default_port_for(s->protocol); 813 if (def > 0 && atoi(s->port) == def) { free(s->port); s->port = strdup(""); } 814 } 815 816 return js_mkundef(); 817} 818 819static ant_value_t url_set_username(ant_t *js, ant_value_t *args, int nargs) { 820 if (nargs < 1) return js_mkundef(); 821 url_state_t *s = url_get_state(js->this_val); 822 if (!s) return js_mkundef(); 823 const char *val = js_getstr(js, args[0], NULL); 824 if (!val) return js_mkundef(); 825 free(s->username); 826 s->username = userinfo_encode(val); 827 return js_mkundef(); 828} 829 830static ant_value_t url_set_password(ant_t *js, ant_value_t *args, int nargs) { 831 if (nargs < 1) return js_mkundef(); 832 url_state_t *s = url_get_state(js->this_val); 833 if (!s) return js_mkundef(); 834 const char *val = js_getstr(js, args[0], NULL); 835 if (!val) return js_mkundef(); 836 free(s->password); 837 s->password = userinfo_encode(val); 838 return js_mkundef(); 839} 840 841static ant_value_t url_set_host(ant_t *js, ant_value_t *args, int nargs) { 842 if (nargs < 1) return js_mkundef(); 843 url_state_t *s = url_get_state(js->this_val); 844 if (!s) return js_mkundef(); 845 const char *val = js_getstr(js, args[0], NULL); 846 if (!val) return js_mkundef(); 847 const char *colon = strchr(val, ':'); 848 849 if (colon) { 850 free(s->hostname); 851 s->hostname = strndup(val, (size_t)(colon - val)); 852 const char *port_str = colon + 1; 853 free(s->port); 854 if (*port_str) { 855 int p = atoi(port_str); 856 int def = default_port_for(s->protocol); 857 s->port = (def > 0 && p == def) ? strdup("") : strdup(port_str); 858 } else s->port = strdup(""); 859 } else { 860 free(s->hostname); 861 s->hostname = strdup(val); 862 } 863 864 return js_mkundef(); 865} 866 867static ant_value_t url_set_hostname(ant_t *js, ant_value_t *args, int nargs) { 868 if (nargs < 1) return js_mkundef(); 869 url_state_t *s = url_get_state(js->this_val); 870 if (!s) return js_mkundef(); 871 const char *val = js_getstr(js, args[0], NULL); 872 if (!val) return js_mkundef(); 873 free(s->hostname); 874 const char *colon = strchr(val, ':'); 875 s->hostname = colon ? strndup(val, (size_t)(colon - val)) : strdup(val); 876 return js_mkundef(); 877} 878 879static ant_value_t url_set_port(ant_t *js, ant_value_t *args, int nargs) { 880 if (nargs < 1) return js_mkundef(); 881 url_state_t *s = url_get_state(js->this_val); 882 if (!s) return js_mkundef(); 883 const char *val = js_getstr(js, args[0], NULL); 884 if (!val) return js_mkundef(); 885 free(s->port); 886 if (!*val) { s->port = strdup(""); return js_mkundef(); } 887 int p = atoi(val); 888 if (p < 0 || p > 65535) { s->port = strdup(""); return js_mkundef(); } 889 int def = default_port_for(s->protocol); 890 if (def > 0 && p == def) s->port = strdup(""); else { 891 char buf[8]; 892 snprintf(buf, sizeof(buf), "%d", p); 893 s->port = strdup(buf); 894 } 895 return js_mkundef(); 896} 897 898static ant_value_t url_set_pathname(ant_t *js, ant_value_t *args, int nargs) { 899 if (nargs < 1) return js_mkundef(); 900 url_state_t *s = url_get_state(js->this_val); 901 if (!s) return js_mkundef(); 902 const char *val = js_getstr(js, args[0], NULL); 903 if (!val) return js_mkundef(); 904 free(s->pathname); 905 if (is_special_scheme(s->protocol) && val[0] != '/') { 906 size_t vlen = strlen(val); 907 s->pathname = malloc(vlen + 2); 908 s->pathname[0] = '/'; 909 memcpy(s->pathname + 1, val, vlen + 1); 910 } else s->pathname = strdup(val); 911 return js_mkundef(); 912} 913 914static ant_value_t url_set_search(ant_t *js, ant_value_t *args, int nargs) { 915 if (nargs < 1) return js_mkundef(); 916 url_state_t *s = url_get_state(js->this_val); 917 if (!s) return js_mkundef(); 918 const char *val = js_getstr(js, args[0], NULL); 919 if (!val) return js_mkundef(); 920 const char *q = (val[0] == '?') ? val + 1 : val; 921 free(s->search); 922 if (*q) { 923 size_t qlen = strlen(q); 924 s->search = malloc(qlen + 2); 925 s->search[0] = '?'; 926 memcpy(s->search + 1, q, qlen + 1); 927 } else s->search = strdup(""); 928 url_sync_usp(js, js->this_val, *q ? q : ""); 929 return js_mkundef(); 930} 931 932static ant_value_t url_set_hash(ant_t *js, ant_value_t *args, int nargs) { 933 if (nargs < 1) return js_mkundef(); 934 url_state_t *s = url_get_state(js->this_val); 935 if (!s) return js_mkundef(); 936 const char *val = js_getstr(js, args[0], NULL); 937 if (!val) return js_mkundef(); 938 const char *h = (val[0] == '#') ? val + 1 : val; 939 free(s->hash); 940 if (*h) { 941 size_t hlen = strlen(h); 942 s->hash = malloc(hlen + 2); 943 s->hash[0] = '#'; 944 memcpy(s->hash + 1, h, hlen + 1); 945 } else s->hash = strdup(""); 946 return js_mkundef(); 947} 948 949static ant_value_t url_toString(ant_t *js, ant_value_t *args, int nargs) { 950 url_state_t *s = url_get_state(js->this_val); 951 if (!s) return js_mkstr(js, "", 0); 952 char *href = build_href(s); 953 ant_value_t ret = js_mkstr(js, href, strlen(href)); 954 free(href); 955 return ret; 956} 957 958static ant_value_t js_URL(ant_t *js, ant_value_t *args, int nargs) { 959 if (is_undefined(js->new_target)) 960 return js_mkerr_typed(js, JS_ERR_TYPE, 961 "Failed to construct 'URL': Please use the 'new' operator."); 962 if (nargs < 1) 963 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': 1 argument required."); 964 965 ant_value_t url_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 966 if (is_err(url_sv)) 967 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL."); 968 const char *url_str = js_getstr(js, url_sv, NULL); 969 if (!url_str) 970 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL."); 971 972 const char *base_str = NULL; 973 if (nargs > 1 && !is_undefined(args[1]) && !is_null(args[1])) 974 base_str = coerce_to_string(js, args[1], NULL); 975 976 url_state_t *s = calloc(1, sizeof(url_state_t)); 977 if (!s) return js_mkerr(js, "out of memory"); 978 979 if (parse_url_to_state(url_str, base_str, s) != 0) { 980 free(s); 981 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL."); 982 } 983 984 ant_value_t obj = js_mkobj(js); 985 js_set_proto_init(obj, g_url_proto); 986 js_set_slot(obj, SLOT_DATA, ANT_PTR(s)); 987 js_set_finalizer(obj, url_finalize); 988 989 const char *query = (s->search && s->search[0] == '?') ? s->search + 1 : ""; 990 ant_value_t usp = make_usp_for_url(js, obj, query); 991 js_set_slot_wb(js, obj, SLOT_ENTRIES, usp); 992 993 return obj; 994} 995 996ant_value_t make_url_obj(ant_t *js, url_state_t *s) { 997 ant_value_t obj = js_mkobj(js); 998 js_set_proto_init(obj, g_url_proto); 999 js_set_slot(obj, SLOT_DATA, ANT_PTR(s)); 1000 js_set_finalizer(obj, url_finalize); 1001 const char *query = (s->search && s->search[0] == '?') ? s->search + 1 : ""; 1002 ant_value_t usp = make_usp_for_url(js, obj, query); 1003 js_set_slot_wb(js, obj, SLOT_ENTRIES, usp); 1004 return obj; 1005} 1006 1007static ant_value_t url_canParse(ant_t *js, ant_value_t *args, int nargs) { 1008 if (nargs < 1) return js_false; 1009 ant_value_t url_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1010 if (is_err(url_sv)) return js_false; 1011 const char *url_str = js_getstr(js, url_sv, NULL); 1012 if (!url_str) return js_false; 1013 const char *base_str = NULL; 1014 if (nargs > 1 && !is_undefined(args[1]) && !is_null(args[1])) 1015 base_str = coerce_to_string(js, args[1], NULL); 1016 url_state_t s; 1017 if (parse_url_to_state(url_str, base_str, &s) != 0) return js_false; 1018 url_state_clear(&s); 1019 return js_true; 1020} 1021 1022static ant_value_t url_parse(ant_t *js, ant_value_t *args, int nargs) { 1023 if (nargs < 1) return js_mknull(); 1024 ant_value_t url_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1025 if (is_err(url_sv)) return js_mknull(); 1026 const char *url_str = js_getstr(js, url_sv, NULL); 1027 if (!url_str) return js_mknull(); 1028 const char *base_str = NULL; 1029 if (nargs > 1 && !is_undefined(args[1]) && !is_null(args[1])) 1030 base_str = coerce_to_string(js, args[1], NULL); 1031 url_state_t *s = calloc(1, sizeof(url_state_t)); 1032 if (!s) return js_mknull(); 1033 if (parse_url_to_state(url_str, base_str, s) != 0) { 1034 free(s); 1035 return js_mknull(); 1036 } 1037 return make_url_obj(js, s); 1038} 1039 1040static ant_value_t usp_get(ant_t *js, ant_value_t *args, int nargs) { 1041 if (nargs < 1) return js_mknull(); 1042 ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1043 if (is_err(key_sv)) return js_mknull(); 1044 const char *key = js_getstr(js, key_sv, NULL); 1045 if (!key) return js_mknull(); 1046 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1047 if (!is_special_object(entries)) return js_mknull(); 1048 ant_offset_t len = js_arr_len(js, entries); 1049 for (ant_offset_t i = 0; i < len; i++) { 1050 ant_value_t entry = js_arr_get(js, entries, i); 1051 const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 1052 if (ek && strcmp(ek, key) == 0) return js_arr_get(js, entry, 1); 1053 } 1054 return js_mknull(); 1055} 1056 1057static ant_value_t usp_getAll(ant_t *js, ant_value_t *args, int nargs) { 1058 ant_value_t result = js_mkarr(js); 1059 if (nargs < 1) return result; 1060 ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1061 if (is_err(key_sv)) return result; 1062 const char *key = js_getstr(js, key_sv, NULL); 1063 if (!key) return result; 1064 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1065 if (!is_special_object(entries)) return result; 1066 ant_offset_t len = js_arr_len(js, entries); 1067 for (ant_offset_t i = 0; i < len; i++) { 1068 ant_value_t entry = js_arr_get(js, entries, i); 1069 const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 1070 if (ek && strcmp(ek, key) == 0) 1071 js_arr_push(js, result, js_arr_get(js, entry, 1)); 1072 } 1073 return result; 1074} 1075 1076static ant_value_t usp_has(ant_t *js, ant_value_t *args, int nargs) { 1077 if (nargs < 1) return js_false; 1078 ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1079 if (is_err(key_sv)) return js_false; 1080 const char *key = js_getstr(js, key_sv, NULL); 1081 if (!key) return js_false; 1082 const char *match_val = NULL; 1083 if (nargs >= 2 && !is_undefined(args[1])) { 1084 ant_value_t mv_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 1085 if (!is_err(mv_sv)) match_val = js_getstr(js, mv_sv, NULL); 1086 } 1087 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1088 if (!is_special_object(entries)) return js_false; 1089 ant_offset_t len = js_arr_len(js, entries); 1090 for (ant_offset_t i = 0; i < len; i++) { 1091 ant_value_t entry = js_arr_get(js, entries, i); 1092 const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 1093 if (!ek || strcmp(ek, key) != 0) continue; 1094 if (!match_val) return js_true; 1095 const char *ev = js_getstr(js, js_arr_get(js, entry, 1), NULL); 1096 if (ev && strcmp(ev, match_val) == 0) return js_true; 1097 } 1098 return js_false; 1099} 1100 1101static ant_value_t usp_set(ant_t *js, ant_value_t *args, int nargs) { 1102 if (nargs < 2) return js_mkundef(); 1103 ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1104 1105 if (is_err(key_sv)) return js_mkundef(); 1106 ant_value_t val_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 1107 1108 if (is_err(val_sv)) return js_mkundef(); 1109 const char *key = js_getstr(js, key_sv, NULL); 1110 1111 if (!key) return js_mkundef(); 1112 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1113 ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; 1114 ant_value_t new_entries = js_mkarr(js); 1115 1116 int found = 0; 1117 for (ant_offset_t i = 0; i < len; i++) { 1118 ant_value_t entry = js_arr_get(js, entries, i); 1119 const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 1120 1121 if (ek && strcmp(ek, key) == 0) { 1122 if (!found) { 1123 ant_value_t ne = js_mkarr(js); 1124 js_arr_push(js, ne, key_sv); 1125 js_arr_push(js, ne, val_sv); 1126 js_arr_push(js, new_entries, ne); 1127 found = 1; 1128 } 1129 } else js_arr_push(js, new_entries, entry); } 1130 1131 if (!found) { 1132 ant_value_t ne = js_mkarr(js); 1133 js_arr_push(js, ne, key_sv); 1134 js_arr_push(js, ne, val_sv); 1135 js_arr_push(js, new_entries, ne); 1136 } 1137 1138 js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); 1139 usp_push_to_url(js, js->this_val); 1140 1141 return js_mkundef(); 1142} 1143 1144static ant_value_t usp_append(ant_t *js, ant_value_t *args, int nargs) { 1145 if (nargs < 2) return js_mkundef(); 1146 ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1147 1148 if (is_err(key_sv)) return js_mkundef(); 1149 ant_value_t val_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 1150 1151 if (is_err(val_sv)) return js_mkundef(); 1152 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1153 1154 if (!is_special_object(entries)) return js_mkundef(); 1155 ant_value_t entry = js_mkarr(js); 1156 1157 js_arr_push(js, entry, key_sv); 1158 js_arr_push(js, entry, val_sv); 1159 js_arr_push(js, entries, entry); 1160 usp_push_to_url(js, js->this_val); 1161 1162 return js_mkundef(); 1163} 1164 1165static ant_value_t usp_delete(ant_t *js, ant_value_t *args, int nargs) { 1166 if (nargs < 1) return js_mkundef(); 1167 ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 1168 1169 if (is_err(key_sv)) return js_mkundef(); 1170 const char *key = js_getstr(js, key_sv, NULL); 1171 1172 if (!key) return js_mkundef(); 1173 const char *match_val = NULL; 1174 1175 if (nargs >= 2 && !is_undefined(args[1])) { 1176 ant_value_t mv_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 1177 if (!is_err(mv_sv)) match_val = js_getstr(js, mv_sv, NULL); 1178 } 1179 1180 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1181 ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; 1182 ant_value_t new_entries = js_mkarr(js); 1183 1184 for (ant_offset_t i = 0; i < len; i++) { 1185 ant_value_t entry = js_arr_get(js, entries, i); 1186 const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 1187 if (ek && strcmp(ek, key) == 0) { 1188 if (!match_val) continue; 1189 const char *ev = js_getstr(js, js_arr_get(js, entry, 1), NULL); 1190 if (ev && strcmp(ev, match_val) == 0) continue; 1191 } 1192 js_arr_push(js, new_entries, entry); 1193 } 1194 1195 js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); 1196 usp_push_to_url(js, js->this_val); 1197 1198 return js_mkundef(); 1199} 1200 1201static ant_value_t usp_toString(ant_t *js, ant_value_t *args, int nargs) { 1202 char *s = usp_serialize(js, js->this_val); 1203 ant_value_t ret = js_mkstr(js, s, strlen(s)); 1204 free(s); 1205 return ret; 1206} 1207 1208static ant_value_t usp_forEach(ant_t *js, ant_value_t *args, int nargs) { 1209 if (nargs < 1 || !is_callable(args[0])) return js_mkundef(); 1210 1211 ant_value_t cb = args[0]; 1212 ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef(); 1213 ant_value_t self = js->this_val; 1214 ant_value_t entries = js_get_slot(self, SLOT_ENTRIES); 1215 1216 if (!is_special_object(entries)) return js_mkundef(); 1217 ant_offset_t len = js_arr_len(js, entries); 1218 1219 for (ant_offset_t i = 0; i < len; i++) { 1220 ant_value_t entry = js_arr_get(js, entries, i); 1221 ant_value_t k = js_arr_get(js, entry, 0); 1222 ant_value_t v = js_arr_get(js, entry, 1); 1223 ant_value_t cb_args[3] = { v, k, self }; 1224 ant_value_t r = sv_vm_call(js->vm, js, cb, this_arg, cb_args, 3, NULL, false); 1225 if (is_err(r)) return r; 1226 } 1227 1228 return js_mkundef(); 1229} 1230 1231static ant_value_t usp_size_get(ant_t *js, ant_value_t *args, int nargs) { 1232 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1233 if (!is_special_object(entries)) return js_mknum(0); 1234 return js_mknum((double)js_arr_len(js, entries)); 1235} 1236 1237static ant_value_t usp_sort(ant_t *js, ant_value_t *args, int nargs) { 1238 ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1239 if (!is_special_object(entries)) return js_mkundef(); 1240 ant_offset_t len = js_arr_len(js, entries); 1241 if (len <= 1) return js_mkundef(); 1242 1243 ant_value_t *arr = malloc(sizeof(ant_value_t) * (size_t)len); 1244 if (!arr) return js_mkundef(); 1245 for (ant_offset_t i = 0; i < len; i++) arr[i] = js_arr_get(js, entries, i); 1246 1247 for (ant_offset_t i = 1; i < len; i++) { 1248 ant_value_t cur = arr[i]; 1249 const char *ck = js_getstr(js, js_arr_get(js, cur, 0), NULL); 1250 ant_offset_t j = i; 1251 1252 while (j > 0) { 1253 const char *jk = js_getstr(js, js_arr_get(js, arr[j - 1], 0), NULL); 1254 if (strcmp(jk ? jk : "", ck ? ck : "") <= 0) break; 1255 arr[j] = arr[j - 1]; j--; 1256 } 1257 1258 arr[j] = cur; 1259 } 1260 1261 ant_value_t new_entries = js_mkarr(js); 1262 for (ant_offset_t i = 0; i < len; i++) js_arr_push(js, new_entries, arr[i]); 1263 free(arr); 1264 1265 js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); 1266 usp_push_to_url(js, js->this_val); 1267 1268 return js_mkundef(); 1269} 1270 1271static ant_value_t usp_iter_next(ant_t *js, ant_value_t *args, int nargs) { 1272 ant_value_t state_v = js_get_slot(js->this_val, SLOT_ITER_STATE); 1273 if (vtype(state_v) != T_NUM) return js_iter_result(js, false, js_mkundef()); 1274 1275 uint32_t state = (uint32_t)js_getnum(state_v); 1276 uint32_t kind = ITER_STATE_KIND(state); 1277 uint32_t idx = ITER_STATE_INDEX(state); 1278 1279 ant_value_t usp = js_get_slot(js->this_val, SLOT_DATA); 1280 ant_value_t entries = js_get_slot(usp, SLOT_ENTRIES); 1281 if (!is_special_object(entries) || (ant_offset_t)idx >= js_arr_len(js, entries)) 1282 return js_iter_result(js, false, js_mkundef()); 1283 1284 js_set_slot(js->this_val, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, idx + 1))); 1285 1286 ant_value_t entry = js_arr_get(js, entries, (ant_offset_t)idx); 1287 ant_value_t k = js_arr_get(js, entry, 0); 1288 ant_value_t v = js_arr_get(js, entry, 1); 1289 1290 ant_value_t out; 1291 switch (kind) { 1292 case USP_ITER_KEYS: out = k; break; 1293 case USP_ITER_VALUES: out = v; break; 1294 default: { 1295 out = js_mkarr(js); 1296 js_arr_push(js, out, k); 1297 js_arr_push(js, out, v); 1298 break; 1299 }} 1300 1301 return js_iter_result(js, true, out); 1302} 1303 1304static ant_value_t make_usp_iter(ant_t *js, ant_value_t usp, int kind) { 1305 ant_value_t iter = js_mkobj(js); 1306 js_set_proto_init(iter, g_usp_iter_proto); 1307 js_set_slot_wb(js, iter, SLOT_DATA, usp); 1308 js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, 0))); 1309 return iter; 1310} 1311 1312static ant_value_t usp_entries_fn(ant_t *js, ant_value_t *args, int nargs) { 1313 return make_usp_iter(js, js->this_val, USP_ITER_ENTRIES); 1314} 1315 1316static ant_value_t usp_keys_fn(ant_t *js, ant_value_t *args, int nargs) { 1317 return make_usp_iter(js, js->this_val, USP_ITER_KEYS); 1318} 1319 1320static ant_value_t usp_values_fn(ant_t *js, ant_value_t *args, int nargs) { 1321 return make_usp_iter(js, js->this_val, USP_ITER_VALUES); 1322} 1323 1324static ant_value_t js_URLSearchParams(ant_t *js, ant_value_t *args, int nargs) { 1325 if (is_undefined(js->new_target)) 1326 return js_mkerr_typed(js, JS_ERR_TYPE, 1327 "Failed to construct 'URLSearchParams': Please use the 'new' operator."); 1328 1329 ant_value_t obj = js_mkobj(js); 1330 js_set_proto_init(obj, g_usp_proto); 1331 js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_URLSEARCHPARAMS)); 1332 js_set_slot(obj, SLOT_DATA, js_mkundef()); 1333 1334 ant_value_t entries = js_mkarr(js); 1335 js_set_slot(obj, SLOT_ENTRIES, entries); 1336 1337 if (nargs < 1 || is_undefined(args[0]) || is_null(args[0])) return obj; 1338 1339 ant_value_t init = args[0]; 1340 uint8_t t = vtype(init); 1341 1342 if (t == T_STR) { 1343 const char *s = js_getstr(js, init, NULL); 1344 if (s) { 1345 const char *q = (s[0] == '?') ? s + 1 : s; 1346 js_set_slot(obj, SLOT_ENTRIES, parse_query_to_arr(js, q)); 1347 } 1348 return obj; 1349 } 1350 1351 if (t == T_ARR) { 1352 ant_offset_t len = js_arr_len(js, init); 1353 for (ant_offset_t i = 0; i < len; i++) { 1354 ant_value_t pair = js_arr_get(js, init, i); 1355 if (vtype(pair) != T_ARR) 1356 return js_mkerr_typed(js, JS_ERR_TYPE, 1357 "Failed to construct 'URLSearchParams': Each element must be an array."); 1358 1359 ant_offset_t plen = js_arr_len(js, pair); 1360 if (plen != 2) 1361 return js_mkerr_typed(js, JS_ERR_TYPE, 1362 "Failed to construct 'URLSearchParams': Each pair must have exactly 2 elements."); 1363 1364 ant_value_t pk = js_arr_get(js, pair, 0); 1365 ant_value_t pv = js_arr_get(js, pair, 1); 1366 ant_value_t ksv = (vtype(pk) == T_STR) ? pk : js_tostring_val(js, pk); 1367 1368 if (is_err(ksv)) return ksv; 1369 ant_value_t vsv = (vtype(pv) == T_STR) ? pv : js_tostring_val(js, pv); 1370 1371 if (is_err(vsv)) return vsv; 1372 ant_value_t entry = js_mkarr(js); 1373 1374 js_arr_push(js, entry, ksv); 1375 js_arr_push(js, entry, vsv); 1376 js_arr_push(js, entries, entry); 1377 } 1378 1379 return obj; 1380 } 1381 1382 if (is_special_object(init)) { 1383 ant_value_t src = js_get_slot(init, SLOT_ENTRIES); 1384 if (vtype(src) == T_ARR) { 1385 ant_offset_t len = js_arr_len(js, src); 1386 for (ant_offset_t i = 0; i < len; i++) { 1387 ant_value_t entry = js_arr_get(js, src, i); 1388 ant_value_t ne = js_mkarr(js); 1389 1390 js_arr_push(js, ne, js_arr_get(js, entry, 0)); 1391 js_arr_push(js, ne, js_arr_get(js, entry, 1)); 1392 js_arr_push(js, entries, ne); 1393 } 1394 1395 return obj; 1396 } 1397 1398 ant_iter_t it = js_prop_iter_begin(js, init); 1399 const char *key; 1400 size_t key_len; 1401 ant_value_t val; 1402 1403 while (js_prop_iter_next(&it, &key, &key_len, &val)) { 1404 ant_value_t sv = (vtype(val) == T_STR) ? val : js_tostring_val(js, val); 1405 if (is_err(sv)) { js_prop_iter_end(&it); return sv; } 1406 ant_value_t entry = js_mkarr(js); 1407 js_arr_push(js, entry, js_mkstr(js, key, key_len)); 1408 js_arr_push(js, entry, sv); 1409 js_arr_push(js, entries, entry); 1410 } 1411 1412 js_prop_iter_end(&it); 1413 } 1414 1415 return obj; 1416} 1417 1418void init_url_module(void) { 1419 ant_t *js = rt->js; 1420 ant_value_t glob = js->global; 1421 1422 g_usp_iter_proto = js_mkobj(js); 1423 js_set_proto_init(g_usp_iter_proto, js->sym.iterator_proto); 1424 js_set(js, g_usp_iter_proto, "next", js_mkfun(usp_iter_next)); 1425 js_set_descriptor(js, g_usp_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); 1426 js_set_sym(js, g_usp_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb)); 1427 1428 g_usp_proto = js_mkobj(js); 1429 js_set(js, g_usp_proto, "get", js_mkfun(usp_get)); 1430 js_set(js, g_usp_proto, "getAll", js_mkfun(usp_getAll)); 1431 js_set(js, g_usp_proto, "has", js_mkfun(usp_has)); 1432 js_set(js, g_usp_proto, "set", js_mkfun(usp_set)); 1433 js_set(js, g_usp_proto, "append", js_mkfun(usp_append)); 1434 js_set(js, g_usp_proto, "delete", js_mkfun(usp_delete)); 1435 js_set(js, g_usp_proto, "sort", js_mkfun(usp_sort)); 1436 js_set(js, g_usp_proto, "toString", js_mkfun(usp_toString)); 1437 js_set(js, g_usp_proto, "forEach", js_mkfun(usp_forEach)); 1438 js_set_getter_desc(js, g_usp_proto, "size", 4, js_mkfun(usp_size_get), JS_DESC_C); 1439 1440 js_set(js, g_usp_proto, "entries", js_mkfun(usp_entries_fn)); 1441 js_set(js, g_usp_proto, "keys", js_mkfun(usp_keys_fn)); 1442 js_set(js, g_usp_proto, "values", js_mkfun(usp_values_fn)); 1443 1444 js_set_sym(js, g_usp_proto, get_iterator_sym(), js_get(js, g_usp_proto, "entries")); 1445 js_set_sym(js, g_usp_proto, get_toStringTag_sym(), js_mkstr(js, "URLSearchParams", 15)); 1446 1447 ant_value_t usp_ctor = js_make_ctor(js, js_URLSearchParams, g_usp_proto, "URLSearchParams", 15); 1448 js_set(js, glob, "URLSearchParams", usp_ctor); 1449 1450 g_url_proto = js_mkobj(js); 1451 js_set_accessor_desc(js, g_url_proto, "href", 4, js_mkfun(url_get_href), js_mkfun(url_set_href), JS_DESC_C); 1452 js_set_accessor_desc(js, g_url_proto, "protocol", 8, js_mkfun(url_get_protocol), js_mkfun(url_set_protocol), JS_DESC_C); 1453 js_set_accessor_desc(js, g_url_proto, "username", 8, js_mkfun(url_get_username), js_mkfun(url_set_username), JS_DESC_C); 1454 js_set_accessor_desc(js, g_url_proto, "password", 8, js_mkfun(url_get_password), js_mkfun(url_set_password), JS_DESC_C); 1455 js_set_accessor_desc(js, g_url_proto, "host", 4, js_mkfun(url_get_host), js_mkfun(url_set_host), JS_DESC_C); 1456 js_set_accessor_desc(js, g_url_proto, "hostname", 8, js_mkfun(url_get_hostname), js_mkfun(url_set_hostname), JS_DESC_C); 1457 js_set_accessor_desc(js, g_url_proto, "port", 4, js_mkfun(url_get_port), js_mkfun(url_set_port), JS_DESC_C); 1458 js_set_accessor_desc(js, g_url_proto, "pathname", 8, js_mkfun(url_get_pathname), js_mkfun(url_set_pathname), JS_DESC_C); 1459 js_set_accessor_desc(js, g_url_proto, "search", 6, js_mkfun(url_get_search), js_mkfun(url_set_search), JS_DESC_C); 1460 js_set_accessor_desc(js, g_url_proto, "hash", 4, js_mkfun(url_get_hash), js_mkfun(url_set_hash), JS_DESC_C); 1461 js_set_getter_desc(js, g_url_proto, "origin", 6, js_mkfun(url_get_origin), JS_DESC_C); 1462 js_set_getter_desc(js, g_url_proto, "searchParams", 12, js_mkfun(url_get_searchParams), JS_DESC_C); 1463 js_set(js, g_url_proto, "toString", js_mkfun(url_toString)); 1464 js_set(js, g_url_proto, "toJSON", js_mkfun(url_toString)); 1465 js_set_sym(js, g_url_proto, get_toStringTag_sym(), js_mkstr(js, "URL", 3)); 1466 1467 ant_value_t url_ctor = js_make_ctor(js, js_URL, g_url_proto, "URL", 3); 1468 js_set(js, url_ctor, "canParse", js_mkfun(url_canParse)); 1469 js_set(js, url_ctor, "parse", js_mkfun(url_parse)); 1470 js_set(js, glob, "URL", url_ctor); 1471} 1472 1473static ant_value_t builtin_fileURLToPath(ant_t *js, ant_value_t *args, int nargs) { 1474 if (nargs < 1) return js_mkerr(js, "fileURLToPath requires a string or URL argument"); 1475 1476 size_t len; 1477 const char *str = coerce_to_string(js, args[0], &len); 1478 if (!str) return js_mkerr(js, "fileURLToPath requires a string or URL argument"); 1479 1480 url_state_t s; 1481 if (parse_url_to_state(str, NULL, &s) != 0) 1482 return js_mkerr(js, "Invalid URL"); 1483 if (strcmp(s.protocol, "file:") != 0) { 1484 url_state_clear(&s); 1485 return js_mkerr(js, "fileURLToPath requires a file: URL"); 1486 } 1487 1488 char *decoded = url_decode_component(s.pathname); 1489 url_state_clear(&s); 1490 if (!decoded) return js_mkerr(js, "allocation failure"); 1491 ant_value_t ret = js_mkstr(js, decoded, strlen(decoded)); 1492 1493 free(decoded); 1494 return ret; 1495} 1496 1497static ant_value_t builtin_pathToFileURL(ant_t *js, ant_value_t *args, int nargs) { 1498 if (nargs < 1 || vtype(args[0]) != T_STR) 1499 return js_mkerr(js, "pathToFileURL requires a string argument"); 1500 1501 size_t len; 1502 const char *path = js_getstr(js, args[0], &len); 1503 size_t total = 7 + len; 1504 char *buf = malloc(total + 1); 1505 if (!buf) return js_mkerr(js, "allocation failure"); 1506 1507 memcpy(buf, "file://", 7); 1508 memcpy(buf + 7, path, len); 1509 buf[total] = '\0'; 1510 1511 url_state_t *s = calloc(1, sizeof(url_state_t)); 1512 if (!s) { free(buf); return js_mkerr(js, "allocation failure"); } 1513 if (parse_url_to_state(buf, NULL, s) != 0) { 1514 free(buf); free(s); 1515 return js_mkerr(js, "Invalid file URL"); 1516 } 1517 1518 free(buf); 1519 return make_url_obj(js, s); 1520} 1521 1522typedef struct { 1523 char *buf; 1524 size_t len; 1525 size_t cap; 1526} url_fmt_buf_t; 1527 1528static bool url_fmt_reserve(url_fmt_buf_t *b, size_t extra) { 1529 if (extra <= b->cap - b->len) return true; 1530 1531 size_t needed = b->len + extra + 1; 1532 size_t next = b->cap ? b->cap : 128; 1533 while (next < needed) next *= 2; 1534 1535 char *buf = realloc(b->buf, next); 1536 if (!buf) return false; 1537 b->buf = buf; 1538 b->cap = next; 1539 1540 return true; 1541} 1542 1543static bool url_fmt_append_n(url_fmt_buf_t *b, const char *s, size_t n) { 1544 if (!s || n == 0) return true; 1545 if (!url_fmt_reserve(b, n)) return false; 1546 memcpy(b->buf + b->len, s, n); 1547 b->len += n; 1548 b->buf[b->len] = '\0'; 1549 return true; 1550} 1551 1552static bool url_fmt_append(url_fmt_buf_t *b, const char *s) { 1553 return url_fmt_append_n(b, s, s ? strlen(s) : 0); 1554} 1555 1556static bool url_fmt_append_c(url_fmt_buf_t *b, char c) { 1557 if (!url_fmt_reserve(b, 1)) return false; 1558 b->buf[b->len++] = c; 1559 b->buf[b->len] = '\0'; 1560 return true; 1561} 1562 1563static bool url_fmt_append_value_string(ant_t *js, url_fmt_buf_t *b, ant_value_t value) { 1564 ant_value_t str_val = vtype(value) == T_STR ? value : js_tostring_val(js, value); 1565 if (is_err(str_val)) return false; 1566 1567 size_t len = 0; 1568 const char *str = js_getstr(js, str_val, &len); 1569 return str && url_fmt_append_n(b, str, len); 1570} 1571 1572static bool url_fmt_get_string_prop( 1573 ant_t *js, 1574 ant_value_t obj, 1575 const char *name, 1576 ant_value_t *out, 1577 const char **str, 1578 size_t *len 1579) { 1580 *out = js_get(js, obj, name); 1581 if (is_undefined(*out) || is_null(*out)) return false; 1582 1583 ant_value_t str_val = vtype(*out) == T_STR ? *out : js_tostring_val(js, *out); 1584 if (is_err(str_val)) return false; 1585 1586 *out = str_val; 1587 *str = js_getstr(js, str_val, len); 1588 return *str != NULL; 1589} 1590 1591static bool url_fmt_is_query_unescaped(unsigned char c) { 1592 return isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~'; 1593} 1594 1595static bool url_fmt_append_query_component(url_fmt_buf_t *b, const char *s, size_t len) { 1596 static const char hex[] = "0123456789ABCDEF"; 1597 1598 for (size_t i = 0; i < len; i++) { 1599 unsigned char c = (unsigned char)s[i]; 1600 if (url_fmt_is_query_unescaped(c)) { 1601 if (!url_fmt_append_c(b, (char)c)) return false; 1602 } else { 1603 char esc[3] = { '%', hex[c >> 4], hex[c & 0x0f] }; 1604 if (!url_fmt_append_n(b, esc, sizeof(esc))) return false; 1605 }} 1606 return true; 1607} 1608 1609static bool url_fmt_append_query_object(ant_t *js, url_fmt_buf_t *b, ant_value_t query) { 1610 if (!is_special_object(query)) return true; 1611 1612 bool first = true; 1613 ant_iter_t it = js_prop_iter_begin(js, query); 1614 1615 const char *key; 1616 size_t key_len; 1617 ant_value_t val; 1618 1619 while (js_prop_iter_next(&it, &key, &key_len, &val)) { 1620 if (!first && !url_fmt_append_c(b, '&')) { 1621 js_prop_iter_end(&it); 1622 return false; 1623 } 1624 first = false; 1625 1626 if (!url_fmt_append_query_component(b, key, key_len)) { 1627 js_prop_iter_end(&it); 1628 return false; 1629 } 1630 1631 if (!url_fmt_append_c(b, '=')) { 1632 js_prop_iter_end(&it); 1633 return false; 1634 } 1635 1636 ant_value_t str_val = vtype(val) == T_STR ? val : js_tostring_val(js, val); 1637 if (is_err(str_val)) { 1638 js_prop_iter_end(&it); 1639 return false; 1640 } 1641 1642 size_t val_len = 0; 1643 const char *val_str = js_getstr(js, str_val, &val_len); 1644 if (!val_str || !url_fmt_append_query_component(b, val_str, val_len)) { 1645 js_prop_iter_end(&it); 1646 return false; 1647 } 1648 } 1649 1650 js_prop_iter_end(&it); 1651 return true; 1652} 1653 1654static bool url_fmt_protocol_needs_slashes(const char *protocol, size_t len) { 1655 if (len > 0 && protocol[len - 1] == ':') len--; 1656 return 1657 (len == 4 && memcmp(protocol, "http", 4) == 0) || 1658 (len == 5 && memcmp(protocol, "https", 5) == 0) || 1659 (len == 3 && memcmp(protocol, "ftp", 3) == 0) || 1660 (len == 4 && memcmp(protocol, "file", 4) == 0) || 1661 (len == 2 && memcmp(protocol, "ws", 2) == 0) || 1662 (len == 3 && memcmp(protocol, "wss", 3) == 0); 1663} 1664 1665static ant_value_t builtin_url_format(ant_t *js, ant_value_t *args, int nargs) { 1666 if (nargs < 1 || !is_object_type(args[0])) 1667 return js_mkerr_typed(js, JS_ERR_TYPE, "url.format() requires a URL or object argument"); 1668 1669 url_state_t *state = url_get_state(args[0]); 1670 if (state) { 1671 char *href = build_href(state); 1672 ant_value_t ret = js_mkstr(js, href, strlen(href)); 1673 free(href); 1674 return ret; 1675 } 1676 1677 ant_value_t obj = args[0]; 1678 ant_value_t tmp; 1679 1680 const char *protocol = NULL, *auth = NULL, *host = NULL, *hostname = NULL; 1681 const char *port = NULL, *pathname = NULL, *search = NULL, *hash = NULL; 1682 1683 size_t protocol_len = 0, auth_len = 0, host_len = 0, hostname_len = 0; 1684 size_t port_len = 0, pathname_len = 0, search_len = 0, hash_len = 0; 1685 1686 url_fmt_get_string_prop(js, obj, "protocol", &tmp, &protocol, &protocol_len); 1687 url_fmt_get_string_prop(js, obj, "auth", &tmp, &auth, &auth_len); 1688 url_fmt_get_string_prop(js, obj, "host", &tmp, &host, &host_len); 1689 url_fmt_get_string_prop(js, obj, "hostname", &tmp, &hostname, &hostname_len); 1690 url_fmt_get_string_prop(js, obj, "port", &tmp, &port, &port_len); 1691 url_fmt_get_string_prop(js, obj, "pathname", &tmp, &pathname, &pathname_len); 1692 url_fmt_get_string_prop(js, obj, "search", &tmp, &search, &search_len); 1693 url_fmt_get_string_prop(js, obj, "hash", &tmp, &hash, &hash_len); 1694 1695 url_fmt_buf_t b = {0}; 1696 1697 if (protocol && protocol_len > 0) { 1698 if (!url_fmt_append_n(&b, protocol, protocol_len)) goto oom; 1699 if (protocol[protocol_len - 1] != ':' && !url_fmt_append_c(&b, ':')) goto oom; 1700 } 1701 1702 bool has_host = (host && host_len > 0) || (hostname && hostname_len > 0); 1703 ant_value_t slashes_val = js_get(js, obj, "slashes"); 1704 1705 bool needs_slashes = 1706 js_truthy(js, slashes_val) || 1707 (protocol && url_fmt_protocol_needs_slashes(protocol, protocol_len)); 1708 1709 if (needs_slashes && (has_host || (protocol && protocol_len >= 4 && memcmp(protocol, "file", 4) == 0))) { 1710 if (!url_fmt_append(&b, "//")) goto oom; 1711 } 1712 1713 if (auth && auth_len > 0) { 1714 if (!url_fmt_append_n(&b, auth, auth_len)) goto oom; 1715 if (!url_fmt_append_c(&b, '@')) goto oom; 1716 } 1717 1718 if (host && host_len > 0) { 1719 if (!url_fmt_append_n(&b, host, host_len)) goto oom; 1720 } else if (hostname && hostname_len > 0) { 1721 if (!url_fmt_append_n(&b, hostname, hostname_len)) goto oom; 1722 if (port && port_len > 0) { 1723 if (!url_fmt_append_c(&b, ':')) goto oom; 1724 if (!url_fmt_append_n(&b, port, port_len)) goto oom; 1725 }} 1726 1727 if (pathname && pathname_len > 0) { 1728 if (has_host && pathname[0] != '/' && !url_fmt_append_c(&b, '/')) goto oom; 1729 if (!url_fmt_append_n(&b, pathname, pathname_len)) goto oom; 1730 } 1731 1732 if (search && search_len > 0) { 1733 if (search[0] != '?' && !url_fmt_append_c(&b, '?')) goto oom; 1734 if (!url_fmt_append_n(&b, search, search_len)) goto oom; 1735 } else { 1736 ant_value_t query = js_get(js, obj, "query"); 1737 if (vtype(query) == T_STR) { 1738 size_t qlen = 0; 1739 const char *q = js_getstr(js, query, &qlen); 1740 if (q && qlen > 0) { 1741 if (!url_fmt_append_c(&b, '?')) goto oom; 1742 if (!url_fmt_append_n(&b, q, qlen)) goto oom; 1743 } 1744 } else if (is_special_object(query)) { 1745 url_fmt_buf_t qb = {0}; 1746 if (!url_fmt_append_query_object(js, &qb, query)) { 1747 free(qb.buf); 1748 goto oom; 1749 } 1750 if (qb.len > 0) { 1751 if (!url_fmt_append_c(&b, '?')) { 1752 free(qb.buf); 1753 goto oom; 1754 } 1755 if (!url_fmt_append_n(&b, qb.buf, qb.len)) { 1756 free(qb.buf); 1757 goto oom; 1758 }} 1759 free(qb.buf); 1760 } 1761 } 1762 1763 if (hash && hash_len > 0) { 1764 if (hash[0] != '#' && !url_fmt_append_c(&b, '#')) goto oom; 1765 if (!url_fmt_append_n(&b, hash, hash_len)) goto oom; 1766 } 1767 1768 ant_value_t ret = js_mkstr(js, b.buf ? b.buf : "", b.len); 1769 free(b.buf); 1770 return ret; 1771 1772oom: 1773 free(b.buf); 1774 return js_mkerr(js, "allocation failure"); 1775} 1776 1777ant_value_t url_library(ant_t *js) { 1778 ant_value_t lib = js_mkobj(js); 1779 ant_value_t glob = js_glob(js); 1780 1781 js_set(js, lib, "URL", js_get(js, glob, "URL")); 1782 js_set(js, lib, "URLSearchParams",js_get(js, glob, "URLSearchParams")); 1783 js_set(js, lib, "fileURLToPath", js_mkfun(builtin_fileURLToPath)); 1784 js_set(js, lib, "pathToFileURL", js_mkfun(builtin_pathToFileURL)); 1785 js_set(js, lib, "parse", js_mkfun(url_parse)); 1786 js_set(js, lib, "format", js_mkfun(builtin_url_format)); 1787 js_set(js, lib, "default", lib); 1788 1789 return lib; 1790}