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.

bring URL up to spec

+1100 -437
+31
include/modules/url.h
··· 1 1 #ifndef URL_H 2 2 #define URL_H 3 3 4 + #include <stddef.h> 4 5 #include "types.h" 5 6 7 + typedef struct { 8 + char *protocol; 9 + char *username; 10 + char *password; 11 + char *hostname; 12 + char *port; 13 + char *pathname; 14 + char *search; 15 + char *hash; 16 + } url_state_t; 17 + 18 + 6 19 void init_url_module(void); 20 + void url_state_clear(url_state_t *s); 21 + void url_free_state(url_state_t *s); 22 + 7 23 ant_value_t url_library(ant_t *js); 24 + url_state_t *url_get_state(ant_value_t obj); 25 + ant_value_t make_url_obj(ant_t *js, url_state_t *s); 26 + 27 + char *build_href(const url_state_t *s); 28 + char *usp_serialize(ant_t *js, ant_value_t usp); 29 + 30 + char *form_urlencode(const char *str); 31 + char *form_urlencode_n(const char *str, size_t len); 32 + char *form_urldecode(const char *str); 33 + char *url_decode_component(const char *str); 34 + 35 + int parse_url_to_state( 36 + const char *url_str, 37 + const char *base_str, url_state_t *s 38 + ); 8 39 9 40 #endif
+1069 -437
src/modules/url.c
··· 10 10 #include "errors.h" 11 11 #include "internal.h" 12 12 #include "runtime.h" 13 - #include "utils.h" 14 13 #include "descriptors.h" 15 14 15 + #include "silver/engine.h" 16 16 #include "modules/url.h" 17 17 #include "modules/symbol.h" 18 18 19 - typedef struct { 20 - char *protocol; 21 - char *username; 22 - char *password; 23 - char *hostname; 24 - char *port; 25 - char *pathname; 26 - char *search; 27 - char *hash; 28 - } parsed_url_t; 19 + static ant_value_t g_url_proto = 0; 20 + static ant_value_t g_usp_proto = 0; 21 + static ant_value_t g_usp_iter_proto = 0; 22 + 23 + enum { 24 + USP_ITER_ENTRIES = 0, 25 + USP_ITER_KEYS = 1, 26 + USP_ITER_VALUES = 2 27 + }; 28 + 29 + url_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 + 35 + void url_state_clear(url_state_t *s) { 36 + free(s->protocol); free(s->username); free(s->password); 37 + free(s->hostname); free(s->port); free(s->pathname); 38 + free(s->search); free(s->hash); 39 + } 40 + 41 + void url_free_state(url_state_t *s) { 42 + if (!s) return; 43 + url_state_clear(s); 44 + free(s); 45 + } 46 + 47 + static void url_finalize(ant_t *js, ant_object_t *obj) { 48 + if (!obj->extra_slots) return; 49 + ant_extra_slot_t *slots = (ant_extra_slot_t *)obj->extra_slots; 50 + for (uint8_t i = 0; i < obj->extra_count; i++) { 51 + if (slots[i].slot == SLOT_DATA && vtype(slots[i].value) == T_NUM) { 52 + url_free_state((url_state_t *)(uintptr_t)(size_t)js_getnum(slots[i].value)); 53 + return; 54 + }} 55 + } 56 + 57 + static int default_port_for(const char *proto) { 58 + if (!proto) return -1; 59 + if (strcmp(proto, "http:") == 0 || strcmp(proto, "ws:") == 0) return 80; 60 + if (strcmp(proto, "https:") == 0 || strcmp(proto, "wss:") == 0) return 443; 61 + if (strcmp(proto, "ftp:") == 0) return 21; 62 + return -1; 63 + } 64 + 65 + static bool is_special_scheme(const char *proto) { 66 + if (!proto) return false; 67 + return 68 + strcmp(proto, "http:") == 0 || strcmp(proto, "https:") == 0 || 69 + strcmp(proto, "ftp:") == 0 || strcmp(proto, "ws:") == 0 || 70 + strcmp(proto, "wss:") == 0; 71 + } 72 + 73 + char *form_urlencode_n(const char *str, size_t len) { 74 + if (!str) return strdup(""); 75 + char *out = malloc(len * 3 + 1); 76 + 77 + if (!out) return strdup(""); 78 + size_t j = 0; 79 + 80 + for (size_t i = 0; i < len; i++) { 81 + unsigned char c = (unsigned char)str[i]; 82 + if (isalnum(c) || c == '*' || c == '-' || c == '.' || c == '_') out[j++] = (char)c; 83 + else if (c == ' ') out[j++] = '+'; 84 + else { 85 + snprintf(out + j, 4, "%%%02X", c); 86 + j += 3; 87 + }} 88 + 89 + out[j] = '\0'; 90 + return out; 91 + } 29 92 30 - static void free_parsed_url(parsed_url_t *p) { 31 - if (p->protocol) free(p->protocol); 32 - if (p->username) free(p->username); 33 - if (p->password) free(p->password); 34 - if (p->hostname) free(p->hostname); 35 - if (p->port) free(p->port); 36 - if (p->pathname) free(p->pathname); 37 - if (p->search) free(p->search); 38 - if (p->hash) free(p->hash); 93 + char *form_urlencode(const char *str) { 94 + if (!str) return strdup(""); 95 + return form_urlencode_n(str, strlen(str)); 39 96 } 40 97 41 - static char *url_encode_component(const char *str) { 98 + char *form_urldecode(const char *str) { 42 99 if (!str) return strdup(""); 43 100 size_t len = strlen(str); 44 - char *out = malloc(len * 3 + 1); 45 - if (!out) return NULL; 101 + char *out = malloc(len + 1); 102 + 103 + if (!out) return strdup(""); 46 104 size_t j = 0; 105 + 47 106 for (size_t i = 0; i < len; i++) { 48 - unsigned char c = (unsigned char)str[i]; 49 - if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { 50 - out[j++] = c; 51 - } else { 52 - snprintf(out + j, 4, "%%%02X", c); 53 - j += 3; 54 - } 107 + if (str[i] == '+') out[j++] = ' '; 108 + else if ( 109 + str[i] == '%' && i + 2 < len && 110 + isxdigit((unsigned char)str[i+1]) && 111 + isxdigit((unsigned char)str[i+2]) 112 + ) { 113 + int hi = isdigit((unsigned char)str[i+1]) ? str[i+1]-'0' : tolower((unsigned char)str[i+1])-'a'+10; 114 + int lo = isdigit((unsigned char)str[i+2]) ? str[i+2]-'0' : tolower((unsigned char)str[i+2])-'a'+10; 115 + out[j++] = (char)((hi << 4) | lo); 116 + i += 2; 117 + } else out[j++] = str[i]; 55 118 } 119 + 56 120 out[j] = '\0'; 57 121 return out; 58 122 } 59 123 60 - static char *url_decode_component(const char *str) { 124 + char *url_decode_component(const char *str) { 61 125 if (!str) return strdup(""); 62 126 size_t len = strlen(str); 63 127 char *out = malloc(len + 1); 64 - if (!out) return NULL; 128 + 129 + if (!out) return strdup(""); 65 130 size_t j = 0; 131 + 66 132 for (size_t i = 0; i < len; i++) { 67 - if (str[i] == '%' && i + 2 < len && isxdigit(str[i+1]) && isxdigit(str[i+2])) { 68 - int hi = isdigit(str[i+1]) ? str[i+1] - '0' : tolower(str[i+1]) - 'a' + 10; 69 - int lo = isdigit(str[i+2]) ? str[i+2] - '0' : tolower(str[i+2]) - 'a' + 10; 133 + if ( 134 + str[i] == '%' && i + 2 < len && 135 + isxdigit((unsigned char)str[i+1]) && 136 + isxdigit((unsigned char)str[i+2]) 137 + ) { 138 + int hi = isdigit((unsigned char)str[i+1]) ? str[i+1]-'0' : tolower((unsigned char)str[i+1])-'a'+10; 139 + int lo = isdigit((unsigned char)str[i+2]) ? str[i+2]-'0' : tolower((unsigned char)str[i+2])-'a'+10; 70 140 out[j++] = (char)((hi << 4) | lo); 71 141 i += 2; 72 - } 73 - else if (str[i] == '+') out[j++] = ' '; 74 - else out[j++] = str[i]; 142 + } else out[j++] = str[i]; 75 143 } 144 + 76 145 out[j] = '\0'; 77 146 return out; 78 147 } 79 148 80 - static char *uri_range_dup(const UriTextRangeA *range) { 81 - if (!range->first || !range->afterLast) return strdup(""); 82 - size_t len = (size_t)(range->afterLast - range->first); 83 - return strndup(range->first, len); 149 + static char *userinfo_encode(const char *str) { 150 + if (!str) return strdup(""); 151 + size_t len = strlen(str); 152 + char *out = malloc(len * 3 + 1); 153 + 154 + if (!out) return strdup(""); 155 + size_t j = 0; 156 + 157 + for (size_t i = 0; i < len; i++) { 158 + unsigned char c = (unsigned char)str[i]; 159 + if ( 160 + isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~' || 161 + c == '!' || c == '$' || c == '&' || c == '\'' || c == '(' || 162 + c == ')' || c == '*' || c == '+' || c == ',' || c == ';' || 163 + c == '=' || c == ':' 164 + ) out[j++] = (char)c; else { snprintf(out + j, 4, "%%%02X", c); j += 3; } 165 + } 166 + 167 + out[j] = '\0'; 168 + return out; 169 + } 170 + 171 + static char *uri_range_dup(const UriTextRangeA *r) { 172 + if (!r->first || !r->afterLast) return strdup(""); 173 + return strndup(r->first, (size_t)(r->afterLast - r->first)); 84 174 } 85 175 86 - static void uri_to_parsed(const UriUriA *uri, parsed_url_t *out) { 176 + static void uri_to_state(const UriUriA *uri, url_state_t *s) { 87 177 char *scheme = uri_range_dup(&uri->scheme); 88 178 size_t slen = strlen(scheme); 89 - out->protocol = malloc(slen + 2); 90 - memcpy(out->protocol, scheme, slen); 91 - out->protocol[slen] = ':'; 92 - out->protocol[slen + 1] = '\0'; 179 + for (size_t i = 0; i < slen; i++) scheme[i] = (char)tolower((unsigned char)scheme[i]); 180 + 181 + s->protocol = malloc(slen + 2); 182 + memcpy(s->protocol, scheme, slen); 183 + s->protocol[slen] = ':'; 184 + s->protocol[slen + 1] = '\0'; 93 185 free(scheme); 94 186 95 187 char *userinfo = uri_range_dup(&uri->userInfo); 96 188 char *colon = strchr(userinfo, ':'); 189 + 97 190 if (colon) { 98 191 *colon = '\0'; 99 - out->username = strdup(userinfo); 100 - out->password = strdup(colon + 1); 192 + s->username = strdup(userinfo); 193 + s->password = strdup(colon + 1); 101 194 } else { 102 - out->username = strdup(userinfo); 103 - out->password = strdup(""); 195 + s->username = strdup(userinfo); 196 + s->password = strdup(""); 104 197 } 198 + 105 199 free(userinfo); 200 + s->hostname = uri_range_dup(&uri->hostText); 106 201 107 - out->hostname = uri_range_dup(&uri->hostText); 108 - out->port = uri_range_dup(&uri->portText); 202 + char *port = uri_range_dup(&uri->portText); 203 + int def = default_port_for(s->protocol); 204 + if (def > 0 && *port && atoi(port) == def) { 205 + free(port); 206 + port = strdup(""); 207 + } s->port = port; 109 208 110 - size_t path_cap = 1; 209 + size_t path_cap = 2; 111 210 for (UriPathSegmentA *seg = uri->pathHead; seg; seg = seg->next) 112 211 path_cap += (size_t)(seg->text.afterLast - seg->text.first) + 1; 212 + 113 213 char *path = malloc(path_cap + 1); 114 214 size_t pos = 0; 215 + 115 216 for (UriPathSegmentA *seg = uri->pathHead; seg; seg = seg->next) { 116 217 path[pos++] = '/'; 117 - size_t slen_uri = (size_t)(seg->text.afterLast - seg->text.first); 118 - memcpy(path + pos, seg->text.first, slen_uri); 119 - pos += slen_uri; 218 + size_t seglen = (size_t)(seg->text.afterLast - seg->text.first); 219 + memcpy(path + pos, seg->text.first, seglen); 220 + pos += seglen; 120 221 } 222 + 121 223 if (pos == 0) path[pos++] = '/'; 122 224 path[pos] = '\0'; 123 - out->pathname = path; 225 + s->pathname = path; 124 226 125 227 char *query = uri_range_dup(&uri->query); 126 228 if (*query) { 127 - out->search = malloc(strlen(query) + 2); 128 - out->search[0] = '?'; 129 - strcpy(out->search + 1, query); 130 - } else out->search = strdup(""); 229 + size_t qlen = strlen(query); 230 + s->search = malloc(qlen + 2); 231 + s->search[0] = '?'; 232 + memcpy(s->search + 1, query, qlen + 1); 233 + } else s->search = strdup(""); 131 234 free(query); 132 235 133 236 char *frag = uri_range_dup(&uri->fragment); 134 237 if (*frag) { 135 - out->hash = malloc(strlen(frag) + 2); 136 - out->hash[0] = '#'; 137 - strcpy(out->hash + 1, frag); 138 - } else out->hash = strdup(""); 238 + size_t flen = strlen(frag); 239 + s->hash = malloc(flen + 2); 240 + s->hash[0] = '#'; 241 + memcpy(s->hash + 1, frag, flen + 1); 242 + } else s->hash = strdup(""); 139 243 free(frag); 140 244 } 141 245 142 - static const char *coerce_to_string(ant_t *js, ant_value_t val, size_t *len) { 143 - if (vtype(val) == T_STR) return js_getstr(js, val, len); 144 - if (is_object_type(val)) { 145 - ant_value_t href = js_get(js, val, "href"); 146 - if (vtype(href) == T_STR) return js_getstr(js, href, len); 147 - } 148 - return NULL; 149 - } 246 + int parse_url_to_state(const char *url_str, const char *base_str, url_state_t *s) { 247 + memset(s, 0, sizeof(*s)); 248 + const char *errpos; 150 249 151 - static int parse_url(const char *url_str, const char *base_str, parsed_url_t *out) { 152 - memset(out, 0, sizeof(*out)); 250 + if (base_str) { 251 + UriUriA base_uri, ref_uri, resolved; 252 + if (uriParseSingleUriA(&base_uri, base_str, &errpos) != URI_SUCCESS) return -1; 253 + 254 + char *base_scheme = uri_range_dup(&base_uri.scheme); 255 + size_t bslen = strlen(base_scheme); 256 + for (size_t i = 0; i < bslen; i++) base_scheme[i] = (char)tolower((unsigned char)base_scheme[i]); 257 + 258 + char proto_buf[bslen + 2]; 259 + memcpy(proto_buf, base_scheme, bslen); 260 + proto_buf[bslen] = ':'; 261 + proto_buf[bslen + 1] = '\0'; 262 + free(base_scheme); 263 + 264 + bool base_special = is_special_scheme(proto_buf); 265 + if (!base_special) { 266 + const char *after_colon = strchr(base_str, ':'); 267 + if (after_colon) { 268 + after_colon++; 269 + bool is_opaque = (*after_colon != '/' && *after_colon != '\0'); 270 + if (is_opaque) { uriFreeUriMembersA(&base_uri); return -1; } 271 + }} 153 272 154 - UriUriA uri; 155 - const char *errorPos; 156 - 157 - if (base_str && !strstr(url_str, "://")) { 158 - UriUriA base_uri, rel_uri, resolved; 159 - 160 - if (uriParseSingleUriA(&base_uri, base_str, &errorPos) != URI_SUCCESS) 161 - return -1; 162 - 163 - if (uriParseSingleUriA(&rel_uri, url_str, &errorPos) != URI_SUCCESS) { 273 + if (uriParseSingleUriA(&ref_uri, url_str, &errpos) != URI_SUCCESS) { 164 274 uriFreeUriMembersA(&base_uri); 165 275 return -1; 166 276 } 167 277 168 - if (uriAddBaseUriA(&resolved, &rel_uri, &base_uri) != URI_SUCCESS) { 278 + if (uriAddBaseUriA(&resolved, &ref_uri, &base_uri) != URI_SUCCESS) { 169 279 uriFreeUriMembersA(&base_uri); 170 - uriFreeUriMembersA(&rel_uri); 280 + uriFreeUriMembersA(&ref_uri); 171 281 return -1; 172 282 } 173 283 174 284 uriNormalizeSyntaxA(&resolved); 175 - uri_to_parsed(&resolved, out); 285 + if (!resolved.scheme.first || resolved.scheme.first == resolved.scheme.afterLast) { 286 + uriFreeUriMembersA(&resolved); 287 + uriFreeUriMembersA(&ref_uri); 288 + uriFreeUriMembersA(&base_uri); 289 + return -1; 290 + } 176 291 292 + uri_to_state(&resolved, s); 177 293 uriFreeUriMembersA(&resolved); 178 - uriFreeUriMembersA(&rel_uri); 294 + uriFreeUriMembersA(&ref_uri); 179 295 uriFreeUriMembersA(&base_uri); 296 + 180 297 return 0; 181 298 } 182 299 183 - if (uriParseSingleUriA(&uri, url_str, &errorPos) != URI_SUCCESS) 300 + UriUriA uri; 301 + if (uriParseSingleUriA(&uri, url_str, &errpos) != URI_SUCCESS) return -1; 302 + if (!uri.scheme.first || uri.scheme.first == uri.scheme.afterLast) { 303 + uriFreeUriMembersA(&uri); 184 304 return -1; 185 - 186 - uri_to_parsed(&uri, out); 305 + } 306 + 307 + uriNormalizeSyntaxA(&uri); 308 + uri_to_state(&uri, s); 187 309 uriFreeUriMembersA(&uri); 310 + 188 311 return 0; 189 312 } 190 313 191 - static char *build_href( 192 - const char *protocol, const char *username, const char *password, 193 - const char *hostname, const char *port, const char *pathname, 194 - const char *search, const char *hash 195 - ) { 196 - size_t len = strlen(protocol) + 2 + strlen(hostname) + strlen(pathname) + strlen(search) + strlen(hash) + 32; 197 - if (username && *username) len += strlen(username) + strlen(password) + 2; 198 - if (port && *port) len += strlen(port) + 1; 314 + char *build_href(const url_state_t *s) { 315 + size_t len = strlen(s->protocol) + 2; 316 + len += strlen(s->hostname) + strlen(s->pathname) + strlen(s->search) + strlen(s->hash) + 32; 317 + 318 + if (s->username && *s->username) len += strlen(s->username) + 1; 319 + if (s->password && *s->password) len += strlen(s->password) + 1; 320 + if (s->port && *s->port) len += strlen(s->port) + 1; 199 321 200 322 char *href = malloc(len); 201 - if (!href) return NULL; 323 + if (!href) return strdup(""); 324 + size_t pos = 0; 325 + pos += (size_t)sprintf(href + pos, "%s//", s->protocol); 202 326 203 - size_t used = 0; 204 - size_t remaining = len; 327 + if (s->username && *s->username) { 328 + pos += (size_t)sprintf(href + pos, "%s", s->username); 329 + if (s->password && *s->password) 330 + pos += (size_t)sprintf(href + pos, ":%s", s->password); 331 + href[pos++] = '@'; 332 + } 333 + 334 + pos += (size_t)sprintf(href + pos, "%s", s->hostname); 335 + if (s->port && *s->port) 336 + pos += (size_t)sprintf(href + pos, ":%s", s->port); 337 + 338 + pos += (size_t)sprintf(href + pos, "%s%s%s", s->pathname, s->search, s->hash); 339 + href[pos] = '\0'; 340 + 341 + return href; 342 + } 343 + 344 + static const char *coerce_to_string(ant_t *js, ant_value_t val, size_t *len) { 345 + if (vtype(val) == T_STR) return js_getstr(js, val, len); 346 + if (is_object_type(val)) { 347 + ant_value_t href = js_getprop_fallback(js, val, "href"); 348 + if (vtype(href) == T_STR) return js_getstr(js, href, len); 349 + } 350 + return NULL; 351 + } 352 + 353 + static ant_value_t parse_query_to_arr(ant_t *js, const char *query) { 354 + ant_value_t arr = js_mkarr(js); 355 + if (!query || !*query) return arr; 356 + const char *p = query; 357 + 358 + while (*p) { 359 + const char *amp = strchr(p, '&'); 360 + size_t plen = amp ? (size_t)(amp - p) : strlen(p); 361 + if (plen == 0) { p = amp ? amp + 1 : p + 1; continue; } 362 + char *pair = strndup(p, plen); 363 + if (!pair) { p = amp ? amp + 1 : p + plen; continue; } 364 + 365 + char *eq = strchr(pair, '='); 366 + char *raw_v = eq ? ((*eq = '\0'), eq + 1) : ""; 367 + char *k = form_urldecode(pair); 368 + char *v = form_urldecode(raw_v); 369 + 370 + ant_value_t entry = js_mkarr(js); 371 + js_arr_push(js, entry, js_mkstr(js, k, strlen(k))); 372 + js_arr_push(js, entry, js_mkstr(js, v, strlen(v))); 373 + js_arr_push(js, arr, entry); 374 + free(pair); free(k); free(v); 375 + p = amp ? amp + 1 : p + plen; 376 + } 377 + 378 + return arr; 379 + } 205 380 206 - int written = snprintf(href + used, remaining, "%s//", protocol); 207 - if (written < 0) { href[0] = '\0'; return href; } 208 - if ((size_t)written >= remaining) { href[len - 1] = '\0'; return href; } 209 - used += (size_t)written; 210 - remaining -= (size_t)written; 381 + char *usp_serialize(ant_t *js, ant_value_t usp) { 382 + ant_value_t entries = js_get_slot(usp, SLOT_ENTRIES); 383 + ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; 211 384 212 - if (username && *username) { 213 - written = snprintf(href + used, remaining, "%s", username); 214 - if (written < 0) { href[0] = '\0'; return href; } 215 - if ((size_t)written >= remaining) { href[len - 1] = '\0'; return href; } 216 - used += (size_t)written; 217 - remaining -= (size_t)written; 385 + size_t cap = 256; 386 + char *buf = malloc(cap); 387 + if (!buf) return strdup(""); 388 + size_t pos = 0; 218 389 219 - if (password && *password) { 220 - written = snprintf(href + used, remaining, ":%s", password); 221 - if (written < 0) { href[0] = '\0'; return href; } 222 - if ((size_t)written >= remaining) { href[len - 1] = '\0'; return href; } 223 - used += (size_t)written; 224 - remaining -= (size_t)written; 390 + for (ant_offset_t i = 0; i < len; i++) { 391 + ant_value_t entry = js_arr_get(js, entries, i); 392 + size_t klen = 0, vlen = 0; 393 + 394 + char *k = js_getstr(js, js_arr_get(js, entry, 0), &klen); 395 + char *v = js_getstr(js, js_arr_get(js, entry, 1), &vlen); 396 + char *ek = k ? form_urlencode_n(k, klen) : strdup(""); 397 + char *ev = v ? form_urlencode_n(v, vlen) : strdup(""); 398 + 399 + size_t needed = strlen(ek) + strlen(ev) + 3; 400 + if (pos + needed >= cap) { 401 + cap = cap * 2 + needed; 402 + buf = realloc(buf, cap); 403 + if (!buf) { free(ek); free(ev); return strdup(""); } 225 404 } 226 - 227 - written = snprintf(href + used, remaining, "@"); 228 - if (written < 0) { href[0] = '\0'; return href; } 229 - if ((size_t)written >= remaining) { href[len - 1] = '\0'; return href; } 230 - used += (size_t)written; 231 - remaining -= (size_t)written; 405 + 406 + if (pos > 0) buf[pos++] = '&'; 407 + pos += (size_t)sprintf(buf + pos, "%s=%s", ek, ev); 408 + free(ek); free(ev); 232 409 } 410 + 411 + buf[pos] = '\0'; 412 + return buf; 413 + } 233 414 234 - written = snprintf(href + used, remaining, "%s", hostname); 235 - if (written < 0) { href[0] = '\0'; return href; } 236 - if ((size_t)written >= remaining) { href[len - 1] = '\0'; return href; } 237 - used += (size_t)written; 238 - remaining -= (size_t)written; 415 + static void usp_push_to_url(ant_t *js, ant_value_t usp) { 416 + ant_value_t url_obj = js_get_slot(usp, SLOT_DATA); 417 + if (!is_special_object(url_obj)) return; 418 + url_state_t *s = url_get_state(url_obj); 419 + 420 + if (!s) return; 421 + char *qs = usp_serialize(js, usp); 422 + free(s->search); 423 + 424 + if (*qs) { 425 + size_t qlen = strlen(qs); 426 + s->search = malloc(qlen + 2); 427 + s->search[0] = '?'; 428 + memcpy(s->search + 1, qs, qlen + 1); 429 + } else s->search = strdup(""); 430 + 431 + free(qs); 432 + } 239 433 240 - if (port && *port) { 241 - written = snprintf(href + used, remaining, ":%s", port); 242 - if (written < 0) { href[0] = '\0'; return href; } 243 - if ((size_t)written >= remaining) { href[len - 1] = '\0'; return href; } 244 - used += (size_t)written; 245 - remaining -= (size_t)written; 246 - } 434 + static void url_sync_usp(ant_t *js, ant_value_t url_obj, const char *query) { 435 + ant_value_t usp = js_get_slot(url_obj, SLOT_ENTRIES); 436 + if (!is_special_object(usp)) return; 437 + ant_value_t new_entries = parse_query_to_arr(js, query); 438 + js_set_slot_wb(js, usp, SLOT_ENTRIES, new_entries); 439 + } 247 440 248 - written = snprintf(href + used, remaining, "%s%s%s", pathname, search, hash); 249 - if (written < 0) { href[0] = '\0'; return href; } 250 - if ((size_t)written >= remaining) { href[len - 1] = '\0'; return href; } 441 + static ant_value_t make_usp_for_url(ant_t *js, ant_value_t url_obj, const char *query) { 442 + ant_value_t usp = js_mkobj(js); 443 + js_set_proto_init(usp, g_usp_proto); 444 + js_set_slot_wb(js, usp, SLOT_DATA, url_obj); 445 + js_set_slot(usp, SLOT_ENTRIES, parse_query_to_arr(js, query)); 446 + return usp; 447 + } 251 448 252 - return href; 449 + static ant_value_t url_get_href(ant_t *js, ant_value_t *args, int nargs) { 450 + url_state_t *s = url_get_state(js->this_val); 451 + if (!s) return js_mkstr(js, "", 0); 452 + char *href = build_href(s); 453 + ant_value_t ret = js_mkstr(js, href, strlen(href)); 454 + free(href); 455 + return ret; 456 + } 457 + 458 + static ant_value_t url_get_protocol(ant_t *js, ant_value_t *args, int nargs) { 459 + url_state_t *s = url_get_state(js->this_val); 460 + if (!s) return js_mkstr(js, "", 0); 461 + return js_mkstr(js, s->protocol, strlen(s->protocol)); 253 462 } 254 463 255 - static void update_url_href(ant_t *js, ant_value_t url_obj) { 256 - char *protocol = js_getstr(js, js_get(js, url_obj, "protocol"), NULL); 257 - char *username = js_getstr(js, js_get(js, url_obj, "username"), NULL); 258 - char *password = js_getstr(js, js_get(js, url_obj, "password"), NULL); 259 - char *hostname = js_getstr(js, js_get(js, url_obj, "hostname"), NULL); 260 - char *port = js_getstr(js, js_get(js, url_obj, "port"), NULL); 261 - char *pathname = js_getstr(js, js_get(js, url_obj, "pathname"), NULL); 262 - char *search = js_getstr(js, js_get(js, url_obj, "search"), NULL); 263 - char *hash = js_getstr(js, js_get(js, url_obj, "hash"), NULL); 464 + static ant_value_t url_get_username(ant_t *js, ant_value_t *args, int nargs) { 465 + url_state_t *s = url_get_state(js->this_val); 466 + if (!s) return js_mkstr(js, "", 0); 467 + return js_mkstr(js, s->username, strlen(s->username)); 468 + } 264 469 265 - char *host; 266 - if (port && *port) { 267 - size_t hlen = strlen(hostname) + strlen(port) + 2; 268 - host = malloc(hlen); 269 - snprintf(host, hlen, "%s:%s", hostname, port); 270 - } else { 271 - host = strdup(hostname ? hostname : ""); 470 + static ant_value_t url_get_password(ant_t *js, ant_value_t *args, int nargs) { 471 + url_state_t *s = url_get_state(js->this_val); 472 + if (!s) return js_mkstr(js, "", 0); 473 + return js_mkstr(js, s->password, strlen(s->password)); 474 + } 475 + 476 + static ant_value_t url_get_host(ant_t *js, ant_value_t *args, int nargs) { 477 + url_state_t *s = url_get_state(js->this_val); 478 + if (!s) return js_mkstr(js, "", 0); 479 + if (s->port && *s->port) { 480 + size_t len = strlen(s->hostname) + strlen(s->port) + 2; 481 + char *host = malloc(len); 482 + snprintf(host, len, "%s:%s", s->hostname, s->port); 483 + ant_value_t ret = js_mkstr(js, host, strlen(host)); 484 + free(host); 485 + return ret; 272 486 } 273 - js_set(js, url_obj, "host", js_mkstr(js, host, strlen(host))); 487 + return js_mkstr(js, s->hostname, strlen(s->hostname)); 488 + } 489 + 490 + static ant_value_t url_get_hostname(ant_t *js, ant_value_t *args, int nargs) { 491 + url_state_t *s = url_get_state(js->this_val); 492 + if (!s) return js_mkstr(js, "", 0); 493 + return js_mkstr(js, s->hostname, strlen(s->hostname)); 494 + } 495 + 496 + static ant_value_t url_get_port(ant_t *js, ant_value_t *args, int nargs) { 497 + url_state_t *s = url_get_state(js->this_val); 498 + if (!s) return js_mkstr(js, "", 0); 499 + return js_mkstr(js, s->port, strlen(s->port)); 500 + } 274 501 275 - char *origin; 276 - if (port && *port) { 277 - size_t olen = strlen(protocol) + strlen(hostname) + strlen(port) + 8; 278 - origin = malloc(olen); 279 - snprintf(origin, olen, "%s//%s:%s", protocol ? protocol : "", hostname ? hostname : "", port); 280 - } else { 281 - size_t olen = strlen(protocol) + strlen(hostname) + 8; 282 - origin = malloc(olen); 283 - snprintf(origin, olen, "%s//%s", protocol ? protocol : "", hostname ? hostname : ""); 502 + static ant_value_t url_get_pathname(ant_t *js, ant_value_t *args, int nargs) { 503 + url_state_t *s = url_get_state(js->this_val); 504 + if (!s) return js_mkstr(js, "/", 1); 505 + return js_mkstr(js, s->pathname, strlen(s->pathname)); 506 + } 507 + 508 + static ant_value_t url_get_search(ant_t *js, ant_value_t *args, int nargs) { 509 + url_state_t *s = url_get_state(js->this_val); 510 + if (!s) return js_mkstr(js, "", 0); 511 + return js_mkstr(js, s->search, strlen(s->search)); 512 + } 513 + 514 + static ant_value_t url_get_hash(ant_t *js, ant_value_t *args, int nargs) { 515 + url_state_t *s = url_get_state(js->this_val); 516 + if (!s) return js_mkstr(js, "", 0); 517 + return js_mkstr(js, s->hash, strlen(s->hash)); 518 + } 519 + 520 + static ant_value_t url_get_origin(ant_t *js, ant_value_t *args, int nargs) { 521 + url_state_t *s = url_get_state(js->this_val); 522 + if (!s || !is_special_scheme(s->protocol)) return js_mkstr(js, "null", 4); 523 + 524 + size_t proto_len = strlen(s->protocol) - 1; 525 + size_t host_len = strlen(s->hostname); 526 + size_t port_len = (s->port && *s->port) ? strlen(s->port) + 1 : 0; 527 + size_t total = proto_len + 3 + host_len + port_len + 1; 528 + char *origin = malloc(total); 529 + 530 + size_t pos = 0; 531 + memcpy(origin + pos, s->protocol, proto_len); pos += proto_len; 532 + memcpy(origin + pos, "://", 3); pos += 3; 533 + memcpy(origin + pos, s->hostname, host_len); pos += host_len; 534 + 535 + if (s->port && *s->port) { 536 + origin[pos++] = ':'; 537 + memcpy(origin + pos, s->port, strlen(s->port)); 538 + pos += strlen(s->port); 284 539 } 285 - js_set(js, url_obj, "origin", js_mkstr(js, origin, strlen(origin))); 540 + 541 + origin[pos] = '\0'; 542 + ant_value_t ret = js_mkstr(js, origin, pos); 543 + free(origin); 544 + 545 + return ret; 546 + } 286 547 287 - char *href = build_href( 288 - protocol ? protocol : "", username ? username : "", 289 - password ? password : "", hostname ? hostname : "", 290 - port ? port : "", pathname ? pathname : "/", 291 - search ? search : "", hash ? hash : "" 292 - ); 548 + static ant_value_t url_get_searchParams(ant_t *js, ant_value_t *args, int nargs) { 549 + ant_value_t usp = js_get_slot(js->this_val, SLOT_ENTRIES); 550 + if (vtype(usp) == T_OBJ) return usp; 551 + return js_mkundef(); 552 + } 553 + 554 + static ant_value_t url_set_href(ant_t *js, ant_value_t *args, int nargs) { 555 + if (nargs < 1) return js_mkundef(); 556 + url_state_t *s = url_get_state(js->this_val); 557 + if (!s) return js_mkundef(); 558 + 559 + const char *val = js_getstr(js, args[0], NULL); 560 + if (!val) return js_mkerr(js, "TypeError: Invalid URL"); 561 + 562 + url_state_t tmp; 563 + if (parse_url_to_state(val, NULL, &tmp) != 0) 564 + return js_mkerr(js, "TypeError: Invalid URL"); 565 + 566 + free(s->protocol); s->protocol = tmp.protocol; 567 + free(s->username); s->username = tmp.username; 568 + free(s->password); s->password = tmp.password; 569 + free(s->hostname); s->hostname = tmp.hostname; 570 + free(s->port); s->port = tmp.port; 571 + free(s->pathname); s->pathname = tmp.pathname; 572 + free(s->search); s->search = tmp.search; 573 + free(s->hash); s->hash = tmp.hash; 574 + 575 + const char *q = (s->search[0] == '?') ? s->search + 1 : ""; 576 + url_sync_usp(js, js->this_val, q); 293 577 294 - js_set(js, url_obj, "href", js_mkstr(js, href, strlen(href))); 578 + return js_mkundef(); 579 + } 295 580 296 - free(host); 297 - free(origin); 298 - free(href); 581 + static ant_value_t url_set_protocol(ant_t *js, ant_value_t *args, int nargs) { 582 + if (nargs < 1) return js_mkundef(); 583 + url_state_t *s = url_get_state(js->this_val); 584 + 585 + if (!s) return js_mkundef(); 586 + const char *val = js_getstr(js, args[0], NULL); 587 + 588 + if (!val || !*val) return js_mkundef(); 589 + const char *colon = strchr(val, ':'); 590 + 591 + size_t slen = colon ? (size_t)(colon - val) : strlen(val); 592 + if (!slen || !isalpha((unsigned char)val[0])) return js_mkundef(); 593 + 594 + for (size_t i = 1; i < slen; i++) { 595 + unsigned char c = (unsigned char)val[i]; 596 + if (!isalnum(c) && c != '+' && c != '-' && c != '.') return js_mkundef(); 597 + } 598 + 599 + free(s->protocol); 600 + s->protocol = malloc(slen + 2); 601 + for (size_t i = 0; i < slen; i++) s->protocol[i] = (char)tolower((unsigned char)val[i]); 602 + s->protocol[slen] = ':'; 603 + s->protocol[slen + 1] = '\0'; 604 + 605 + if (s->port && *s->port) { 606 + int def = default_port_for(s->protocol); 607 + if (def > 0 && atoi(s->port) == def) { free(s->port); s->port = strdup(""); } 608 + } 609 + 610 + return js_mkundef(); 611 + } 612 + 613 + static ant_value_t url_set_username(ant_t *js, ant_value_t *args, int nargs) { 614 + if (nargs < 1) return js_mkundef(); 615 + url_state_t *s = url_get_state(js->this_val); 616 + if (!s) return js_mkundef(); 617 + const char *val = js_getstr(js, args[0], NULL); 618 + if (!val) return js_mkundef(); 619 + free(s->username); 620 + s->username = userinfo_encode(val); 621 + return js_mkundef(); 299 622 } 300 623 301 - static ant_value_t url_toString(ant_t *js, ant_value_t *args, int nargs) { 302 - return js_get(js, js_getthis(js), "href"); 624 + static ant_value_t url_set_password(ant_t *js, ant_value_t *args, int nargs) { 625 + if (nargs < 1) return js_mkundef(); 626 + url_state_t *s = url_get_state(js->this_val); 627 + if (!s) return js_mkundef(); 628 + const char *val = js_getstr(js, args[0], NULL); 629 + if (!val) return js_mkundef(); 630 + free(s->password); 631 + s->password = userinfo_encode(val); 632 + return js_mkundef(); 303 633 } 304 634 305 - static ant_value_t js_URLSearchParams(ant_t *js, ant_value_t *args, int nargs) { 306 - ant_value_t usp = js_mkobj(js); 307 - ant_value_t proto = js_get_ctor_proto(js, "URLSearchParams", 15); 308 - if (is_special_object(proto)) js_set_proto_init(usp, proto); 635 + static ant_value_t url_set_host(ant_t *js, ant_value_t *args, int nargs) { 636 + if (nargs < 1) return js_mkundef(); 637 + url_state_t *s = url_get_state(js->this_val); 638 + if (!s) return js_mkundef(); 639 + const char *val = js_getstr(js, args[0], NULL); 640 + if (!val) return js_mkundef(); 641 + const char *colon = strchr(val, ':'); 309 642 310 - ant_value_t entries = js_mkarr(js); 311 - js_set_slot(usp, SLOT_ENTRIES, entries); 643 + if (colon) { 644 + free(s->hostname); 645 + s->hostname = strndup(val, (size_t)(colon - val)); 646 + const char *port_str = colon + 1; 647 + free(s->port); 648 + if (*port_str) { 649 + int p = atoi(port_str); 650 + int def = default_port_for(s->protocol); 651 + s->port = (def > 0 && p == def) ? strdup("") : strdup(port_str); 652 + } else s->port = strdup(""); 653 + } else { 654 + free(s->hostname); 655 + s->hostname = strdup(val); 656 + } 657 + 658 + return js_mkundef(); 659 + } 312 660 313 - if (nargs < 1 || vtype(args[0]) != T_STR) return usp; 314 - char *init = js_getstr(js, args[0], NULL); 315 - if (!init) return usp; 661 + static ant_value_t url_set_hostname(ant_t *js, ant_value_t *args, int nargs) { 662 + if (nargs < 1) return js_mkundef(); 663 + url_state_t *s = url_get_state(js->this_val); 664 + if (!s) return js_mkundef(); 665 + const char *val = js_getstr(js, args[0], NULL); 666 + if (!val) return js_mkundef(); 667 + free(s->hostname); 668 + const char *colon = strchr(val, ':'); 669 + s->hostname = colon ? strndup(val, (size_t)(colon - val)) : strdup(val); 670 + return js_mkundef(); 671 + } 316 672 317 - const char *p = init; 318 - if (*p == '?') p++; 673 + static ant_value_t url_set_port(ant_t *js, ant_value_t *args, int nargs) { 674 + if (nargs < 1) return js_mkundef(); 675 + url_state_t *s = url_get_state(js->this_val); 676 + if (!s) return js_mkundef(); 677 + const char *val = js_getstr(js, args[0], NULL); 678 + if (!val) return js_mkundef(); 679 + free(s->port); 680 + if (!*val) { s->port = strdup(""); return js_mkundef(); } 681 + int p = atoi(val); 682 + if (p < 0 || p > 65535) { s->port = strdup(""); return js_mkundef(); } 683 + int def = default_port_for(s->protocol); 684 + if (def > 0 && p == def) s->port = strdup(""); else { 685 + char buf[8]; 686 + snprintf(buf, sizeof(buf), "%d", p); 687 + s->port = strdup(buf); 688 + } 689 + return js_mkundef(); 690 + } 319 691 320 - parse_pair: 321 - if (!*p) return usp; 322 - const char *amp = strchr(p, '&'); 323 - size_t plen = amp ? (size_t)(amp - p) : strlen(p); 324 - char *pair = strndup(p, plen); 325 - char *eq = strchr(pair, '='); 326 - char *key = pair, *val = eq ? (eq[0] = '\0', eq + 1) : ""; 327 - char *dk = url_decode_component(key); 328 - char *dv = url_decode_component(val); 329 - ant_value_t entry = js_mkarr(js); 330 - js_arr_push(js, entry, js_mkstr(js, dk, strlen(dk))); 331 - js_arr_push(js, entry, js_mkstr(js, dv, strlen(dv))); 332 - js_arr_push(js, entries, entry); 333 - free(pair); free(dk); free(dv); 334 - if (!amp) return usp; 335 - p = amp + 1; 336 - goto parse_pair; 692 + static ant_value_t url_set_pathname(ant_t *js, ant_value_t *args, int nargs) { 693 + if (nargs < 1) return js_mkundef(); 694 + url_state_t *s = url_get_state(js->this_val); 695 + if (!s) return js_mkundef(); 696 + const char *val = js_getstr(js, args[0], NULL); 697 + if (!val) return js_mkundef(); 698 + free(s->pathname); 699 + if (is_special_scheme(s->protocol) && val[0] != '/') { 700 + size_t vlen = strlen(val); 701 + s->pathname = malloc(vlen + 2); 702 + s->pathname[0] = '/'; 703 + memcpy(s->pathname + 1, val, vlen + 1); 704 + } else s->pathname = strdup(val); 705 + return js_mkundef(); 706 + } 707 + 708 + static ant_value_t url_set_search(ant_t *js, ant_value_t *args, int nargs) { 709 + if (nargs < 1) return js_mkundef(); 710 + url_state_t *s = url_get_state(js->this_val); 711 + if (!s) return js_mkundef(); 712 + const char *val = js_getstr(js, args[0], NULL); 713 + if (!val) return js_mkundef(); 714 + const char *q = (val[0] == '?') ? val + 1 : val; 715 + free(s->search); 716 + if (*q) { 717 + size_t qlen = strlen(q); 718 + s->search = malloc(qlen + 2); 719 + s->search[0] = '?'; 720 + memcpy(s->search + 1, q, qlen + 1); 721 + } else s->search = strdup(""); 722 + url_sync_usp(js, js->this_val, *q ? q : ""); 723 + return js_mkundef(); 724 + } 725 + 726 + static ant_value_t url_set_hash(ant_t *js, ant_value_t *args, int nargs) { 727 + if (nargs < 1) return js_mkundef(); 728 + url_state_t *s = url_get_state(js->this_val); 729 + if (!s) return js_mkundef(); 730 + const char *val = js_getstr(js, args[0], NULL); 731 + if (!val) return js_mkundef(); 732 + const char *h = (val[0] == '#') ? val + 1 : val; 733 + free(s->hash); 734 + if (*h) { 735 + size_t hlen = strlen(h); 736 + s->hash = malloc(hlen + 2); 737 + s->hash[0] = '#'; 738 + memcpy(s->hash + 1, h, hlen + 1); 739 + } else s->hash = strdup(""); 740 + return js_mkundef(); 741 + } 742 + 743 + static ant_value_t url_toString(ant_t *js, ant_value_t *args, int nargs) { 744 + url_state_t *s = url_get_state(js->this_val); 745 + if (!s) return js_mkstr(js, "", 0); 746 + char *href = build_href(s); 747 + ant_value_t ret = js_mkstr(js, href, strlen(href)); 748 + free(href); 749 + return ret; 337 750 } 338 751 339 752 static ant_value_t js_URL(ant_t *js, ant_value_t *args, int nargs) { 340 - if (nargs < 1) return js_mkerr(js, "TypeError: URL requires at least 1 argument"); 753 + if (is_undefined(js->new_target)) 754 + return js_mkerr_typed(js, JS_ERR_TYPE, 755 + "Failed to construct 'URL': Please use the 'new' operator."); 756 + if (nargs < 1) 757 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': 1 argument required."); 758 + 759 + ant_value_t url_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 760 + if (is_err(url_sv)) 761 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL."); 762 + const char *url_str = js_getstr(js, url_sv, NULL); 763 + if (!url_str) 764 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL."); 765 + 766 + const char *base_str = NULL; 767 + if (nargs > 1 && !is_undefined(args[1]) && !is_null(args[1])) 768 + base_str = coerce_to_string(js, args[1], NULL); 341 769 342 - char *url_str = js_getstr(js, args[0], NULL); 343 - char *base_str = (nargs > 1) ? (char *)coerce_to_string(js, args[1], NULL) : NULL; 344 - if (!url_str) return js_mkerr(js, "TypeError: Invalid URL"); 770 + url_state_t *s = calloc(1, sizeof(url_state_t)); 771 + if (!s) return js_mkerr(js, "out of memory"); 345 772 346 - parsed_url_t parsed; 347 - if (parse_url(url_str, base_str, &parsed) < 0) { 348 - return js_mkerr(js, "TypeError: Invalid URL"); 773 + if (parse_url_to_state(url_str, base_str, s) != 0) { 774 + free(s); 775 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL."); 349 776 } 350 777 351 - ant_value_t url_obj = js_mkobj(js); 352 - ant_value_t proto = js_get_ctor_proto(js, "URL", 3); 353 - if (is_special_object(proto)) js_set_proto_init(url_obj, proto); 778 + ant_value_t obj = js_mkobj(js); 779 + js_set_proto_init(obj, g_url_proto); 780 + js_set_slot(obj, SLOT_DATA, ANT_PTR(s)); 781 + js_set_finalizer(obj, url_finalize); 354 782 355 - js_set(js, url_obj, "protocol", js_mkstr(js, parsed.protocol, strlen(parsed.protocol))); 356 - js_set(js, url_obj, "username", js_mkstr(js, parsed.username, strlen(parsed.username))); 357 - js_set(js, url_obj, "password", js_mkstr(js, parsed.password, strlen(parsed.password))); 358 - js_set(js, url_obj, "hostname", js_mkstr(js, parsed.hostname, strlen(parsed.hostname))); 359 - js_set(js, url_obj, "port", js_mkstr(js, parsed.port, strlen(parsed.port))); 360 - js_set(js, url_obj, "pathname", js_mkstr(js, parsed.pathname, strlen(parsed.pathname))); 361 - js_set(js, url_obj, "search", js_mkstr(js, parsed.search, strlen(parsed.search))); 362 - js_set(js, url_obj, "hash", js_mkstr(js, parsed.hash, strlen(parsed.hash))); 783 + const char *query = (s->search && s->search[0] == '?') ? s->search + 1 : ""; 784 + ant_value_t usp = make_usp_for_url(js, obj, query); 785 + js_set_slot_wb(js, obj, SLOT_ENTRIES, usp); 363 786 364 - update_url_href(js, url_obj); 787 + return obj; 788 + } 365 789 366 - ant_value_t search_params = js_mkobj(js); 367 - ant_value_t usp_proto = js_get_ctor_proto(js, "URLSearchParams", 15); 368 - if (is_special_object(usp_proto)) js_set_proto_init(search_params, usp_proto); 369 - js_set_slot(search_params, SLOT_DATA, url_obj); 370 - if (parsed.search && *parsed.search) { 371 - const char *qs = parsed.search[0] == '?' ? parsed.search + 1 : parsed.search; 372 - ant_value_t arg = js_mkstr(js, qs, strlen(qs)); 373 - ant_value_t tmp = js_URLSearchParams(js, &arg, 1); 374 - js_set_slot(search_params, SLOT_ENTRIES, js_get_slot(tmp, SLOT_ENTRIES)); 375 - } else { 376 - js_set_slot(search_params, SLOT_ENTRIES, js_mkarr(js)); 377 - } 378 - js_set(js, url_obj, "searchParams", search_params); 790 + ant_value_t make_url_obj(ant_t *js, url_state_t *s) { 791 + ant_value_t obj = js_mkobj(js); 792 + js_set_proto_init(obj, g_url_proto); 793 + js_set_slot(obj, SLOT_DATA, ANT_PTR(s)); 794 + js_set_finalizer(obj, url_finalize); 795 + const char *query = (s->search && s->search[0] == '?') ? s->search + 1 : ""; 796 + ant_value_t usp = make_usp_for_url(js, obj, query); 797 + js_set_slot_wb(js, obj, SLOT_ENTRIES, usp); 798 + return obj; 799 + } 379 800 380 - free_parsed_url(&parsed); 381 - return url_obj; 801 + static ant_value_t url_canParse(ant_t *js, ant_value_t *args, int nargs) { 802 + if (nargs < 1) return js_false; 803 + ant_value_t url_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 804 + if (is_err(url_sv)) return js_false; 805 + const char *url_str = js_getstr(js, url_sv, NULL); 806 + if (!url_str) return js_false; 807 + const char *base_str = NULL; 808 + if (nargs > 1 && !is_undefined(args[1]) && !is_null(args[1])) 809 + base_str = coerce_to_string(js, args[1], NULL); 810 + url_state_t s; 811 + if (parse_url_to_state(url_str, base_str, &s) != 0) return js_false; 812 + url_state_clear(&s); 813 + return js_true; 814 + } 815 + 816 + static ant_value_t url_parse(ant_t *js, ant_value_t *args, int nargs) { 817 + if (nargs < 1) return js_mknull(); 818 + ant_value_t url_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 819 + if (is_err(url_sv)) return js_mknull(); 820 + const char *url_str = js_getstr(js, url_sv, NULL); 821 + if (!url_str) return js_mknull(); 822 + const char *base_str = NULL; 823 + if (nargs > 1 && !is_undefined(args[1]) && !is_null(args[1])) 824 + base_str = coerce_to_string(js, args[1], NULL); 825 + url_state_t *s = calloc(1, sizeof(url_state_t)); 826 + if (!s) return js_mknull(); 827 + if (parse_url_to_state(url_str, base_str, s) != 0) { 828 + free(s); 829 + return js_mknull(); 830 + } 831 + return make_url_obj(js, s); 382 832 } 383 833 384 834 static ant_value_t usp_get(ant_t *js, ant_value_t *args, int nargs) { 385 835 if (nargs < 1) return js_mknull(); 386 - ant_value_t this_val = js_getthis(js); 387 - 388 - char *key = js_getstr(js, args[0], NULL); 836 + ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 837 + if (is_err(key_sv)) return js_mknull(); 838 + const char *key = js_getstr(js, key_sv, NULL); 389 839 if (!key) return js_mknull(); 390 - 391 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 840 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 392 841 if (!is_special_object(entries)) return js_mknull(); 393 - 394 842 ant_offset_t len = js_arr_len(js, entries); 395 843 for (ant_offset_t i = 0; i < len; i++) { 396 844 ant_value_t entry = js_arr_get(js, entries, i); 397 - char *ks = js_getstr(js, js_arr_get(js, entry, 0), NULL); 398 - if (ks && strcmp(ks, key) == 0) return js_arr_get(js, entry, 1); 845 + const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 846 + if (ek && strcmp(ek, key) == 0) return js_arr_get(js, entry, 1); 399 847 } 400 - 401 848 return js_mknull(); 402 849 } 403 850 404 851 static ant_value_t usp_getAll(ant_t *js, ant_value_t *args, int nargs) { 405 - if (nargs < 1) return js_mkarr(js); 406 - ant_value_t this_val = js_getthis(js); 407 - 408 - char *key = js_getstr(js, args[0], NULL); 409 - if (!key) return js_mkarr(js); 410 - 411 852 ant_value_t result = js_mkarr(js); 412 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 853 + if (nargs < 1) return result; 854 + ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 855 + if (is_err(key_sv)) return result; 856 + const char *key = js_getstr(js, key_sv, NULL); 857 + if (!key) return result; 858 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 413 859 if (!is_special_object(entries)) return result; 414 - 415 860 ant_offset_t len = js_arr_len(js, entries); 416 861 for (ant_offset_t i = 0; i < len; i++) { 417 862 ant_value_t entry = js_arr_get(js, entries, i); 418 - char *ks = js_getstr(js, js_arr_get(js, entry, 0), NULL); 419 - if (ks && strcmp(ks, key) == 0) js_arr_push(js, result, js_arr_get(js, entry, 1)); 863 + const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 864 + if (ek && strcmp(ek, key) == 0) 865 + js_arr_push(js, result, js_arr_get(js, entry, 1)); 420 866 } 421 - 422 867 return result; 423 868 } 424 869 425 870 static ant_value_t usp_has(ant_t *js, ant_value_t *args, int nargs) { 426 871 if (nargs < 1) return js_false; 427 - ant_value_t this_val = js_getthis(js); 428 - 429 - char *key = js_getstr(js, args[0], NULL); 872 + ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 873 + if (is_err(key_sv)) return js_false; 874 + const char *key = js_getstr(js, key_sv, NULL); 430 875 if (!key) return js_false; 431 - 432 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 876 + const char *match_val = NULL; 877 + if (nargs >= 2 && !is_undefined(args[1])) { 878 + ant_value_t mv_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 879 + if (!is_err(mv_sv)) match_val = js_getstr(js, mv_sv, NULL); 880 + } 881 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 433 882 if (!is_special_object(entries)) return js_false; 434 - 435 883 ant_offset_t len = js_arr_len(js, entries); 436 884 for (ant_offset_t i = 0; i < len; i++) { 437 885 ant_value_t entry = js_arr_get(js, entries, i); 438 - char *ks = js_getstr(js, js_arr_get(js, entry, 0), NULL); 439 - if (ks && strcmp(ks, key) == 0) return js_true; 886 + const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 887 + if (!ek || strcmp(ek, key) != 0) continue; 888 + if (!match_val) return js_true; 889 + const char *ev = js_getstr(js, js_arr_get(js, entry, 1), NULL); 890 + if (ev && strcmp(ev, match_val) == 0) return js_true; 440 891 } 441 - 442 892 return js_false; 443 893 } 444 894 445 - static void usp_sync_url(ant_t *js, ant_value_t this_val) { 446 - ant_value_t url_obj = js_get_slot(this_val, SLOT_DATA); 447 - if (!is_special_object(url_obj)) return; 448 - 449 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 450 - ant_offset_t len = js_arr_len(js, entries); 451 - 452 - size_t buf_size = 1024; 453 - char *buf = try_oom(buf_size); 454 - size_t pos = 0; 455 - 456 - for (ant_offset_t i = 0; i < len; i++) { 457 - ant_value_t entry = js_arr_get(js, entries, i); 458 - char *k = js_getstr(js, js_arr_get(js, entry, 0), NULL); 459 - char *v = js_getstr(js, js_arr_get(js, entry, 1), NULL); 460 - if (!k) continue; 461 - 462 - char *ek = url_encode_component(k); 463 - char *ev = url_encode_component(v ? v : ""); 464 - 465 - size_t needed = strlen(ek) + strlen(ev) + 3; 466 - if (pos + needed >= buf_size) { 467 - buf_size = buf_size * 2 + needed; 468 - buf = realloc(buf, buf_size); 469 - if (!buf) { free(buf); return; } 470 - } 471 - 472 - buf[pos] = pos == 0 ? '?' : '&'; pos++; 473 - pos += sprintf(buf + pos, "%s=%s", ek, ev); 474 - free(ek); free(ev); 475 - } 476 - 477 - buf[pos] = '\0'; 478 - js_set(js, url_obj, "search", js_mkstr(js, buf, pos)); 479 - update_url_href(js, url_obj); 480 - free(buf); 481 - } 482 - 483 895 static ant_value_t usp_set(ant_t *js, ant_value_t *args, int nargs) { 484 896 if (nargs < 2) return js_mkundef(); 485 - ant_value_t this_val = js_getthis(js); 897 + ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 486 898 487 - char *key = js_getstr(js, args[0], NULL); 899 + if (is_err(key_sv)) return js_mkundef(); 900 + ant_value_t val_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 901 + 902 + if (is_err(val_sv)) return js_mkundef(); 903 + const char *key = js_getstr(js, key_sv, NULL); 904 + 488 905 if (!key) return js_mkundef(); 489 - 490 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 491 - ant_offset_t len = js_arr_len(js, entries); 492 - 906 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 907 + ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; 493 908 ant_value_t new_entries = js_mkarr(js); 909 + 494 910 int found = 0; 495 - 496 911 for (ant_offset_t i = 0; i < len; i++) { 497 - ant_value_t entry = js_arr_get(js, entries, i); 498 - char *ks = js_getstr(js, js_arr_get(js, entry, 0), NULL); 499 - if (ks && strcmp(ks, key) == 0) { 500 - if (!found) { 501 - ant_value_t new_entry = js_mkarr(js); 502 - js_arr_push(js, new_entry, args[0]); 503 - js_arr_push(js, new_entry, args[1]); 504 - js_arr_push(js, new_entries, new_entry); 505 - found = 1; 506 - } 507 - } else js_arr_push(js, new_entries, entry); 508 - } 509 - 912 + ant_value_t entry = js_arr_get(js, entries, i); 913 + const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 914 + 915 + if (ek && strcmp(ek, key) == 0) { 916 + if (!found) { 917 + ant_value_t ne = js_mkarr(js); 918 + js_arr_push(js, ne, key_sv); 919 + js_arr_push(js, ne, val_sv); 920 + js_arr_push(js, new_entries, ne); 921 + found = 1; 922 + } 923 + } else js_arr_push(js, new_entries, entry); } 924 + 510 925 if (!found) { 511 - ant_value_t new_entry = js_mkarr(js); 512 - js_arr_push(js, new_entry, args[0]); 513 - js_arr_push(js, new_entry, args[1]); 514 - js_arr_push(js, new_entries, new_entry); 926 + ant_value_t ne = js_mkarr(js); 927 + js_arr_push(js, ne, key_sv); 928 + js_arr_push(js, ne, val_sv); 929 + js_arr_push(js, new_entries, ne); 515 930 } 516 - 517 - js_set_slot_wb(js, this_val, SLOT_ENTRIES, new_entries); 518 - usp_sync_url(js, this_val); 931 + 932 + js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); 933 + usp_push_to_url(js, js->this_val); 519 934 520 935 return js_mkundef(); 521 936 } 522 937 523 938 static ant_value_t usp_append(ant_t *js, ant_value_t *args, int nargs) { 524 939 if (nargs < 2) return js_mkundef(); 525 - ant_value_t this_val = js_getthis(js); 526 - 527 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 940 + ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 941 + 942 + if (is_err(key_sv)) return js_mkundef(); 943 + ant_value_t val_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 944 + 945 + if (is_err(val_sv)) return js_mkundef(); 946 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 947 + 948 + if (!is_special_object(entries)) return js_mkundef(); 528 949 ant_value_t entry = js_mkarr(js); 529 - js_arr_push(js, entry, args[0]); 530 - js_arr_push(js, entry, args[1]); 950 + 951 + js_arr_push(js, entry, key_sv); 952 + js_arr_push(js, entry, val_sv); 531 953 js_arr_push(js, entries, entry); 532 - 533 - usp_sync_url(js, this_val); 954 + usp_push_to_url(js, js->this_val); 955 + 534 956 return js_mkundef(); 535 957 } 536 958 537 959 static ant_value_t usp_delete(ant_t *js, ant_value_t *args, int nargs) { 538 960 if (nargs < 1) return js_mkundef(); 539 - ant_value_t this_val = js_getthis(js); 540 - char *key = js_getstr(js, args[0], NULL); 961 + ant_value_t key_sv = (vtype(args[0]) == T_STR) ? args[0] : js_tostring_val(js, args[0]); 962 + 963 + if (is_err(key_sv)) return js_mkundef(); 964 + const char *key = js_getstr(js, key_sv, NULL); 965 + 541 966 if (!key) return js_mkundef(); 967 + const char *match_val = NULL; 968 + 969 + if (nargs >= 2 && !is_undefined(args[1])) { 970 + ant_value_t mv_sv = (vtype(args[1]) == T_STR) ? args[1] : js_tostring_val(js, args[1]); 971 + if (!is_err(mv_sv)) match_val = js_getstr(js, mv_sv, NULL); 972 + } 973 + 974 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 975 + ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; 976 + ant_value_t new_entries = js_mkarr(js); 977 + 978 + for (ant_offset_t i = 0; i < len; i++) { 979 + ant_value_t entry = js_arr_get(js, entries, i); 980 + const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); 981 + if (ek && strcmp(ek, key) == 0) { 982 + if (!match_val) continue; 983 + const char *ev = js_getstr(js, js_arr_get(js, entry, 1), NULL); 984 + if (ev && strcmp(ev, match_val) == 0) continue; 985 + } 986 + js_arr_push(js, new_entries, entry); 987 + } 988 + 989 + js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); 990 + usp_push_to_url(js, js->this_val); 991 + 992 + return js_mkundef(); 993 + } 542 994 543 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 995 + static ant_value_t usp_toString(ant_t *js, ant_value_t *args, int nargs) { 996 + char *s = usp_serialize(js, js->this_val); 997 + ant_value_t ret = js_mkstr(js, s, strlen(s)); 998 + free(s); 999 + return ret; 1000 + } 1001 + 1002 + static ant_value_t usp_forEach(ant_t *js, ant_value_t *args, int nargs) { 1003 + if (nargs < 1 || !is_callable(args[0])) return js_mkundef(); 1004 + 1005 + ant_value_t cb = args[0]; 1006 + ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef(); 1007 + ant_value_t self = js->this_val; 1008 + ant_value_t entries = js_get_slot(self, SLOT_ENTRIES); 1009 + 1010 + if (!is_special_object(entries)) return js_mkundef(); 544 1011 ant_offset_t len = js_arr_len(js, entries); 545 - 546 - ant_value_t new_entries = js_mkarr(js); 1012 + 547 1013 for (ant_offset_t i = 0; i < len; i++) { 548 1014 ant_value_t entry = js_arr_get(js, entries, i); 549 - char *ks = js_getstr(js, js_arr_get(js, entry, 0), NULL); 550 - if (!ks || strcmp(ks, key) != 0) js_arr_push(js, new_entries, entry); 1015 + ant_value_t k = js_arr_get(js, entry, 0); 1016 + ant_value_t v = js_arr_get(js, entry, 1); 1017 + ant_value_t cb_args[3] = { v, k, self }; 1018 + ant_value_t r = sv_vm_call(js->vm, js, cb, this_arg, cb_args, 3, NULL, false); 1019 + if (is_err(r)) return r; 551 1020 } 552 - 553 - js_set_slot_wb(js, this_val, SLOT_ENTRIES, new_entries); 554 - usp_sync_url(js, this_val); 555 1021 556 1022 return js_mkundef(); 557 1023 } 558 1024 559 - static ant_value_t usp_toString(ant_t *js, ant_value_t *args, int nargs) { 560 - ant_value_t this_val = js_getthis(js); 561 - ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); 1025 + static ant_value_t usp_size_get(ant_t *js, ant_value_t *args, int nargs) { 1026 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1027 + if (!is_special_object(entries)) return js_mknum(0); 1028 + return js_mknum((double)js_arr_len(js, entries)); 1029 + } 1030 + 1031 + static ant_value_t usp_sort(ant_t *js, ant_value_t *args, int nargs) { 1032 + ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); 1033 + if (!is_special_object(entries)) return js_mkundef(); 562 1034 ant_offset_t len = js_arr_len(js, entries); 1035 + if (len <= 1) return js_mkundef(); 563 1036 564 - size_t buf_size = 1024; 565 - char *buf = try_oom(buf_size); 566 - size_t pos = 0; 1037 + ant_value_t *arr = malloc(sizeof(ant_value_t) * (size_t)len); 1038 + if (!arr) return js_mkundef(); 1039 + for (ant_offset_t i = 0; i < len; i++) arr[i] = js_arr_get(js, entries, i); 1040 + 1041 + for (ant_offset_t i = 1; i < len; i++) { 1042 + ant_value_t cur = arr[i]; 1043 + const char *ck = js_getstr(js, js_arr_get(js, cur, 0), NULL); 1044 + ant_offset_t j = i - 1; 1045 + while (j >= 0) { 1046 + const char *jk = js_getstr(js, js_arr_get(js, arr[j], 0), NULL); 1047 + if (strcmp(jk ? jk : "", ck ? ck : "") <= 0) break; 1048 + arr[j + 1] = arr[j]; j--; 1049 + } 1050 + arr[j + 1] = cur; 1051 + } 1052 + 1053 + ant_value_t new_entries = js_mkarr(js); 1054 + for (ant_offset_t i = 0; i < len; i++) js_arr_push(js, new_entries, arr[i]); 1055 + free(arr); 1056 + 1057 + js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); 1058 + usp_push_to_url(js, js->this_val); 1059 + 1060 + return js_mkundef(); 1061 + } 567 1062 568 - for (ant_offset_t i = 0; i < len; i++) { 569 - ant_value_t entry = js_arr_get(js, entries, i); 570 - char *k = js_getstr(js, js_arr_get(js, entry, 0), NULL); 571 - char *v = js_getstr(js, js_arr_get(js, entry, 1), NULL); 572 - if (!k) continue; 1063 + static ant_value_t usp_iter_next(ant_t *js, ant_value_t *args, int nargs) { 1064 + ant_value_t state_v = js_get_slot(js->this_val, SLOT_ITER_STATE); 1065 + if (vtype(state_v) != T_NUM) return js_iter_result(js, false, js_mkundef()); 1066 + 1067 + uint32_t state = (uint32_t)js_getnum(state_v); 1068 + uint32_t kind = ITER_STATE_KIND(state); 1069 + uint32_t idx = ITER_STATE_INDEX(state); 1070 + 1071 + ant_value_t usp = js_get_slot(js->this_val, SLOT_DATA); 1072 + ant_value_t entries = js_get_slot(usp, SLOT_ENTRIES); 1073 + if (!is_special_object(entries) || (ant_offset_t)idx >= js_arr_len(js, entries)) 1074 + return js_iter_result(js, false, js_mkundef()); 1075 + 1076 + js_set_slot(js->this_val, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, idx + 1))); 1077 + 1078 + ant_value_t entry = js_arr_get(js, entries, (ant_offset_t)idx); 1079 + ant_value_t k = js_arr_get(js, entry, 0); 1080 + ant_value_t v = js_arr_get(js, entry, 1); 1081 + 1082 + ant_value_t out; 1083 + switch (kind) { 1084 + case USP_ITER_KEYS: out = k; break; 1085 + case USP_ITER_VALUES: out = v; break; 1086 + default: { 1087 + out = js_mkarr(js); 1088 + js_arr_push(js, out, k); 1089 + js_arr_push(js, out, v); 1090 + break; 1091 + }} 1092 + 1093 + return js_iter_result(js, true, out); 1094 + } 1095 + 1096 + static ant_value_t make_usp_iter(ant_t *js, ant_value_t usp, int kind) { 1097 + ant_value_t iter = js_mkobj(js); 1098 + js_set_proto_init(iter, g_usp_iter_proto); 1099 + js_set_slot_wb(js, iter, SLOT_DATA, usp); 1100 + js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)ITER_STATE_PACK(kind, 0))); 1101 + return iter; 1102 + } 1103 + 1104 + static ant_value_t usp_entries_fn(ant_t *js, ant_value_t *args, int nargs) { 1105 + return make_usp_iter(js, js->this_val, USP_ITER_ENTRIES); 1106 + } 1107 + 1108 + static ant_value_t usp_keys_fn(ant_t *js, ant_value_t *args, int nargs) { 1109 + return make_usp_iter(js, js->this_val, USP_ITER_KEYS); 1110 + } 1111 + 1112 + static ant_value_t usp_values_fn(ant_t *js, ant_value_t *args, int nargs) { 1113 + return make_usp_iter(js, js->this_val, USP_ITER_VALUES); 1114 + } 1115 + 1116 + static ant_value_t js_URLSearchParams(ant_t *js, ant_value_t *args, int nargs) { 1117 + if (is_undefined(js->new_target)) 1118 + return js_mkerr_typed(js, JS_ERR_TYPE, 1119 + "Failed to construct 'URLSearchParams': Please use the 'new' operator."); 1120 + 1121 + ant_value_t obj = js_mkobj(js); 1122 + js_set_proto_init(obj, g_usp_proto); 1123 + js_set_slot(obj, SLOT_DATA, js_mkundef()); 1124 + ant_value_t entries = js_mkarr(js); 1125 + js_set_slot(obj, SLOT_ENTRIES, entries); 1126 + 1127 + if (nargs < 1 || is_undefined(args[0]) || is_null(args[0])) return obj; 1128 + 1129 + ant_value_t init = args[0]; 1130 + uint8_t t = vtype(init); 1131 + 1132 + if (t == T_STR) { 1133 + const char *s = js_getstr(js, init, NULL); 1134 + if (s) { 1135 + const char *q = (s[0] == '?') ? s + 1 : s; 1136 + js_set_slot(obj, SLOT_ENTRIES, parse_query_to_arr(js, q)); 1137 + } 1138 + return obj; 1139 + } 1140 + 1141 + if (t == T_ARR) { 1142 + ant_offset_t len = js_arr_len(js, init); 1143 + for (ant_offset_t i = 0; i < len; i++) { 1144 + ant_value_t pair = js_arr_get(js, init, i); 1145 + if (vtype(pair) != T_ARR) 1146 + return js_mkerr_typed(js, JS_ERR_TYPE, 1147 + "Failed to construct 'URLSearchParams': Each element must be an array."); 1148 + 1149 + ant_offset_t plen = js_arr_len(js, pair); 1150 + if (plen != 2) 1151 + return js_mkerr_typed(js, JS_ERR_TYPE, 1152 + "Failed to construct 'URLSearchParams': Each pair must have exactly 2 elements."); 1153 + 1154 + ant_value_t pk = js_arr_get(js, pair, 0); 1155 + ant_value_t pv = js_arr_get(js, pair, 1); 1156 + ant_value_t ksv = (vtype(pk) == T_STR) ? pk : js_tostring_val(js, pk); 1157 + 1158 + if (is_err(ksv)) return ksv; 1159 + ant_value_t vsv = (vtype(pv) == T_STR) ? pv : js_tostring_val(js, pv); 1160 + 1161 + if (is_err(vsv)) return vsv; 1162 + ant_value_t entry = js_mkarr(js); 1163 + 1164 + js_arr_push(js, entry, ksv); 1165 + js_arr_push(js, entry, vsv); 1166 + js_arr_push(js, entries, entry); 1167 + } 573 1168 574 - char *ek = url_encode_component(k); 575 - char *ev = url_encode_component(v ? v : ""); 576 - size_t needed = strlen(ek) + strlen(ev) + 3; 1169 + return obj; 1170 + } 1171 + 1172 + if (is_special_object(init)) { 1173 + ant_value_t src = js_get_slot(init, SLOT_ENTRIES); 1174 + if (vtype(src) == T_ARR) { 1175 + ant_offset_t len = js_arr_len(js, src); 1176 + for (ant_offset_t i = 0; i < len; i++) { 1177 + ant_value_t entry = js_arr_get(js, src, i); 1178 + ant_value_t ne = js_mkarr(js); 1179 + 1180 + js_arr_push(js, ne, js_arr_get(js, entry, 0)); 1181 + js_arr_push(js, ne, js_arr_get(js, entry, 1)); 1182 + js_arr_push(js, entries, ne); 1183 + } 1184 + 1185 + return obj; 1186 + } 577 1187 578 - if (pos + needed >= buf_size) { 579 - buf_size = buf_size * 2 + needed; 580 - buf = realloc(buf, buf_size); 581 - if (!buf) { free(buf); return js_mkstr(js, "", 0); } 1188 + ant_iter_t it = js_prop_iter_begin(js, init); 1189 + const char *key; 1190 + size_t key_len; 1191 + ant_value_t val; 1192 + 1193 + while (js_prop_iter_next(&it, &key, &key_len, &val)) { 1194 + ant_value_t sv = (vtype(val) == T_STR) ? val : js_tostring_val(js, val); 1195 + if (is_err(sv)) { js_prop_iter_end(&it); return sv; } 1196 + ant_value_t entry = js_mkarr(js); 1197 + js_arr_push(js, entry, js_mkstr(js, key, key_len)); 1198 + js_arr_push(js, entry, sv); 1199 + js_arr_push(js, entries, entry); 582 1200 } 583 1201 584 - if (pos > 0) buf[pos++] = '&'; 585 - pos += sprintf(buf + pos, "%s=%s", ek, ev); 586 - free(ek); 587 - free(ev); 1202 + js_prop_iter_end(&it); 588 1203 } 589 - 590 - buf[pos] = '\0'; 591 - ant_value_t ret = js_mkstr(js, buf, pos); 592 - free(buf); 593 - 594 - return ret; 1204 + 1205 + return obj; 595 1206 } 596 1207 597 1208 void init_url_module(void) { 598 1209 ant_t *js = rt->js; 599 1210 ant_value_t glob = js->global; 600 1211 601 - ant_value_t url_ctor = js_mkobj(js); 602 - ant_value_t url_proto = js_mkobj(js); 603 - 604 - js_set(js, url_proto, "toString", js_mkfun(url_toString)); 605 - js_set_sym(js, url_proto, get_toStringTag_sym(), js_mkstr(js, "URL", 3)); 606 - 607 - js_set_slot(url_ctor, SLOT_CFUNC, js_mkfun(js_URL)); 608 - js_mkprop_fast(js, url_ctor, "prototype", 9, url_proto); 609 - js_mkprop_fast(js, url_ctor, "name", 4, ANT_STRING("URL")); 610 - js_set_descriptor(js, url_ctor, "name", 4, 0); 611 - 612 - js_set(js, glob, "URL", js_obj_to_func(url_ctor)); 613 - 614 - ant_value_t usp_ctor = js_mkobj(js); 615 - ant_value_t usp_proto = js_mkobj(js); 616 - 617 - js_set(js, usp_proto, "get", js_mkfun(usp_get)); 618 - js_set(js, usp_proto, "getAll", js_mkfun(usp_getAll)); 619 - js_set(js, usp_proto, "has", js_mkfun(usp_has)); 620 - js_set(js, usp_proto, "set", js_mkfun(usp_set)); 621 - js_set(js, usp_proto, "append", js_mkfun(usp_append)); 622 - js_set(js, usp_proto, "delete", js_mkfun(usp_delete)); 623 - js_set(js, usp_proto, "toString", js_mkfun(usp_toString)); 624 - js_set_sym(js, usp_proto, get_toStringTag_sym(), js_mkstr(js, "URLSearchParams", 15)); 625 - 626 - js_set_slot(usp_ctor, SLOT_CFUNC, js_mkfun(js_URLSearchParams)); 627 - js_mkprop_fast(js, usp_ctor, "prototype", 9, usp_proto); 628 - js_mkprop_fast(js, usp_ctor, "name", 4, ANT_STRING("URLSearchParams")); 629 - js_set_descriptor(js, usp_ctor, "name", 4, 0); 630 - 631 - js_set(js, glob, "URLSearchParams", js_obj_to_func(usp_ctor)); 1212 + g_usp_iter_proto = js_mkobj(js); 1213 + js_set_proto_init(g_usp_iter_proto, js->sym.iterator_proto); 1214 + js_set(js, g_usp_iter_proto, "next", js_mkfun(usp_iter_next)); 1215 + js_set_descriptor(js, g_usp_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); 1216 + js_set_sym(js, g_usp_iter_proto, get_iterator_sym(), js_mkfun(sym_this_cb)); 1217 + 1218 + g_usp_proto = js_mkobj(js); 1219 + js_set(js, g_usp_proto, "get", js_mkfun(usp_get)); 1220 + js_set(js, g_usp_proto, "getAll", js_mkfun(usp_getAll)); 1221 + js_set(js, g_usp_proto, "has", js_mkfun(usp_has)); 1222 + js_set(js, g_usp_proto, "set", js_mkfun(usp_set)); 1223 + js_set(js, g_usp_proto, "append", js_mkfun(usp_append)); 1224 + js_set(js, g_usp_proto, "delete", js_mkfun(usp_delete)); 1225 + js_set(js, g_usp_proto, "sort", js_mkfun(usp_sort)); 1226 + js_set(js, g_usp_proto, "toString", js_mkfun(usp_toString)); 1227 + js_set(js, g_usp_proto, "forEach", js_mkfun(usp_forEach)); 1228 + js_set_getter_desc(js, g_usp_proto, "size", 4, js_mkfun(usp_size_get), JS_DESC_C); 1229 + 1230 + ant_value_t entries_fn = js_mkfun(usp_entries_fn); 1231 + js_set(js, g_usp_proto, "entries", entries_fn); 1232 + js_set(js, g_usp_proto, "keys", js_mkfun(usp_keys_fn)); 1233 + js_set(js, g_usp_proto, "values", js_mkfun(usp_values_fn)); 1234 + js_set_sym(js, g_usp_proto, get_iterator_sym(), entries_fn); 1235 + js_set_sym(js, g_usp_proto, get_toStringTag_sym(), js_mkstr(js, "URLSearchParams", 15)); 1236 + 1237 + ant_value_t usp_ctor = js_make_ctor(js, js_URLSearchParams, g_usp_proto, "URLSearchParams", 15); 1238 + js_set(js, glob, "URLSearchParams", usp_ctor); 1239 + 1240 + g_url_proto = js_mkobj(js); 1241 + js_set_accessor_desc(js, g_url_proto, "href", 4, js_mkfun(url_get_href), js_mkfun(url_set_href), JS_DESC_C); 1242 + js_set_accessor_desc(js, g_url_proto, "protocol", 8, js_mkfun(url_get_protocol), js_mkfun(url_set_protocol), JS_DESC_C); 1243 + js_set_accessor_desc(js, g_url_proto, "username", 8, js_mkfun(url_get_username), js_mkfun(url_set_username), JS_DESC_C); 1244 + js_set_accessor_desc(js, g_url_proto, "password", 8, js_mkfun(url_get_password), js_mkfun(url_set_password), JS_DESC_C); 1245 + js_set_accessor_desc(js, g_url_proto, "host", 4, js_mkfun(url_get_host), js_mkfun(url_set_host), JS_DESC_C); 1246 + js_set_accessor_desc(js, g_url_proto, "hostname", 8, js_mkfun(url_get_hostname), js_mkfun(url_set_hostname), JS_DESC_C); 1247 + js_set_accessor_desc(js, g_url_proto, "port", 4, js_mkfun(url_get_port), js_mkfun(url_set_port), JS_DESC_C); 1248 + js_set_accessor_desc(js, g_url_proto, "pathname", 8, js_mkfun(url_get_pathname), js_mkfun(url_set_pathname), JS_DESC_C); 1249 + js_set_accessor_desc(js, g_url_proto, "search", 6, js_mkfun(url_get_search), js_mkfun(url_set_search), JS_DESC_C); 1250 + js_set_accessor_desc(js, g_url_proto, "hash", 4, js_mkfun(url_get_hash), js_mkfun(url_set_hash), JS_DESC_C); 1251 + js_set_getter_desc(js, g_url_proto, "origin", 6, js_mkfun(url_get_origin), JS_DESC_C); 1252 + js_set_getter_desc(js, g_url_proto, "searchParams", 12, js_mkfun(url_get_searchParams), JS_DESC_C); 1253 + js_set(js, g_url_proto, "toString", js_mkfun(url_toString)); 1254 + js_set(js, g_url_proto, "toJSON", js_mkfun(url_toString)); 1255 + js_set_sym(js, g_url_proto, get_toStringTag_sym(), js_mkstr(js, "URL", 3)); 1256 + 1257 + ant_value_t url_ctor = js_make_ctor(js, js_URL, g_url_proto, "URL", 3); 1258 + js_set(js, url_ctor, "canParse", js_mkfun(url_canParse)); 1259 + js_set(js, url_ctor, "parse", js_mkfun(url_parse)); 1260 + js_set(js, glob, "URL", url_ctor); 632 1261 } 633 1262 634 1263 static ant_value_t builtin_fileURLToPath(ant_t *js, ant_value_t *args, int nargs) { 635 1264 if (nargs < 1) return js_mkerr(js, "fileURLToPath requires a string or URL argument"); 636 - 1265 + 637 1266 size_t len; 638 1267 const char *str = coerce_to_string(js, args[0], &len); 639 1268 if (!str) return js_mkerr(js, "fileURLToPath requires a string or URL argument"); 640 1269 641 - parsed_url_t parsed; 642 - if (parse_url(str, NULL, &parsed) != 0) 1270 + url_state_t s; 1271 + if (parse_url_to_state(str, NULL, &s) != 0) 643 1272 return js_mkerr(js, "Invalid URL"); 644 - 645 - if (strcmp(parsed.protocol, "file:") != 0) { 646 - free_parsed_url(&parsed); 1273 + if (strcmp(s.protocol, "file:") != 0) { 1274 + url_state_clear(&s); 647 1275 return js_mkerr(js, "fileURLToPath requires a file: URL"); 648 1276 } 649 - 650 - char *decoded = url_decode_component(parsed.pathname); 651 - free_parsed_url(&parsed); 1277 + 1278 + char *decoded = url_decode_component(s.pathname); 1279 + url_state_clear(&s); 652 1280 if (!decoded) return js_mkerr(js, "allocation failure"); 653 - 654 1281 ant_value_t ret = js_mkstr(js, decoded, strlen(decoded)); 1282 + 655 1283 free(decoded); 656 1284 return ret; 657 1285 } ··· 659 1287 static ant_value_t builtin_pathToFileURL(ant_t *js, ant_value_t *args, int nargs) { 660 1288 if (nargs < 1 || vtype(args[0]) != T_STR) 661 1289 return js_mkerr(js, "pathToFileURL requires a string argument"); 662 - 1290 + 663 1291 size_t len; 664 1292 const char *path = js_getstr(js, args[0], &len); 665 - 666 1293 size_t total = 7 + len; 667 1294 char *buf = malloc(total + 1); 668 1295 if (!buf) return js_mkerr(js, "allocation failure"); 669 - 1296 + 670 1297 memcpy(buf, "file://", 7); 671 1298 memcpy(buf + 7, path, len); 672 1299 buf[total] = '\0'; 673 - 674 - ant_value_t url_args[1] = { js_mkstr(js, buf, total) }; 1300 + 1301 + url_state_t *s = calloc(1, sizeof(url_state_t)); 1302 + if (!s) { free(buf); return js_mkerr(js, "allocation failure"); } 1303 + if (parse_url_to_state(buf, NULL, s) != 0) { 1304 + free(buf); free(s); 1305 + return js_mkerr(js, "Invalid file URL"); 1306 + } 1307 + 675 1308 free(buf); 676 - 677 - return js_URL(js, url_args, 1); 1309 + return make_url_obj(js, s); 678 1310 } 679 1311 680 1312 ant_value_t url_library(ant_t *js) { 681 1313 ant_value_t lib = js_mkobj(js); 682 1314 ant_value_t glob = js_glob(js); 683 - 684 - js_set(js, lib, "URL", js_get(js, glob, "URL")); 685 - js_set(js, lib, "URLSearchParams", js_get(js, glob, "URLSearchParams")); 686 - js_set(js, lib, "fileURLToPath", js_mkfun(builtin_fileURLToPath)); 687 - js_set(js, lib, "pathToFileURL", js_mkfun(builtin_pathToFileURL)); 1315 + 1316 + js_set(js, lib, "URL", js_get(js, glob, "URL")); 1317 + js_set(js, lib, "URLSearchParams",js_get(js, glob, "URLSearchParams")); 1318 + js_set(js, lib, "fileURLToPath", js_mkfun(builtin_fileURLToPath)); 1319 + js_set(js, lib, "pathToFileURL", js_mkfun(builtin_pathToFileURL)); 688 1320 js_set(js, lib, "default", lib); 689 - 1321 + 690 1322 return lib; 691 1323 }