Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649#include <compat.h> // IWYU pragma: keep
#include <stdlib.h>#include <stdio.h>#include <string.h>#include <ctype.h>#include <ada_c.h>
#include "ant.h"#include "cfunc.h"#include "ptr.h"#include "errors.h"#include "internal.h"#include "descriptors.h"
#include "modules/blob.h"#include "modules/crypto.h"#include "silver/call.h"#include "modules/url.h"#include "modules/symbol.h"#include "modules/iterator.h"#include "url/url_internal.h"
static constexpr uint32_t URL_NATIVE_TAG = Ant_NativeTag("URLS");
enum { USP_ITER_ENTRIES = 0, USP_ITER_KEYS = 1, USP_ITER_VALUES = 2};
url_state_t *url_get_state(ant_value_t obj) { return (url_state_t *)js_get_native(obj, URL_NATIVE_TAG);}
bool usp_is_urlsearchparams(ant_t *js, ant_value_t obj) { return js_check_brand(obj, BRAND_URLSEARCHPARAMS);}
static ant_value_t usp_array_len(ant_t *js, ant_value_t arr, ant_offset_t *out) { *out = 0; if (!is_proxy(arr)) { *out = js_arr_len(js, arr); return js_mkundef(); }
ant_value_t length = js_get(js, arr, "length"); if (is_err(length)) return length; if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); double len_num = js_to_number(js, length); if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); if (isnan(len_num) || !isfinite(len_num)) return js_mkerr_typed(js, JS_ERR_TYPE, "URLSearchParams sequence length must be finite"); *out = (ant_offset_t)js_to_uint32(len_num); return js_mkundef();}
static ant_value_t usp_array_get(ant_t *js, ant_value_t arr, ant_offset_t idx) { ant_value_t value = js_arr_get(js, arr, idx); if (is_err(value)) return value; if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); return value;}
void url_state_clear(url_state_t *s) { if (!s) return; free(s->href); free(s->protocol); free(s->username); free(s->password); free(s->hostname); free(s->port); free(s->pathname); free(s->search); free(s->hash); memset(s, 0, sizeof(*s));}
void url_free_state(url_state_t *s) { if (!s) return; url_state_clear(s); free(s);}
static void url_finalize(ant_t *js, ant_object_t *obj) { ant_value_t value = js_obj_from_ptr(obj); url_free_state((url_state_t *)js_get_native(value, URL_NATIVE_TAG)); js_clear_native(value, URL_NATIVE_TAG);}
static bool is_special_scheme(const char *proto) { if (!proto) return false; return strcmp(proto, "http:") == 0 || strcmp(proto, "https:") == 0 || strcmp(proto, "ftp:") == 0 || strcmp(proto, "ws:") == 0 || strcmp(proto, "wss:") == 0;}
static bool uses_authority_syntax(const char *proto) { if (!proto) return false; return is_special_scheme(proto) || strcmp(proto, "file:") == 0;}
static bool url_has_opaque_path(const url_state_t *s) { if (!s || uses_authority_syntax(s->protocol) || (s->hostname && *s->hostname)) return false; return !s->pathname || s->pathname[0] != '/';}
char *form_urlencode_n(const char *str, size_t len) { if (!str) return strdup(""); char *out = malloc(len * 3 + 1); if (!out) return strdup(""); size_t j = 0; for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)str[i]; if (isalnum(c) || c == '*' || c == '-' || c == '.' || c == '_') out[j++] = (char)c; else if (c == ' ') out[j++] = '+'; else { snprintf(out + j, 4, "%%%02X", c); j += 3; }}
out[j] = '\0'; return out;}
char *form_urldecode_len(const char *str, size_t *out_len) { if (out_len) *out_len = 0; if (!str) return strdup(""); size_t len = strlen(str); char *out = malloc(len + 1);
if (!out) return strdup(""); size_t j = 0;
for (size_t i = 0; i < len; i++) { if (str[i] == '+') out[j++] = ' '; else if ( str[i] == '%' && i + 2 < len && isxdigit((unsigned char)str[i+1]) && isxdigit((unsigned char)str[i+2]) ) { int hi = isdigit((unsigned char)str[i+1]) ? str[i+1]-'0' : tolower((unsigned char)str[i+1])-'a'+10; int lo = isdigit((unsigned char)str[i+2]) ? str[i+2]-'0' : tolower((unsigned char)str[i+2])-'a'+10; out[j++] = (char)((hi << 4) | lo); i += 2; } else out[j++] = str[i]; } out[j] = '\0'; if (out_len) *out_len = j; return out;}
char *url_decode_component_len(const char *str, size_t *out_len) { if (out_len) *out_len = 0; if (!str) return strdup(""); size_t len = strlen(str); char *out = malloc(len + 1);
if (!out) return strdup(""); size_t j = 0;
for (size_t i = 0; i < len; i++) { if ( str[i] == '%' && i + 2 < len && isxdigit((unsigned char)str[i+1]) && isxdigit((unsigned char)str[i+2]) ) { int hi = isdigit((unsigned char)str[i+1]) ? str[i+1]-'0' : tolower((unsigned char)str[i+1])-'a'+10; int lo = isdigit((unsigned char)str[i+2]) ? str[i+2]-'0' : tolower((unsigned char)str[i+2])-'a'+10; out[j++] = (char)((hi << 4) | lo); i += 2; } else out[j++] = str[i]; }
out[j] = '\0'; if (out_len) *out_len = j; return out;}
char *form_urldecode(const char *str) { return form_urldecode_len(str, NULL);}
char *url_decode_component(const char *str) { return url_decode_component_len(str, NULL);}
static char *ada_string_dup(ada_string value) { return value.data ? strndup(value.data, value.length) : strdup("");}
static int ada_url_to_state(ada_url url, url_state_t *s) { s->href = ada_string_dup(ada_get_href(url)); s->protocol = ada_string_dup(ada_get_protocol(url)); s->username = ada_string_dup(ada_get_username(url)); s->password = ada_string_dup(ada_get_password(url)); s->hostname = ada_string_dup(ada_get_hostname(url)); s->port = ada_string_dup(ada_get_port(url)); s->pathname = ada_string_dup(ada_get_pathname(url)); s->search = ada_string_dup(ada_get_search(url)); s->hash = ada_string_dup(ada_get_hash(url));
if (s->search && !*s->search && ada_has_search(url)) { free(s->search); s->search = strdup("?"); } if (s->hash && !*s->hash && ada_has_hash(url)) { free(s->hash); s->hash = strdup("#"); }
if ( !s->href || !s->protocol || !s->username || !s->password || !s->hostname || !s->port || !s->pathname || !s->search || !s->hash ) { url_state_clear(s); return -1; } return 0;}
static int parse_url_to_state_n( const char *url_str, size_t url_len, const char *base_str, size_t base_len, url_state_t *s) { if (!url_str || !s) return -1; memset(s, 0, sizeof(*s));
ada_url url = base_str ? ada_parse_with_base(url_str, url_len, base_str, base_len) : ada_parse(url_str, url_len); if (!url || !ada_is_valid(url)) { if (url) ada_free(url); return -1; }
int result = ada_url_to_state(url, s); ada_free(url); return result;}
int parse_url_to_state(const char *url_str, const char *base_str, url_state_t *s) { return parse_url_to_state_n( url_str, url_str ? strlen(url_str) : 0, base_str, base_str ? strlen(base_str) : 0, s );}
int Ant_URL_ParseDocumentRelative(ant_t *js, const char *url_str, url_state_t *s) { ant_value_t href = js->builtins.location_href; return parse_url_to_state(url_str, vtype(href) == kTypeString ? js_getstr(js, href, NULL) : NULL, s);}
int Ant_URL_ParseLocation(const char *href, url_state_t *s) { if (parse_url_to_state(href, NULL, s) != 0) return -1; if (strcmp(s->protocol, "http:") == 0 || strcmp(s->protocol, "https:") == 0) return 0; url_state_clear(s); return -1;}
char *build_href(const url_state_t *s) { if (!s) return NULL; if (s->href) return strdup(s->href); if (!s->protocol) return NULL;
bool has_authority = (s->hostname && *s->hostname) || (s->username && *s->username) || (s->password && *s->password) || (s->port && *s->port); bool opaque_like = url_has_opaque_path(s); bool serialize_authority = has_authority || uses_authority_syntax(s->protocol); const char *pathname = s->pathname ? s->pathname : ""; size_t len = strlen(s->protocol) + strlen(pathname) + strlen(s->search) + strlen(s->hash) + 32;
if (has_authority) len += strlen(s->hostname) + 2; if (s->username && *s->username) len += strlen(s->username) + 1; if (s->password && *s->password) len += strlen(s->password) + 1; if (s->port && *s->port) len += strlen(s->port) + 1;
char *href = malloc(len); if (!href) return strdup(""); size_t pos = 0; pos += (size_t)sprintf(href + pos, "%s", s->protocol);
if (opaque_like) { pos += (size_t)sprintf(href + pos, "%s%s%s", pathname, s->search, s->hash); href[pos] = '\0'; return href; }
if (!serialize_authority) { pos += (size_t)sprintf(href + pos, "%s%s%s", pathname, s->search, s->hash); href[pos] = '\0'; return href; }
pos += (size_t)sprintf(href + pos, "//"); if (s->username && *s->username) { pos += (size_t)sprintf(href + pos, "%s", s->username); if (s->password && *s->password) pos += (size_t)sprintf(href + pos, ":%s", s->password); href[pos++] = '@'; }
pos += (size_t)sprintf(href + pos, "%s", s->hostname); if (s->port && *s->port) pos += (size_t)sprintf(href + pos, ":%s", s->port);
pos += (size_t)sprintf(href + pos, "%s%s%s", pathname, s->search, s->hash); href[pos] = '\0'; return href;}
static const char *coerce_to_string(ant_t *js, ant_value_t val, size_t *len) { if (vtype(val) == kTypeString) return js_getstr(js, val, len); if (is_object_type(val)) { ant_value_t href = js_getprop_fallback(js, val, "href"); if (vtype(href) == kTypeString) return js_getstr(js, href, len); } return NULL;}
static ant_value_t url_string_value(ant_t *js, ant_value_t value) { return vtype(value) == kTypeString ? value : js_tostring_val(js, value);}
typedef bool (*ada_url_setter_t)(ada_url, const char *, size_t);
static bool ada_set_search_bool(ada_url url, const char *value, size_t len) { ada_set_search(url, value, len); return true;}
static bool ada_set_hash_bool(ada_url url, const char *value, size_t len) { ada_set_hash(url, value, len); return true;}
static ant_value_t url_apply_setter( ant_t *js, url_state_t *state, ant_value_t input, ada_url_setter_t setter) { ant_value_t value = url_string_value(js, input); if (is_err(value)) return value;
size_t value_len = 0; const char *value_str = js_getstr(js, value, &value_len); char *href = build_href(state); if (!value_str || !href) { free(href); return js_mkerr(js, "out of memory"); }
ada_url url = ada_parse(href, strlen(href)); free(href); if (!url || !ada_is_valid(url)) { if (url) ada_free(url); return js_mkundef(); }
setter(url, value_str, value_len); if (ada_is_valid(url)) { url_state_t next = {0}; if (ada_url_to_state(url, &next) == 0) { url_state_clear(state); *state = next; } } ada_free(url); return js_mkundef();}
static ant_value_t parse_query_to_arr(ant_t *js, const char *query) { ant_value_t arr = js_mkarr(js); if (!query || !*query) return arr; const char *p = query; while (*p) { const char *amp = strchr(p, '&'); size_t plen = amp ? (size_t)(amp - p) : strlen(p); if (plen == 0) { p = amp ? amp + 1 : p + 1; continue; } char *pair = strndup(p, plen); if (!pair) { p = amp ? amp + 1 : p + plen; continue; } char *eq = strchr(pair, '='); char *raw_v = eq ? ((*eq = '\0'), eq + 1) : ""; char *k = form_urldecode(pair); char *v = form_urldecode(raw_v); ant_value_t entry = js_mkarr(js); js_arr_push(js, entry, js_mkstr(js, k, strlen(k))); js_arr_push(js, entry, js_mkstr(js, v, strlen(v))); js_arr_push(js, arr, entry); free(pair); free(k); free(v); p = amp ? amp + 1 : p + plen; } return arr;}
char *usp_serialize(ant_t *js, ant_value_t usp) { ant_value_t entries = js_get_slot(usp, SLOT_ENTRIES); ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0;
size_t cap = 256; char *buf = malloc(cap); if (!buf) return strdup(""); size_t pos = 0;
for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, entries, i); size_t klen = 0, vlen = 0; char *k = js_getstr(js, js_arr_get(js, entry, 0), &klen); char *v = js_getstr(js, js_arr_get(js, entry, 1), &vlen); char *ek = k ? form_urlencode_n(k, klen) : strdup(""); char *ev = v ? form_urlencode_n(v, vlen) : strdup(""); size_t needed = strlen(ek) + strlen(ev) + 3; if (pos + needed >= cap) { cap = cap * 2 + needed; buf = realloc(buf, cap); if (!buf) { free(ek); free(ev); return strdup(""); } } if (pos > 0) buf[pos++] = '&'; pos += (size_t)sprintf(buf + pos, "%s=%s", ek, ev); free(ek); free(ev); } buf[pos] = '\0'; return buf;}
static void usp_push_to_url(ant_t *js, ant_value_t usp) { ant_value_t url_obj = js_get_slot(usp, SLOT_DATA); if (!is_special_object(url_obj)) return; url_state_t *s = url_get_state(url_obj); if (!s) return; char *qs = usp_serialize(js, usp); ada_url url = s->href ? ada_parse(s->href, strlen(s->href)) : NULL; if (url && ada_is_valid(url)) { if (*qs) ada_set_search(url, qs, strlen(qs)); else ada_clear_search(url);
url_state_t next = {0}; if (ada_url_to_state(url, &next) == 0) { url_state_clear(s); *s = next; } } if (url) ada_free(url); free(qs);}
static void url_sync_usp(ant_t *js, ant_value_t url_obj, const char *query) { ant_value_t usp = js_get_slot(url_obj, SLOT_ENTRIES); if (!is_special_object(usp)) return; ant_value_t new_entries = parse_query_to_arr(js, query); js_set_slot_wb(js, usp, SLOT_ENTRIES, new_entries);}
static ant_value_t make_usp_for_url(ant_t *js, ant_value_t url_obj, const char *query) { ant_value_t usp = Ant_Object_CreateBare(js); js_set_proto_init(usp, js->builtins.usp_proto); js_set_slot(usp, SLOT_BRAND, js_mknum(BRAND_URLSEARCHPARAMS)); js_set_slot_wb(js, usp, SLOT_DATA, url_obj); js_set_slot(usp, SLOT_ENTRIES, parse_query_to_arr(js, query)); return usp;}
static ant_value_t url_get_href(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); char *href = build_href(s); if (!href) return js_mkstr(js, "", 0); ant_value_t ret = js_mkstr(js, href, strlen(href)); free(href); return ret;}
static ant_value_t url_get_protocol(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return js_mkstr(js, s->protocol, strlen(s->protocol));}
static ant_value_t url_get_username(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return js_mkstr(js, s->username, strlen(s->username));}
static ant_value_t url_get_password(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return js_mkstr(js, s->password, strlen(s->password));}
static ant_value_t url_get_host(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); if (s->port && *s->port) { size_t len = strlen(s->hostname) + strlen(s->port) + 2; char *host = malloc(len); snprintf(host, len, "%s:%s", s->hostname, s->port); ant_value_t ret = js_mkstr(js, host, strlen(host)); free(host); return ret; } return js_mkstr(js, s->hostname, strlen(s->hostname));}
static ant_value_t url_get_hostname(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return js_mkstr(js, s->hostname, strlen(s->hostname));}
static ant_value_t url_get_port(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return js_mkstr(js, s->port, strlen(s->port));}
static ant_value_t url_get_pathname(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return js_mkstr(js, s->pathname, strlen(s->pathname));}
static ant_value_t url_get_search(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); if (s->search[0] == '?' && s->search[1] == '\0') return js_mkstr(js, "", 0); return js_mkstr(js, s->search, strlen(s->search));}
static ant_value_t url_get_hash(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); if (s->hash[0] == '#' && s->hash[1] == '\0') return js_mkstr(js, "", 0); return js_mkstr(js, s->hash, strlen(s->hash));}
static ant_value_t url_get_origin(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation");
char *href = build_href(s); if (!href) return js_mkstr(js, "null", 4); ada_url url = ada_parse(href, strlen(href)); free(href); if (!url || !ada_is_valid(url)) { if (url) ada_free(url); return js_mkstr(js, "null", 4); }
ada_owned_string origin = ada_get_origin(url); ant_value_t result = origin.data ? js_mkstr(js, origin.data, origin.length) : js_mkstr(js, "null", 4); ada_free_owned_string(origin); ada_free(url); return result;}
static ant_value_t url_get_searchParams(ant_params_t) { if (!url_get_state(js->this_val)) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); ant_value_t usp = js_get_slot(js->this_val, SLOT_ENTRIES); if (vtype(usp) == kTypeObject) return usp; return js_mkundef();}
static ant_value_t url_set_href(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation");
ant_value_t value = url_string_value(js, nargs ? args[0] : js_mkundef()); if (is_err(value)) return value; size_t value_len = 0; const char *val = js_getstr(js, value, &value_len); if (!val) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid URL"); url_state_t tmp; if (parse_url_to_state_n(val, value_len, NULL, 0, &tmp) != 0) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid URL"); url_state_clear(s); *s = tmp; const char *q = (s->search[0] == '?') ? s->search + 1 : ""; url_sync_usp(js, js->this_val, q); return js_mkundef();}
static ant_value_t url_set_protocol(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_protocol);}
static ant_value_t url_set_username(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_username);}
static ant_value_t url_set_password(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_password);}
static ant_value_t url_set_host(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_host);}
static ant_value_t url_set_hostname(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_hostname);}
static ant_value_t url_set_port(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_port);}
static ant_value_t url_set_pathname(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_pathname);}
static ant_value_t url_set_search(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); ant_value_t result = url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_search_bool); if (is_err(result)) return result; const char *query = s->search && s->search[0] == '?' ? s->search + 1 : ""; url_sync_usp(js, js->this_val, query); return result;}
static ant_value_t url_set_hash(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); return url_apply_setter(js, s, nargs ? args[0] : js_mkundef(), ada_set_hash_bool);}
static ant_value_t url_toString(ant_params_t) { url_state_t *s = url_get_state(js->this_val); if (!s) return js_mkerr_typed(js, JS_ERR_TYPE, "Illegal invocation"); char *href = build_href(s); if (!href) return js_mkstr(js, "", 0); ant_value_t ret = js_mkstr(js, href, strlen(href)); free(href); return ret;}
static ant_value_t js_URL(ant_params_t) { if (is_undefined(call_new_target)) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Please use the 'new' operator."); if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': 1 argument required.");
ant_value_t url_sv = url_string_value(js, args[0]); if (is_err(url_sv)) return url_sv; size_t url_len = 0; const char *url_str = js_getstr(js, url_sv, &url_len); if (!url_str) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL.");
const char *base_str = NULL; size_t base_len = 0; ant_value_t base_sv = js_mkundef(); if (nargs > 1 && !is_undefined(args[1])) { base_sv = url_string_value(js, args[1]); if (is_err(base_sv)) return base_sv; base_str = js_getstr(js, base_sv, &base_len); }
url_state_t *s = calloc(1, sizeof(url_state_t)); if (!s) return js_mkerr(js, "out of memory");
if (parse_url_to_state_n(url_str, url_len, base_str, base_len, s) != 0) { free(s); return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URL': Invalid URL."); }
ant_value_t obj = Ant_Object_CreateBare(js); ant_value_t proto = js_instance_proto_from_new_target(js, js->builtins.url_proto, call_new_target); js_set_proto_init(obj, is_object_type(proto) ? proto : js->builtins.url_proto); js_set_native(obj, s, URL_NATIVE_TAG); js_set_finalizer(obj, url_finalize);
const char *query = (s->search && s->search[0] == '?') ? s->search + 1 : ""; ant_value_t usp = make_usp_for_url(js, obj, query); js_set_slot_wb(js, obj, SLOT_ENTRIES, usp);
return obj;}
ant_value_t make_url_obj(ant_t *js, url_state_t *s) { ant_value_t obj = Ant_Object_CreateBare(js); js_set_proto_init(obj, js->builtins.url_proto); js_set_native(obj, s, URL_NATIVE_TAG); js_set_finalizer(obj, url_finalize); const char *query = (s->search && s->search[0] == '?') ? s->search + 1 : ""; ant_value_t usp = make_usp_for_url(js, obj, query); js_set_slot_wb(js, obj, SLOT_ENTRIES, usp); return obj;}
static ant_value_t url_canParse(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "URL.canParse requires at least 1 argument"); ant_value_t url_sv = url_string_value(js, args[0]); if (is_err(url_sv)) return url_sv; size_t url_len = 0; const char *url_str = js_getstr(js, url_sv, &url_len); if (!url_str) return js_false; const char *base_str = NULL; size_t base_len = 0; ant_value_t base_sv = js_mkundef(); if (nargs > 1 && !is_undefined(args[1])) { base_sv = url_string_value(js, args[1]); if (is_err(base_sv)) return base_sv; base_str = js_getstr(js, base_sv, &base_len); } url_state_t s; if (parse_url_to_state_n(url_str, url_len, base_str, base_len, &s) != 0) return js_false; url_state_clear(&s); return js_true;}
static ant_value_t url_parse(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "URL.parse requires at least 1 argument"); ant_value_t url_sv = url_string_value(js, args[0]); if (is_err(url_sv)) return url_sv; size_t url_len = 0; const char *url_str = js_getstr(js, url_sv, &url_len); if (!url_str) return js_mknull(); const char *base_str = NULL; size_t base_len = 0; ant_value_t base_sv = js_mkundef(); if (nargs > 1 && !is_undefined(args[1])) { base_sv = url_string_value(js, args[1]); if (is_err(base_sv)) return base_sv; base_str = js_getstr(js, base_sv, &base_len); } url_state_t *s = calloc(1, sizeof(url_state_t)); if (!s) return js_mknull(); if (parse_url_to_state_n(url_str, url_len, base_str, base_len, s) != 0) { free(s); return js_mknull(); } return make_url_obj(js, s);}
static size_t object_url_key_length(const char *url) { const char *hash = url ? strchr(url, '#') : NULL; return hash ? (size_t)(hash - url) : (url ? strlen(url) : 0);}
ant_value_t url_resolve_object_url(ant_t *js, const char *url) { if (!url || strncmp(url, "blob:", 5) != 0 || !is_object_type(js->builtins.url_ctor)) return js_mkundef();
ant_value_t store = js_get_slot(js->builtins.url_ctor, SLOT_DATA); if (vtype(store) != kTypeArray) return js_mkundef();
size_t url_len = object_url_key_length(url); ant_offset_t len = js_arr_len(js, store); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, store, i); ant_value_t key = js_arr_get(js, entry, 0); size_t key_len = 0; const char *key_str = js_getstr(js, key, &key_len); if (key_str && key_len == url_len && memcmp(key_str, url, url_len) == 0) return js_arr_get(js, entry, 1); }
return js_mkundef();}
static ant_value_t url_create_object_url(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "URL.createObjectURL requires at least 1 argument"); if (!blob_is_blob(js, args[0])) return js_mkerr_typed(js, JS_ERR_TYPE, "URL.createObjectURL requires a Blob");
char uuid[37]; if (crypto_random_uuid(uuid) < 0) return js_mkerr(js, "failed to generate blob URL");
char url[sizeof("blob:ant/") + 36]; snprintf(url, sizeof(url), "blob:ant/%s", uuid); ant_value_t url_value = js_mkstr(js, url, strlen(url));
ant_value_t store = js_get_slot(js->builtins.url_ctor, SLOT_DATA); if (vtype(store) != kTypeArray) { store = js_mkarr(js); js_set_slot_wb(js, js->builtins.url_ctor, SLOT_DATA, store); }
ant_value_t entry = js_mkarr(js); js_arr_push(js, entry, url_value); js_arr_push(js, entry, args[0]); js_arr_push(js, store, entry); return url_value;}
static ant_value_t url_revoke_object_url(ant_params_t) { if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "URL.revokeObjectURL requires at least 1 argument");
ant_value_t value = url_string_value(js, args[0]); if (is_err(value)) return value; size_t url_len = 0; const char *url = js_getstr(js, value, &url_len); if (!url || url_len < 5 || strncmp(url, "blob:", 5) != 0) return js_mkundef(); url_len = object_url_key_length(url);
ant_value_t store = js_get_slot(js->builtins.url_ctor, SLOT_DATA); if (vtype(store) != kTypeArray) return js_mkundef();
ant_value_t next = js_mkarr(js); ant_offset_t len = js_arr_len(js, store); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, store, i); ant_value_t key = js_arr_get(js, entry, 0); size_t key_len = 0; const char *key_str = js_getstr(js, key, &key_len); if (key_str && key_len == url_len && memcmp(key_str, url, url_len) == 0) continue; js_arr_push(js, next, entry); } js_set_slot_wb(js, js->builtins.url_ctor, SLOT_DATA, next); return js_mkundef();}
static ant_value_t usp_get(ant_params_t) { if (nargs < 1) return js_mknull(); ant_value_t key_sv = (vtype(args[0]) == kTypeString) ? args[0] : js_tostring_val(js, args[0]); if (is_err(key_sv)) return key_sv; const char *key = js_getstr(js, key_sv, NULL); if (!key) return js_mknull(); ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return js_mknull(); ant_offset_t len = js_arr_len(js, entries); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, entries, i); const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); if (ek && strcmp(ek, key) == 0) return js_arr_get(js, entry, 1); } return js_mknull();}
static ant_value_t usp_getAll(ant_params_t) { ant_value_t result = js_mkarr(js); if (nargs < 1) return result; ant_value_t key_sv = (vtype(args[0]) == kTypeString) ? args[0] : js_tostring_val(js, args[0]); if (is_err(key_sv)) return key_sv; const char *key = js_getstr(js, key_sv, NULL); if (!key) return result; ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return result; ant_offset_t len = js_arr_len(js, entries); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, entries, i); const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); if (ek && strcmp(ek, key) == 0) js_arr_push(js, result, js_arr_get(js, entry, 1)); } return result;}
static ant_value_t usp_has(ant_params_t) { if (nargs < 1) return js_false; ant_value_t key_sv = (vtype(args[0]) == kTypeString) ? args[0] : js_tostring_val(js, args[0]); if (is_err(key_sv)) return key_sv; const char *key = js_getstr(js, key_sv, NULL); if (!key) return js_false; const char *match_val = NULL; if (nargs >= 2 && !is_undefined(args[1])) { ant_value_t mv_sv = (vtype(args[1]) == kTypeString) ? args[1] : js_tostring_val(js, args[1]); if (is_err(mv_sv)) return mv_sv; match_val = js_getstr(js, mv_sv, NULL); } ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return js_false; ant_offset_t len = js_arr_len(js, entries); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, entries, i); const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); if (!ek || strcmp(ek, key) != 0) continue; if (!match_val) return js_true; const char *ev = js_getstr(js, js_arr_get(js, entry, 1), NULL); if (ev && strcmp(ev, match_val) == 0) return js_true; } return js_false;}
static ant_value_t usp_set(ant_params_t) { if (nargs < 2) return js_mkundef(); ant_value_t key_sv = (vtype(args[0]) == kTypeString) ? args[0] : js_tostring_val(js, args[0]); if (is_err(key_sv)) return key_sv; ant_value_t val_sv = (vtype(args[1]) == kTypeString) ? args[1] : js_tostring_val(js, args[1]); if (is_err(val_sv)) return val_sv; const char *key = js_getstr(js, key_sv, NULL); if (!key) return js_mkundef(); ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; ant_value_t new_entries = js_mkarr(js); int found = 0; for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, entries, i); const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); if (ek && strcmp(ek, key) == 0) { if (!found) { ant_value_t ne = js_mkarr(js); js_arr_push(js, ne, key_sv); js_arr_push(js, ne, val_sv); js_arr_push(js, new_entries, ne); found = 1; } } else js_arr_push(js, new_entries, entry); } if (!found) { ant_value_t ne = js_mkarr(js); js_arr_push(js, ne, key_sv); js_arr_push(js, ne, val_sv); js_arr_push(js, new_entries, ne); } js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); usp_push_to_url(js, js->this_val); return js_mkundef();}
static ant_value_t usp_append(ant_params_t) { if (nargs < 2) return js_mkundef(); ant_value_t key_sv = (vtype(args[0]) == kTypeString) ? args[0] : js_tostring_val(js, args[0]); if (is_err(key_sv)) return key_sv; ant_value_t val_sv = (vtype(args[1]) == kTypeString) ? args[1] : js_tostring_val(js, args[1]); if (is_err(val_sv)) return val_sv; ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return js_mkundef(); ant_value_t entry = js_mkarr(js); js_arr_push(js, entry, key_sv); js_arr_push(js, entry, val_sv); js_arr_push(js, entries, entry); usp_push_to_url(js, js->this_val); return js_mkundef();}
static ant_value_t usp_delete(ant_params_t) { if (nargs < 1) return js_mkundef(); ant_value_t key_sv = (vtype(args[0]) == kTypeString) ? args[0] : js_tostring_val(js, args[0]); if (is_err(key_sv)) return key_sv; const char *key = js_getstr(js, key_sv, NULL); if (!key) return js_mkundef(); const char *match_val = NULL; if (nargs >= 2 && !is_undefined(args[1])) { ant_value_t mv_sv = (vtype(args[1]) == kTypeString) ? args[1] : js_tostring_val(js, args[1]); if (is_err(mv_sv)) return mv_sv; match_val = js_getstr(js, mv_sv, NULL); } ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); ant_offset_t len = is_special_object(entries) ? js_arr_len(js, entries) : 0; ant_value_t new_entries = js_mkarr(js); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, entries, i); const char *ek = js_getstr(js, js_arr_get(js, entry, 0), NULL); if (ek && strcmp(ek, key) == 0) { if (!match_val) continue; const char *ev = js_getstr(js, js_arr_get(js, entry, 1), NULL); if (ev && strcmp(ev, match_val) == 0) continue; } js_arr_push(js, new_entries, entry); } js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); usp_push_to_url(js, js->this_val); return js_mkundef();}
static ant_value_t usp_toString(ant_params_t) { char *s = usp_serialize(js, js->this_val); ant_value_t ret = js_mkstr(js, s, strlen(s)); free(s); return ret;}
static ant_value_t usp_forEach(ant_params_t) { if (nargs < 1 || !is_callable(args[0])) return js_mkundef(); ant_value_t cb = args[0]; ant_value_t this_arg = (nargs >= 2) ? args[1] : js_mkundef(); ant_value_t self = js->this_val; ant_value_t entries = js_get_slot(self, SLOT_ENTRIES); if (!is_special_object(entries)) return js_mkundef(); ant_offset_t len = js_arr_len(js, entries); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, entries, i); ant_value_t k = js_arr_get(js, entry, 0); ant_value_t v = js_arr_get(js, entry, 1); ant_value_t cb_args[3] = { v, k, self }; ant_value_t r = sv_vm_call(js->vm, js, cb, this_arg, cb_args, 3, NULL, js_mkundef()); if (is_err(r)) return r; } return js_mkundef();}
static ant_value_t usp_size_get(ant_params_t) { ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return js_mknum(0); return js_mknum((double)js_arr_len(js, entries));}
static ant_value_t usp_sort(ant_params_t) { ant_value_t entries = js_get_slot(js->this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return js_mkundef(); ant_offset_t len = js_arr_len(js, entries); if (len <= 1) return js_mkundef();
ant_value_t *arr = malloc(sizeof(ant_value_t) * (size_t)len); if (!arr) return js_mkundef(); for (ant_offset_t i = 0; i < len; i++) arr[i] = js_arr_get(js, entries, i);
for (ant_offset_t i = 1; i < len; i++) { ant_value_t cur = arr[i]; const char *ck = js_getstr(js, js_arr_get(js, cur, 0), NULL); ant_offset_t j = i; while (j > 0) { const char *jk = js_getstr(js, js_arr_get(js, arr[j - 1], 0), NULL); if (strcmp(jk ? jk : "", ck ? ck : "") <= 0) break; arr[j] = arr[j - 1]; j--; } arr[j] = cur; }
ant_value_t new_entries = js_mkarr(js); for (ant_offset_t i = 0; i < len; i++) js_arr_push(js, new_entries, arr[i]); free(arr);
js_set_slot_wb(js, js->this_val, SLOT_ENTRIES, new_entries); usp_push_to_url(js, js->this_val); return js_mkundef();}
static ant_value_t usp_iter_next(ant_params_t) { ant_value_t state_v = js_get_slot(js->this_val, SLOT_ITER_STATE); if (vtype(state_v) != kTypeNumber) return js_iter_result(js, false, js_mkundef());
uint32_t state = (uint32_t)js_getnum(state_v); uint32_t kind = iter_state_kind(state); uint32_t idx = iter_state_index(state);
ant_value_t usp = js_get_slot(js->this_val, SLOT_DATA); ant_value_t entries = js_get_slot(usp, SLOT_ENTRIES); if (!is_special_object(entries) || (ant_offset_t)idx >= js_arr_len(js, entries)) return js_iter_result(js, false, js_mkundef());
js_set_slot(js->this_val, SLOT_ITER_STATE, js_mknum((double)iter_state_pack(kind, idx + 1)));
ant_value_t entry = js_arr_get(js, entries, (ant_offset_t)idx); ant_value_t k = js_arr_get(js, entry, 0); ant_value_t v = js_arr_get(js, entry, 1);
ant_value_t out; switch (kind) { case USP_ITER_KEYS: out = k; break; case USP_ITER_VALUES: out = v; break; default: { out = js_mkarr(js); js_arr_push(js, out, k); js_arr_push(js, out, v); break; }} return js_iter_result(js, true, out);}
static ant_value_t make_usp_iter(ant_t *js, ant_value_t usp, int kind) { ant_value_t iter = Ant_Object_CreateBare(js); js_set_proto_init(iter, js->builtins.usp_iter_proto); js_set_slot_wb(js, iter, SLOT_DATA, usp); js_set_slot(iter, SLOT_ITER_STATE, js_mknum((double)iter_state_pack(kind, 0))); return iter;}
static ant_value_t usp_entries_fn(ant_params_t) { return make_usp_iter(js, js->this_val, USP_ITER_ENTRIES);}
static ant_value_t usp_keys_fn(ant_params_t) { return make_usp_iter(js, js->this_val, USP_ITER_KEYS);}
static ant_value_t usp_values_fn(ant_params_t) { return make_usp_iter(js, js->this_val, USP_ITER_VALUES);}
static ant_value_t js_URLSearchParams(ant_params_t) { if (is_undefined(call_new_target)) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URLSearchParams': Please use the 'new' operator.");
ant_value_t obj = Ant_Object_CreateBare(js); js_set_proto_init(obj, js->builtins.usp_proto); js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_URLSEARCHPARAMS)); js_set_slot(obj, SLOT_DATA, js_mkundef()); ant_value_t entries = js_mkarr(js); js_set_slot(obj, SLOT_ENTRIES, entries);
if (nargs < 1 || is_undefined(args[0]) || is_null(args[0])) return obj;
ant_value_t init = args[0]; uint8_t t = vtype(init);
if (t == kTypeString) { const char *s = js_getstr(js, init, NULL); if (s) { const char *q = (s[0] == '?') ? s + 1 : s; js_set_slot(obj, SLOT_ENTRIES, parse_query_to_arr(js, q)); } return obj; }
bool init_is_array = t == kTypeArray; if (!init_is_array && t == kTypeObject) { ant_value_t is_array_res = js_is_array_value_checked(js, init, &init_is_array); if (is_err(is_array_res)) return is_array_res; if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); }
if (init_is_array) { ant_offset_t len = 0; ant_value_t len_res = usp_array_len(js, init, &len); if (is_err(len_res)) return len_res; for (ant_offset_t i = 0; i < len; i++) { ant_value_t pair = usp_array_get(js, init, i); if (is_err(pair)) return pair; bool pair_is_array = vtype(pair) == kTypeArray; if (!pair_is_array && vtype(pair) == kTypeObject) { ant_value_t is_array_res = js_is_array_value_checked(js, pair, &pair_is_array); if (is_err(is_array_res)) return is_array_res; if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); } if (!pair_is_array) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URLSearchParams': Each element must be an array."); ant_offset_t plen = 0; ant_value_t plen_res = usp_array_len(js, pair, &plen); if (is_err(plen_res)) return plen_res; if (plen != 2) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'URLSearchParams': Each pair must have exactly 2 elements."); ant_value_t pk = usp_array_get(js, pair, 0); if (is_err(pk)) return pk; ant_value_t pv = usp_array_get(js, pair, 1); if (is_err(pv)) return pv; ant_value_t ksv = (vtype(pk) == kTypeString) ? pk : js_tostring_val(js, pk); if (is_err(ksv)) return ksv; ant_value_t vsv = (vtype(pv) == kTypeString) ? pv : js_tostring_val(js, pv); if (is_err(vsv)) return vsv; ant_value_t entry = js_mkarr(js); js_arr_push(js, entry, ksv); js_arr_push(js, entry, vsv); js_arr_push(js, entries, entry); } return obj; }
if (is_special_object(init)) { ant_value_t src = js_get_slot(init, SLOT_ENTRIES); if (vtype(src) == kTypeArray) { ant_offset_t len = js_arr_len(js, src); for (ant_offset_t i = 0; i < len; i++) { ant_value_t entry = js_arr_get(js, src, i); ant_value_t ne = js_mkarr(js); js_arr_push(js, ne, js_arr_get(js, entry, 0)); js_arr_push(js, ne, js_arr_get(js, entry, 1)); js_arr_push(js, entries, ne); } return obj; } ant_iter_t it = js_prop_iter_begin(js, init); const char *key; size_t key_len; ant_value_t val; while (js_prop_iter_next(&it, &key, &key_len, &val)) { ant_value_t sv = (vtype(val) == kTypeString) ? val : js_tostring_val(js, val); if (is_err(sv)) { js_prop_iter_end(&it); return sv; } ant_value_t entry = js_mkarr(js); js_arr_push(js, entry, js_mkstr(js, key, key_len)); js_arr_push(js, entry, sv); js_arr_push(js, entries, entry); } js_prop_iter_end(&it); }
return obj;}
void init_url_module(ant_t *js) { js->builtins.usp_iter_proto = Ant_Object_CreateBare(js); js_set_proto_init(js->builtins.usp_iter_proto, js->sym.iterator_proto); js_set(js, js->builtins.usp_iter_proto, "next", Ant_Function_Create(usp_iter_next)); js_set_descriptor(js, js->builtins.usp_iter_proto, "next", 4, JS_DESC_W | JS_DESC_E | JS_DESC_C); js_set_sym(js, js->builtins.usp_iter_proto, js->sym.iterator_sym, Ant_Function_Create(sym_this_cb));
js->builtins.usp_proto = Ant_Object_CreateBare(js); js_set(js, js->builtins.usp_proto, "get", Ant_Function_CreateWithArity(usp_get, 1)); js_set(js, js->builtins.usp_proto, "getAll", Ant_Function_CreateWithArity(usp_getAll, 1)); js_set(js, js->builtins.usp_proto, "has", Ant_Function_CreateWithArity(usp_has, 1)); js_set(js, js->builtins.usp_proto, "set", Ant_Function_CreateWithArity(usp_set, 2)); js_set(js, js->builtins.usp_proto, "append", Ant_Function_CreateWithArity(usp_append, 2)); js_set(js, js->builtins.usp_proto, "delete", Ant_Function_CreateWithArity(usp_delete, 1)); js_set(js, js->builtins.usp_proto, "sort", Ant_Function_Create(usp_sort)); js_set(js, js->builtins.usp_proto, "toString", Ant_Function_Create(usp_toString)); js_set(js, js->builtins.usp_proto, "forEach", Ant_Function_CreateWithArity(usp_forEach, 1)); js_set_getter_desc(js, js->builtins.usp_proto, "size", 4, Ant_Function_Create(usp_size_get), JS_DESC_C);
js_set(js, js->builtins.usp_proto, "entries", Ant_Function_Create(usp_entries_fn)); js_set(js, js->builtins.usp_proto, "keys", Ant_Function_Create(usp_keys_fn)); js_set(js, js->builtins.usp_proto, "values", Ant_Function_Create(usp_values_fn)); js_set_sym(js, js->builtins.usp_proto, js->sym.iterator_sym, js_get(js, js->builtins.usp_proto, "entries")); js_set_tostringtag(js, js->builtins.usp_proto, js_mkstr(js, "URLSearchParams", 15));
ant_value_t usp_ctor = Ant_Function_CreateIntrinsicConstructor(js, js_URLSearchParams, js->builtins.usp_proto, "URLSearchParams", 0); js_set_global_builtin(js, "URLSearchParams", usp_ctor);
js->builtins.url_proto = Ant_Object_CreateBare(js); js_set_accessor_desc(js, js->builtins.url_proto, "href", 4, Ant_Function_Create(url_get_href), Ant_Function_Create(url_set_href), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "protocol", 8, Ant_Function_Create(url_get_protocol), Ant_Function_Create(url_set_protocol), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "username", 8, Ant_Function_Create(url_get_username), Ant_Function_Create(url_set_username), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "password", 8, Ant_Function_Create(url_get_password), Ant_Function_Create(url_set_password), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "host", 4, Ant_Function_Create(url_get_host), Ant_Function_Create(url_set_host), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "hostname", 8, Ant_Function_Create(url_get_hostname), Ant_Function_Create(url_set_hostname), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "port", 4, Ant_Function_Create(url_get_port), Ant_Function_Create(url_set_port), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "pathname", 8, Ant_Function_Create(url_get_pathname), Ant_Function_Create(url_set_pathname), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "search", 6, Ant_Function_Create(url_get_search), Ant_Function_Create(url_set_search), JS_DESC_E | JS_DESC_C); js_set_accessor_desc(js, js->builtins.url_proto, "hash", 4, Ant_Function_Create(url_get_hash), Ant_Function_Create(url_set_hash), JS_DESC_E | JS_DESC_C); js_set_getter_desc(js, js->builtins.url_proto, "origin", 6, Ant_Function_Create(url_get_origin), JS_DESC_E | JS_DESC_C); js_set_getter_desc(js, js->builtins.url_proto, "searchParams", 12, Ant_Function_Create(url_get_searchParams), JS_DESC_E | JS_DESC_C); js_set(js, js->builtins.url_proto, "toString", Ant_Function_Create(url_toString)); js_set(js, js->builtins.url_proto, "toJSON", Ant_Function_Create(url_toString)); js_set_tostringtag(js, js->builtins.url_proto, js_mkstr(js, "URL", 3));
js->builtins.url_ctor = Ant_Function_CreateIntrinsicConstructor(js, js_URL, js->builtins.url_proto, "URL", 1); js_set_slot(js->builtins.url_ctor, SLOT_DATA, js_mkarr(js)); js_set(js, js->builtins.url_ctor, "canParse", Ant_Function_CreateWithArity(url_canParse, 1)); js_set(js, js->builtins.url_ctor, "createObjectURL", Ant_Function_CreateWithArity(url_create_object_url, 1)); js_set(js, js->builtins.url_ctor, "parse", Ant_Function_CreateWithArity(url_parse, 1)); js_set(js, js->builtins.url_ctor, "revokeObjectURL", Ant_Function_CreateWithArity(url_revoke_object_url, 1)); js_set_global_builtin(js, "URL", js->builtins.url_ctor);}
static ant_value_t builtin_fileURLToPath(ant_params_t) { if (nargs < 1) return js_mkerr(js, "fileURLToPath requires a string or URL argument"); size_t len; const char *str = coerce_to_string(js, args[0], &len); if (!str) return js_mkerr(js, "fileURLToPath requires a string or URL argument");
url_state_t s; if (parse_url_to_state(str, NULL, &s) != 0) return js_mkerr(js, "Invalid URL"); if (strcmp(s.protocol, "file:") != 0) { url_state_clear(&s); return js_mkerr(js, "fileURLToPath requires a file: URL"); } char *decoded = url_decode_component(s.pathname); url_state_clear(&s); if (!decoded) return js_mkerr(js, "allocation failure"); ant_value_t ret = js_mkstr(js, decoded, strlen(decoded)); free(decoded); return ret;}
static ant_value_t builtin_pathToFileURL(ant_params_t) { if (nargs < 1 || vtype(args[0]) != kTypeString) return js_mkerr(js, "pathToFileURL requires a string argument"); size_t len; const char *path = js_getstr(js, args[0], &len); size_t total = 7 + len; char *buf = malloc(total + 1); if (!buf) return js_mkerr(js, "allocation failure"); memcpy(buf, "file://", 7); memcpy(buf + 7, path, len); buf[total] = '\0'; url_state_t *s = calloc(1, sizeof(url_state_t)); if (!s) { free(buf); return js_mkerr(js, "allocation failure"); } if (parse_url_to_state(buf, NULL, s) != 0) { free(buf); free(s); return js_mkerr(js, "Invalid file URL"); } free(buf); return make_url_obj(js, s);}
static bool url_fmt_get_string_prop( ant_t *js, ant_value_t obj, const char *name, ant_value_t *out, const char **str, size_t *len) { *out = js_get(js, obj, name); if (is_err(*out)) return false; if (is_undefined(*out) || is_null(*out)) return true;
ant_value_t str_val = vtype(*out) == kTypeString ? *out : js_tostring_val(js, *out); if (is_err(str_val)) return false;
*out = str_val; *str = js_getstr(js, str_val, len); return *str != NULL;}
static bool url_fmt_is_query_unescaped(unsigned char c) { return isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~';}
static bool url_fmt_append_query_component(url_fmt_buf_t *b, const char *s, size_t len) { static const char hex[] = "0123456789ABCDEF";
for (size_t i = 0; i < len; i++) { unsigned char c = (unsigned char)s[i]; if (url_fmt_is_query_unescaped(c)) { if (!url_fmt_append_c(b, (char)c)) return false; } else { char esc[3] = { '%', hex[c >> 4], hex[c & 0x0f] }; if (!url_fmt_append_n(b, esc, sizeof(esc))) return false; }} return true;}
static bool url_fmt_append_query_value(ant_t *js, url_fmt_buf_t *b, ant_value_t value) { uint8_t type = vtype(value); if (type == kTypeNumber && !isfinite(js_getnum(value))) return true; if (type != kTypeString && type != kTypeNumber && type != kTypeBool && type != kTypeBigInt) return true;
ant_value_t str_val = type == kTypeString ? value : js_tostring_val(js, value); if (is_err(str_val)) return false;
size_t len = 0; const char *str = js_getstr(js, str_val, &len); return str && url_fmt_append_query_component(b, str, len);}
static bool url_fmt_append_query_object(ant_t *js, url_fmt_buf_t *b, ant_value_t query) { if (!is_special_object(query)) return true;
ant_iter_t it = js_prop_iter_begin(js, query); const char *key; size_t key_len; ant_value_t val;
while (js_prop_iter_next(&it, &key, &key_len, &val)) { val = js_getprop_fallback_len(js, query, key, key_len); if (is_err(val)) goto fail;
bool is_array = false; if (is_err(js_is_array_value_checked(js, val, &is_array))) goto fail; ant_offset_t count = is_array ? js_arr_len(js, val) : 1; if (Ant_Exception_Pending(js)) goto fail;
for (ant_offset_t i = 0; i < count; i++) { ant_value_t item = is_array ? js_arr_get(js, val, i) : val; if (is_err(item) || Ant_Exception_Pending(js)) goto fail; if (b->len && !url_fmt_append_c(b, '&')) goto fail; if (!url_fmt_append_query_component(b, key, key_len) || !url_fmt_append_c(b, '=') || !url_fmt_append_query_value(js, b, item)) goto fail; } }
js_prop_iter_end(&it); return true;
fail: js_prop_iter_end(&it); return false;}
static bool url_fmt_protocol_needs_slashes(const char *protocol, size_t len) { if (len > 0 && protocol[len - 1] == ':') len--; return (len == 4 && memcmp(protocol, "http", 4) == 0) || (len == 5 && memcmp(protocol, "https", 5) == 0) || (len == 3 && memcmp(protocol, "ftp", 3) == 0) || (len == 4 && memcmp(protocol, "file", 4) == 0) || (len == 2 && memcmp(protocol, "ws", 2) == 0) || (len == 3 && memcmp(protocol, "wss", 3) == 0);}
static ant_value_t builtin_domainToASCII(ant_params_t) { if (nargs < 1) { ant_value_t props = Ant_Object_CreateBare(js); js_set(js, props, "code", js_mkstr(js, "ERR_MISSING_ARGS", 16)); return js_mkerr_props(js, JS_ERR_TYPE, props, "The \"domain\" argument must be specified"); }
ant_value_t domain = js_template_to_string(js, args[0]); if (is_err(domain)) return domain; if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js);
size_t len = 0; const char *value = js_getstr(js, domain, &len); if (!value || len == 0) return js_mkstr(js, "", 0);
ada_url url = ada_parse("ws://x", 6); if (!ada_set_hostname(url, value, len)) { ada_free(url); return js_mkstr(js, "", 0); }
ada_string hostname = ada_get_hostname(url); ant_value_t result = js_mkstr(js, hostname.data, hostname.length); ada_free(url); return result;}
static ant_value_t builtin_url_parse(ant_params_t) { size_t len = 0; const char *value = NULL;
if (nargs < 1 || vtype(args[0]) != kTypeString) return js_mkerr_typed(js, JS_ERR_TYPE, "The \"url\" argument must be of type string");
value = js_getstr(js, args[0], &len); if (!value) return js_mkerr_typed(js, JS_ERR_TYPE, "The \"url\" argument must be of type string");
return legacy_url_parse_impl( js, value, len, nargs > 1 && js_truthy(js, args[1]), nargs > 2 && js_truthy(js, args[2]) );}
static ant_value_t builtin_url_format(ant_params_t) { if (nargs < 1 || !is_object_type(args[0])) return js_mkerr_typed(js, JS_ERR_TYPE, "url.format() requires a URL or object argument");
url_state_t *state = url_get_state(args[0]); if (state) { char *href = build_href(state); if (!href) return js_mkstr(js, "", 0); ant_value_t ret = js_mkstr(js, href, strlen(href)); free(href); return ret; }
ant_value_t obj = args[0]; ant_value_t tmp; const char *protocol = NULL, *auth = NULL, *host = NULL, *hostname = NULL; const char *port = NULL, *pathname = NULL, *search = NULL, *hash = NULL; size_t protocol_len = 0, auth_len = 0, host_len = 0, hostname_len = 0; size_t port_len = 0, pathname_len = 0, search_len = 0, hash_len = 0;
if (!url_fmt_get_string_prop(js, obj, "protocol", &tmp, &protocol, &protocol_len) || !url_fmt_get_string_prop(js, obj, "auth", &tmp, &auth, &auth_len) || !url_fmt_get_string_prop(js, obj, "host", &tmp, &host, &host_len) || !url_fmt_get_string_prop(js, obj, "hostname", &tmp, &hostname, &hostname_len) || !url_fmt_get_string_prop(js, obj, "port", &tmp, &port, &port_len) || !url_fmt_get_string_prop(js, obj, "pathname", &tmp, &pathname, &pathname_len) || !url_fmt_get_string_prop(js, obj, "search", &tmp, &search, &search_len) || !url_fmt_get_string_prop(js, obj, "hash", &tmp, &hash, &hash_len)) return Ant_Exception_Current(js);
url_fmt_buf_t b = {0};
if (protocol && protocol_len > 0) { if (!url_fmt_append_n(&b, protocol, protocol_len)) goto oom; if (protocol[protocol_len - 1] != ':' && !url_fmt_append_c(&b, ':')) goto oom; }
bool has_host = (host && host_len > 0) || (hostname && hostname_len > 0); ant_value_t slashes_val = js_get(js, obj, "slashes"); if (is_err(slashes_val)) goto oom; bool needs_slashes = js_truthy(js, slashes_val) || (protocol && url_fmt_protocol_needs_slashes(protocol, protocol_len)); if (needs_slashes && (has_host || (protocol && protocol_len >= 4 && memcmp(protocol, "file", 4) == 0))) { if (!url_fmt_append(&b, "//")) goto oom; }
if (auth && auth_len > 0) { if (!url_fmt_append_n(&b, auth, auth_len)) goto oom; if (!url_fmt_append_c(&b, '@')) goto oom; }
if (host && host_len > 0) { if (!url_fmt_append_n(&b, host, host_len)) goto oom; } else if (hostname && hostname_len > 0) { if (!url_fmt_append_n(&b, hostname, hostname_len)) goto oom; if (port && port_len > 0) { if (!url_fmt_append_c(&b, ':')) goto oom; if (!url_fmt_append_n(&b, port, port_len)) goto oom; }}
if (pathname && pathname_len > 0) { if (has_host && pathname[0] != '/' && !url_fmt_append_c(&b, '/')) goto oom; if (!url_fmt_append_n(&b, pathname, pathname_len)) goto oom; }
if (search && search_len > 0) { if (search[0] != '?' && !url_fmt_append_c(&b, '?')) goto oom; if (!url_fmt_append_n(&b, search, search_len)) goto oom; } else { ant_value_t query = js_get(js, obj, "query"); if (is_err(query)) goto oom; if (vtype(query) == kTypeString) { size_t qlen = 0; const char *q = js_getstr(js, query, &qlen); if (q && qlen > 0) { if (!url_fmt_append_c(&b, '?')) goto oom; if (!url_fmt_append_n(&b, q, qlen)) goto oom; } } else if (is_special_object(query)) { url_fmt_buf_t qb = {0}; if (!url_fmt_append_query_object(js, &qb, query)) { free(qb.buf); goto oom; } if (qb.len > 0) { if (!url_fmt_append_c(&b, '?')) { free(qb.buf); goto oom; } if (!url_fmt_append_n(&b, qb.buf, qb.len)) { free(qb.buf); goto oom; }} free(qb.buf); } }
if (hash && hash_len > 0) { if (hash[0] != '#' && !url_fmt_append_c(&b, '#')) goto oom; if (!url_fmt_append_n(&b, hash, hash_len)) goto oom; }
ant_value_t ret = js_mkstr(js, b.buf ? b.buf : "", b.len); free(b.buf); return ret;
oom: free(b.buf); if (Ant_Exception_Pending(js)) return Ant_Exception_Current(js); return js_mkerr(js, "allocation failure");}
ant_value_t url_library(ant_t *js) { ant_value_t lib = Ant_Object_CreateBare(js); ant_value_t glob = js_glob(js); js_set(js, lib, "URL", js_get(js, glob, "URL")); js_set(js, lib, "URLSearchParams", js_get(js, glob, "URLSearchParams")); js_set(js, lib, "fileURLToPath", Ant_Function_CreateWithArity(builtin_fileURLToPath, 1)); js_set(js, lib, "pathToFileURL", Ant_Function_CreateWithArity(builtin_pathToFileURL, 2)); js_set(js, lib, "domainToASCII", Ant_Function_CreateWithArity(builtin_domainToASCII, 1)); js_set(js, lib, "parse", Ant_Function_CreateWithArity(builtin_url_parse, 3)); js_set(js, lib, "format", Ant_Function_CreateWithArity(builtin_url_format, 2)); js_set(js, lib, "default", lib); return lib;}