Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4424333
url.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
57 KB
Referenced Files
None
Subscribers
None
url.c
View Options
#include
<compat.h>
// IWYU pragma: keep
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
#include
<ctype.h>
#include
<uriparser/Uri.h>
#include
"ant.h"
#include
"errors.h"
#include
"internal.h"
#include
"runtime.h"
#include
"descriptors.h"
#include
"silver/engine.h"
#include
"modules/url.h"
#include
"modules/symbol.h"
static
ant_value_t
g_url_proto
=
0
;
static
ant_value_t
g_usp_proto
=
0
;
static
ant_value_t
g_usp_iter_proto
=
0
;
enum
{
USP_ITER_ENTRIES
=
0
,
USP_ITER_KEYS
=
1
,
USP_ITER_VALUES
=
2
};
url_state_t
*
url_get_state
(
ant_value_t
obj
)
{
ant_value_t
slot
=
js_get_slot
(
obj
,
SLOT_DATA
);
if
(
vtype
(
slot
)
!=
T_NUM
)
return
NULL
;
return
(
url_state_t
*
)(
uintptr_t
)(
size_t
)
js_getnum
(
slot
);
}
bool
usp_is_urlsearchparams
(
ant_t
*
js
,
ant_value_t
obj
)
{
return
js_check_brand
(
obj
,
BRAND_URLSEARCHPARAMS
);
}
void
url_state_clear
(
url_state_t
*
s
)
{
free
(
s
->
protocol
);
free
(
s
->
username
);
free
(
s
->
password
);
free
(
s
->
hostname
);
free
(
s
->
port
);
free
(
s
->
pathname
);
free
(
s
->
search
);
free
(
s
->
hash
);
}
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
)
{
if
(
!
obj
->
extra_slots
)
return
;
ant_extra_slot_t
*
slots
=
(
ant_extra_slot_t
*
)
obj
->
extra_slots
;
for
(
uint8_t
i
=
0
;
i
<
obj
->
extra_count
;
i
++
)
{
if
(
slots
[
i
].
slot
==
SLOT_DATA
&&
vtype
(
slots
[
i
].
value
)
==
T_NUM
)
{
url_free_state
((
url_state_t
*
)(
uintptr_t
)(
size_t
)
js_getnum
(
slots
[
i
].
value
));
return
;
}}
}
static
int
default_port_for
(
const
char
*
proto
)
{
if
(
!
proto
)
return
-1
;
if
(
strcmp
(
proto
,
"http:"
)
==
0
||
strcmp
(
proto
,
"ws:"
)
==
0
)
return
80
;
if
(
strcmp
(
proto
,
"https:"
)
==
0
||
strcmp
(
proto
,
"wss:"
)
==
0
)
return
443
;
if
(
strcmp
(
proto
,
"ftp:"
)
==
0
)
return
21
;
return
-1
;
}
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_base_is_opaque
(
const
char
*
base_str
,
const
char
*
proto
)
{
const
char
*
after_colon
=
NULL
;
if
(
!
base_str
||
is_special_scheme
(
proto
))
return
false
;
after_colon
=
strchr
(
base_str
,
':'
);
if
(
!
after_colon
)
return
false
;
after_colon
++
;
return
*
after_colon
!=
'/'
&&
*
after_colon
!=
'\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_urlencode
(
const
char
*
str
)
{
if
(
!
str
)
return
strdup
(
""
);
return
form_urlencode_n
(
str
,
strlen
(
str
));
}
char
*
form_urldecode
(
const
char
*
str
)
{
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'
;
return
out
;
}
char
*
url_decode_component
(
const
char
*
str
)
{
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'
;
return
out
;
}
static
char
*
userinfo_encode
(
const
char
*
str
)
{
if
(
!
str
)
return
strdup
(
""
);
size_t
len
=
strlen
(
str
);
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
==
'~'
||
c
==
'!'
||
c
==
'$'
||
c
==
'&'
||
c
==
'\''
||
c
==
'('
||
c
==
')'
||
c
==
'*'
||
c
==
'+'
||
c
==
','
||
c
==
';'
||
c
==
'='
||
c
==
':'
)
out
[
j
++
]
=
(
char
)
c
;
else
{
snprintf
(
out
+
j
,
4
,
"%%%02X"
,
c
);
j
+=
3
;
}
}
out
[
j
]
=
'\0'
;
return
out
;
}
static
char
*
uri_range_dup
(
const
UriTextRangeA
*
r
)
{
if
(
!
r
->
first
||
!
r
->
afterLast
)
return
strdup
(
""
);
return
strndup
(
r
->
first
,
(
size_t
)(
r
->
afterLast
-
r
->
first
));
}
static
bool
url_has_brackets_in_query_or_fragment
(
const
char
*
url_str
)
{
size_t
len
=
strlen
(
url_str
);
bool
in_query
=
false
;
bool
in_fragment
=
false
;
for
(
size_t
i
=
0
;
i
<
len
;
i
++
)
{
char
c
=
url_str
[
i
];
if
(
c
==
'#'
&&
!
in_fragment
)
{
in_query
=
false
;
in_fragment
=
true
;
continue
;
}
if
(
c
==
'?'
&&
!
in_query
&&
!
in_fragment
)
{
in_query
=
true
;
continue
;
}
if
((
in_query
||
in_fragment
)
&&
(
c
==
'['
||
c
==
']'
))
return
true
;
}
return
false
;
}
static
char
*
url_escape_brackets_in_query_or_fragment
(
const
char
*
url_str
)
{
size_t
len
=
strlen
(
url_str
);
size_t
extra
=
0
;
bool
in_query
=
false
;
bool
in_fragment
=
false
;
for
(
size_t
i
=
0
;
i
<
len
;
i
++
)
{
char
c
=
url_str
[
i
];
if
(
c
==
'#'
&&
!
in_fragment
)
{
in_query
=
false
;
in_fragment
=
true
;
continue
;
}
if
(
c
==
'?'
&&
!
in_query
&&
!
in_fragment
)
{
in_query
=
true
;
continue
;
}
if
((
in_query
||
in_fragment
)
&&
(
c
==
'['
||
c
==
']'
))
extra
+=
2
;
}
char
*
escaped
=
malloc
(
len
+
extra
+
1
);
size_t
pos
=
0
;
in_query
=
false
;
in_fragment
=
false
;
if
(
!
escaped
)
return
NULL
;
for
(
size_t
i
=
0
;
i
<
len
;
i
++
)
{
char
c
=
url_str
[
i
];
if
(
c
==
'#'
&&
!
in_fragment
)
{
in_query
=
false
;
in_fragment
=
true
;
escaped
[
pos
++
]
=
c
;
continue
;
}
if
(
c
==
'?'
&&
!
in_query
&&
!
in_fragment
)
{
in_query
=
true
;
escaped
[
pos
++
]
=
c
;
continue
;
}
if
((
in_query
||
in_fragment
)
&&
c
==
'['
)
{
memcpy
(
escaped
+
pos
,
"%5B"
,
3
);
pos
+=
3
;
continue
;
}
if
((
in_query
||
in_fragment
)
&&
c
==
']'
)
{
memcpy
(
escaped
+
pos
,
"%5D"
,
3
);
pos
+=
3
;
continue
;
}
escaped
[
pos
++
]
=
c
;
}
escaped
[
pos
]
=
'\0'
;
return
escaped
;
}
static
int
url_parse_single_uri_relaxed
(
UriUriA
*
uri
,
const
char
*
url_str
,
const
char
**
errpos
,
char
**
owned_input_out
,
bool
*
used_relaxed_out
)
{
char
*
escaped
=
NULL
;
if
(
owned_input_out
)
*
owned_input_out
=
NULL
;
if
(
used_relaxed_out
)
*
used_relaxed_out
=
false
;
if
(
uriParseSingleUriA
(
uri
,
url_str
,
errpos
)
==
URI_SUCCESS
)
return
0
;
if
(
!
url_has_brackets_in_query_or_fragment
(
url_str
))
return
-1
;
escaped
=
url_escape_brackets_in_query_or_fragment
(
url_str
);
if
(
!
escaped
)
return
-1
;
if
(
owned_input_out
)
*
owned_input_out
=
escaped
;
if
(
used_relaxed_out
)
*
used_relaxed_out
=
true
;
return
uriParseSingleUriA
(
uri
,
escaped
,
errpos
)
==
URI_SUCCESS
?
0
:
-1
;
}
static
void
url_override_search_hash_from_input
(
url_state_t
*
s
,
const
char
*
url_str
)
{
const
char
*
hash
=
strchr
(
url_str
,
'#'
);
const
char
*
query
=
strchr
(
url_str
,
'?'
);
size_t
search_len
=
0
;
size_t
hash_len
=
0
;
if
(
query
&&
hash
&&
hash
<
query
)
query
=
NULL
;
if
(
query
)
{
const
char
*
search_end
=
hash
&&
hash
>
query
?
hash
:
url_str
+
strlen
(
url_str
);
search_len
=
(
size_t
)(
search_end
-
query
);
}
if
(
hash
)
hash_len
=
strlen
(
hash
);
free
(
s
->
search
);
s
->
search
=
search_len
>
0
?
strndup
(
query
,
search_len
)
:
strdup
(
""
);
free
(
s
->
hash
);
s
->
hash
=
hash_len
>
0
?
strndup
(
hash
,
hash_len
)
:
strdup
(
""
);
}
static
void
url_override_search_hash_from_reference
(
url_state_t
*
s
,
const
char
*
url_str
)
{
const
char
*
hash
=
strchr
(
url_str
,
'#'
);
const
char
*
query
=
strchr
(
url_str
,
'?'
);
size_t
search_len
=
0
;
size_t
hash_len
=
0
;
if
(
query
&&
hash
&&
hash
<
query
)
query
=
NULL
;
if
(
query
)
{
const
char
*
search_end
=
hash
&&
hash
>
query
?
hash
:
url_str
+
strlen
(
url_str
);
search_len
=
(
size_t
)(
search_end
-
query
);
free
(
s
->
search
);
s
->
search
=
strndup
(
query
,
search_len
);
}
if
(
hash
)
{
hash_len
=
strlen
(
hash
);
free
(
s
->
hash
);
s
->
hash
=
strndup
(
hash
,
hash_len
);
}
}
static
void
uri_to_state
(
const
UriUriA
*
uri
,
url_state_t
*
s
)
{
char
*
scheme
=
uri_range_dup
(
&
uri
->
scheme
);
size_t
slen
=
strlen
(
scheme
);
for
(
size_t
i
=
0
;
i
<
slen
;
i
++
)
scheme
[
i
]
=
(
char
)
tolower
((
unsigned
char
)
scheme
[
i
]);
s
->
protocol
=
malloc
(
slen
+
2
);
memcpy
(
s
->
protocol
,
scheme
,
slen
);
s
->
protocol
[
slen
]
=
':'
;
s
->
protocol
[
slen
+
1
]
=
'\0'
;
free
(
scheme
);
char
*
userinfo
=
uri_range_dup
(
&
uri
->
userInfo
);
char
*
colon
=
strchr
(
userinfo
,
':'
);
if
(
colon
)
{
*
colon
=
'\0'
;
s
->
username
=
strdup
(
userinfo
);
s
->
password
=
strdup
(
colon
+
1
);
}
else
{
s
->
username
=
strdup
(
userinfo
);
s
->
password
=
strdup
(
""
);
}
free
(
userinfo
);
s
->
hostname
=
uri_range_dup
(
&
uri
->
hostText
);
char
*
port
=
uri_range_dup
(
&
uri
->
portText
);
int
def
=
default_port_for
(
s
->
protocol
);
if
(
def
>
0
&&
*
port
&&
atoi
(
port
)
==
def
)
{
free
(
port
);
port
=
strdup
(
""
);
}
s
->
port
=
port
;
size_t
path_cap
=
2
;
for
(
UriPathSegmentA
*
seg
=
uri
->
pathHead
;
seg
;
seg
=
seg
->
next
)
path_cap
+=
(
size_t
)(
seg
->
text
.
afterLast
-
seg
->
text
.
first
)
+
1
;
char
*
path
=
malloc
(
path_cap
+
1
);
size_t
pos
=
0
;
for
(
UriPathSegmentA
*
seg
=
uri
->
pathHead
;
seg
;
seg
=
seg
->
next
)
{
path
[
pos
++
]
=
'/'
;
size_t
seglen
=
(
size_t
)(
seg
->
text
.
afterLast
-
seg
->
text
.
first
);
memcpy
(
path
+
pos
,
seg
->
text
.
first
,
seglen
);
pos
+=
seglen
;
}
if
(
pos
==
0
)
path
[
pos
++
]
=
'/'
;
path
[
pos
]
=
'\0'
;
s
->
pathname
=
path
;
char
*
query
=
uri_range_dup
(
&
uri
->
query
);
if
(
*
query
)
{
size_t
qlen
=
strlen
(
query
);
s
->
search
=
malloc
(
qlen
+
2
);
s
->
search
[
0
]
=
'?'
;
memcpy
(
s
->
search
+
1
,
query
,
qlen
+
1
);
}
else
s
->
search
=
strdup
(
""
);
free
(
query
);
char
*
frag
=
uri_range_dup
(
&
uri
->
fragment
);
if
(
*
frag
)
{
size_t
flen
=
strlen
(
frag
);
s
->
hash
=
malloc
(
flen
+
2
);
s
->
hash
[
0
]
=
'#'
;
memcpy
(
s
->
hash
+
1
,
frag
,
flen
+
1
);
}
else
s
->
hash
=
strdup
(
""
);
free
(
frag
);
}
int
parse_url_to_state
(
const
char
*
url_str
,
const
char
*
base_str
,
url_state_t
*
s
)
{
memset
(
s
,
0
,
sizeof
(
*
s
));
const
char
*
errpos
;
if
(
base_str
)
{
UriUriA
base_uri
,
ref_uri
,
resolved
;
char
*
escaped_base
=
NULL
;
char
*
escaped_ref
=
NULL
;
bool
used_relaxed_ref_parse
=
false
;
if
(
url_parse_single_uri_relaxed
(
&
base_uri
,
base_str
,
&
errpos
,
&
escaped_base
,
NULL
)
!=
0
)
{
free
(
escaped_base
);
return
-1
;
}
char
*
base_scheme
=
uri_range_dup
(
&
base_uri
.
scheme
);
size_t
bslen
=
strlen
(
base_scheme
);
for
(
size_t
i
=
0
;
i
<
bslen
;
i
++
)
base_scheme
[
i
]
=
(
char
)
tolower
((
unsigned
char
)
base_scheme
[
i
]);
char
proto_buf
[
bslen
+
2
];
memcpy
(
proto_buf
,
base_scheme
,
bslen
);
proto_buf
[
bslen
]
=
':'
;
proto_buf
[
bslen
+
1
]
=
'\0'
;
free
(
base_scheme
);
if
(
url_base_is_opaque
(
base_str
,
proto_buf
))
{
uriFreeUriMembersA
(
&
base_uri
);
free
(
escaped_base
);
return
-1
;
}
if
(
url_parse_single_uri_relaxed
(
&
ref_uri
,
url_str
,
&
errpos
,
&
escaped_ref
,
&
used_relaxed_ref_parse
)
!=
0
)
{
uriFreeUriMembersA
(
&
base_uri
);
free
(
escaped_base
);
free
(
escaped_ref
);
return
-1
;
}
if
(
uriAddBaseUriA
(
&
resolved
,
&
ref_uri
,
&
base_uri
)
!=
URI_SUCCESS
)
{
uriFreeUriMembersA
(
&
base_uri
);
uriFreeUriMembersA
(
&
ref_uri
);
free
(
escaped_base
);
free
(
escaped_ref
);
return
-1
;
}
uriNormalizeSyntaxA
(
&
resolved
);
if
(
!
resolved
.
scheme
.
first
||
resolved
.
scheme
.
first
==
resolved
.
scheme
.
afterLast
)
{
uriFreeUriMembersA
(
&
resolved
);
uriFreeUriMembersA
(
&
ref_uri
);
uriFreeUriMembersA
(
&
base_uri
);
free
(
escaped_base
);
free
(
escaped_ref
);
return
-1
;
}
uri_to_state
(
&
resolved
,
s
);
if
(
used_relaxed_ref_parse
)
url_override_search_hash_from_reference
(
s
,
url_str
);
uriFreeUriMembersA
(
&
resolved
);
uriFreeUriMembersA
(
&
ref_uri
);
uriFreeUriMembersA
(
&
base_uri
);
free
(
escaped_ref
);
free
(
escaped_base
);
return
0
;
}
UriUriA
uri
;
char
*
escaped_url
=
NULL
;
bool
used_relaxed_query_parse
=
false
;
if
(
url_parse_single_uri_relaxed
(
&
uri
,
url_str
,
&
errpos
,
&
escaped_url
,
&
used_relaxed_query_parse
)
!=
0
)
{
free
(
escaped_url
);
return
-1
;
}
if
(
!
uri
.
scheme
.
first
||
uri
.
scheme
.
first
==
uri
.
scheme
.
afterLast
)
{
uriFreeUriMembersA
(
&
uri
);
free
(
escaped_url
);
return
-1
;
}
uriNormalizeSyntaxA
(
&
uri
);
uri_to_state
(
&
uri
,
s
);
if
(
used_relaxed_query_parse
)
url_override_search_hash_from_input
(
s
,
url_str
);
uriFreeUriMembersA
(
&
uri
);
free
(
escaped_url
);
return
0
;
}
char
*
build_href
(
const
url_state_t
*
s
)
{
bool
has_authority
=
(
s
->
hostname
&&
*
s
->
hostname
)
||
(
s
->
username
&&
*
s
->
username
)
||
(
s
->
password
&&
*
s
->
password
)
||
(
s
->
port
&&
*
s
->
port
);
bool
opaque_like
=
!
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
)
{
if
(
pathname
[
0
]
==
'/'
)
pathname
++
;
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
)
==
T_STR
)
return
js_getstr
(
js
,
val
,
len
);
if
(
is_object_type
(
val
))
{
ant_value_t
href
=
js_getprop_fallback
(
js
,
val
,
"href"
);
if
(
vtype
(
href
)
==
T_STR
)
return
js_getstr
(
js
,
href
,
len
);
}
return
NULL
;
}
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
);
free
(
s
->
search
);
if
(
*
qs
)
{
size_t
qlen
=
strlen
(
qs
);
s
->
search
=
malloc
(
qlen
+
2
);
s
->
search
[
0
]
=
'?'
;
memcpy
(
s
->
search
+
1
,
qs
,
qlen
+
1
);
}
else
s
->
search
=
strdup
(
""
);
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
=
js_mkobj
(
js
);
js_set_proto_init
(
usp
,
g_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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
char
*
href
=
build_href
(
s
);
ant_value_t
ret
=
js_mkstr
(
js
,
href
,
strlen
(
href
));
free
(
href
);
return
ret
;
}
static
ant_value_t
url_get_protocol
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
return
js_mkstr
(
js
,
s
->
protocol
,
strlen
(
s
->
protocol
));
}
static
ant_value_t
url_get_username
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
return
js_mkstr
(
js
,
s
->
username
,
strlen
(
s
->
username
));
}
static
ant_value_t
url_get_password
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
return
js_mkstr
(
js
,
s
->
password
,
strlen
(
s
->
password
));
}
static
ant_value_t
url_get_host
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
return
js_mkstr
(
js
,
s
->
hostname
,
strlen
(
s
->
hostname
));
}
static
ant_value_t
url_get_port
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
return
js_mkstr
(
js
,
s
->
port
,
strlen
(
s
->
port
));
}
static
ant_value_t
url_get_pathname
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
"/"
,
1
);
return
js_mkstr
(
js
,
s
->
pathname
,
strlen
(
s
->
pathname
));
}
static
ant_value_t
url_get_search
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
return
js_mkstr
(
js
,
s
->
search
,
strlen
(
s
->
search
));
}
static
ant_value_t
url_get_hash
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
return
js_mkstr
(
js
,
s
->
hash
,
strlen
(
s
->
hash
));
}
static
ant_value_t
url_get_origin
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
||
!
is_special_scheme
(
s
->
protocol
))
return
js_mkstr
(
js
,
"null"
,
4
);
size_t
proto_len
=
strlen
(
s
->
protocol
)
-
1
;
size_t
host_len
=
strlen
(
s
->
hostname
);
size_t
port_len
=
(
s
->
port
&&
*
s
->
port
)
?
strlen
(
s
->
port
)
+
1
:
0
;
size_t
total
=
proto_len
+
3
+
host_len
+
port_len
+
1
;
char
*
origin
=
malloc
(
total
);
size_t
pos
=
0
;
memcpy
(
origin
+
pos
,
s
->
protocol
,
proto_len
);
pos
+=
proto_len
;
memcpy
(
origin
+
pos
,
"://"
,
3
);
pos
+=
3
;
memcpy
(
origin
+
pos
,
s
->
hostname
,
host_len
);
pos
+=
host_len
;
if
(
s
->
port
&&
*
s
->
port
)
{
origin
[
pos
++
]
=
':'
;
memcpy
(
origin
+
pos
,
s
->
port
,
strlen
(
s
->
port
));
pos
+=
strlen
(
s
->
port
);
}
origin
[
pos
]
=
'\0'
;
ant_value_t
ret
=
js_mkstr
(
js
,
origin
,
pos
);
free
(
origin
);
return
ret
;
}
static
ant_value_t
url_get_searchParams
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
usp
=
js_get_slot
(
js
->
this_val
,
SLOT_ENTRIES
);
if
(
vtype
(
usp
)
==
T_OBJ
)
return
usp
;
return
js_mkundef
();
}
static
ant_value_t
url_set_href
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkerr
(
js
,
"TypeError: Invalid URL"
);
url_state_t
tmp
;
if
(
parse_url_to_state
(
val
,
NULL
,
&
tmp
)
!=
0
)
return
js_mkerr
(
js
,
"TypeError: Invalid URL"
);
free
(
s
->
protocol
);
s
->
protocol
=
tmp
.
protocol
;
free
(
s
->
username
);
s
->
username
=
tmp
.
username
;
free
(
s
->
password
);
s
->
password
=
tmp
.
password
;
free
(
s
->
hostname
);
s
->
hostname
=
tmp
.
hostname
;
free
(
s
->
port
);
s
->
port
=
tmp
.
port
;
free
(
s
->
pathname
);
s
->
pathname
=
tmp
.
pathname
;
free
(
s
->
search
);
s
->
search
=
tmp
.
search
;
free
(
s
->
hash
);
s
->
hash
=
tmp
.
hash
;
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
||
!*
val
)
return
js_mkundef
();
const
char
*
colon
=
strchr
(
val
,
':'
);
size_t
slen
=
colon
?
(
size_t
)(
colon
-
val
)
:
strlen
(
val
);
if
(
!
slen
||
!
isalpha
((
unsigned
char
)
val
[
0
]))
return
js_mkundef
();
for
(
size_t
i
=
1
;
i
<
slen
;
i
++
)
{
unsigned
char
c
=
(
unsigned
char
)
val
[
i
];
if
(
!
isalnum
(
c
)
&&
c
!=
'+'
&&
c
!=
'-'
&&
c
!=
'.'
)
return
js_mkundef
();
}
free
(
s
->
protocol
);
s
->
protocol
=
malloc
(
slen
+
2
);
for
(
size_t
i
=
0
;
i
<
slen
;
i
++
)
s
->
protocol
[
i
]
=
(
char
)
tolower
((
unsigned
char
)
val
[
i
]);
s
->
protocol
[
slen
]
=
':'
;
s
->
protocol
[
slen
+
1
]
=
'\0'
;
if
(
s
->
port
&&
*
s
->
port
)
{
int
def
=
default_port_for
(
s
->
protocol
);
if
(
def
>
0
&&
atoi
(
s
->
port
)
==
def
)
{
free
(
s
->
port
);
s
->
port
=
strdup
(
""
);
}
}
return
js_mkundef
();
}
static
ant_value_t
url_set_username
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
free
(
s
->
username
);
s
->
username
=
userinfo_encode
(
val
);
return
js_mkundef
();
}
static
ant_value_t
url_set_password
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
free
(
s
->
password
);
s
->
password
=
userinfo_encode
(
val
);
return
js_mkundef
();
}
static
ant_value_t
url_set_host
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
const
char
*
colon
=
strchr
(
val
,
':'
);
if
(
colon
)
{
free
(
s
->
hostname
);
s
->
hostname
=
strndup
(
val
,
(
size_t
)(
colon
-
val
));
const
char
*
port_str
=
colon
+
1
;
free
(
s
->
port
);
if
(
*
port_str
)
{
int
p
=
atoi
(
port_str
);
int
def
=
default_port_for
(
s
->
protocol
);
s
->
port
=
(
def
>
0
&&
p
==
def
)
?
strdup
(
""
)
:
strdup
(
port_str
);
}
else
s
->
port
=
strdup
(
""
);
}
else
{
free
(
s
->
hostname
);
s
->
hostname
=
strdup
(
val
);
}
return
js_mkundef
();
}
static
ant_value_t
url_set_hostname
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
free
(
s
->
hostname
);
const
char
*
colon
=
strchr
(
val
,
':'
);
s
->
hostname
=
colon
?
strndup
(
val
,
(
size_t
)(
colon
-
val
))
:
strdup
(
val
);
return
js_mkundef
();
}
static
ant_value_t
url_set_port
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
free
(
s
->
port
);
if
(
!*
val
)
{
s
->
port
=
strdup
(
""
);
return
js_mkundef
();
}
int
p
=
atoi
(
val
);
if
(
p
<
0
||
p
>
65535
)
{
s
->
port
=
strdup
(
""
);
return
js_mkundef
();
}
int
def
=
default_port_for
(
s
->
protocol
);
if
(
def
>
0
&&
p
==
def
)
s
->
port
=
strdup
(
""
);
else
{
char
buf
[
8
];
snprintf
(
buf
,
sizeof
(
buf
),
"%d"
,
p
);
s
->
port
=
strdup
(
buf
);
}
return
js_mkundef
();
}
static
ant_value_t
url_set_pathname
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
free
(
s
->
pathname
);
if
(
is_special_scheme
(
s
->
protocol
)
&&
val
[
0
]
!=
'/'
)
{
size_t
vlen
=
strlen
(
val
);
s
->
pathname
=
malloc
(
vlen
+
2
);
s
->
pathname
[
0
]
=
'/'
;
memcpy
(
s
->
pathname
+
1
,
val
,
vlen
+
1
);
}
else
s
->
pathname
=
strdup
(
val
);
return
js_mkundef
();
}
static
ant_value_t
url_set_search
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
const
char
*
q
=
(
val
[
0
]
==
'?'
)
?
val
+
1
:
val
;
free
(
s
->
search
);
if
(
*
q
)
{
size_t
qlen
=
strlen
(
q
);
s
->
search
=
malloc
(
qlen
+
2
);
s
->
search
[
0
]
=
'?'
;
memcpy
(
s
->
search
+
1
,
q
,
qlen
+
1
);
}
else
s
->
search
=
strdup
(
""
);
url_sync_usp
(
js
,
js
->
this_val
,
*
q
?
q
:
""
);
return
js_mkundef
();
}
static
ant_value_t
url_set_hash
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkundef
();
const
char
*
val
=
js_getstr
(
js
,
args
[
0
],
NULL
);
if
(
!
val
)
return
js_mkundef
();
const
char
*
h
=
(
val
[
0
]
==
'#'
)
?
val
+
1
:
val
;
free
(
s
->
hash
);
if
(
*
h
)
{
size_t
hlen
=
strlen
(
h
);
s
->
hash
=
malloc
(
hlen
+
2
);
s
->
hash
[
0
]
=
'#'
;
memcpy
(
s
->
hash
+
1
,
h
,
hlen
+
1
);
}
else
s
->
hash
=
strdup
(
""
);
return
js_mkundef
();
}
static
ant_value_t
url_toString
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
url_state_t
*
s
=
url_get_state
(
js
->
this_val
);
if
(
!
s
)
return
js_mkstr
(
js
,
""
,
0
);
char
*
href
=
build_href
(
s
);
ant_value_t
ret
=
js_mkstr
(
js
,
href
,
strlen
(
href
));
free
(
href
);
return
ret
;
}
static
ant_value_t
js_URL
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
is_undefined
(
js
->
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
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
url_sv
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'URL': Invalid URL."
);
const
char
*
url_str
=
js_getstr
(
js
,
url_sv
,
NULL
);
if
(
!
url_str
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'URL': Invalid URL."
);
const
char
*
base_str
=
NULL
;
if
(
nargs
>
1
&&
!
is_undefined
(
args
[
1
])
&&
!
is_null
(
args
[
1
]))
base_str
=
coerce_to_string
(
js
,
args
[
1
],
NULL
);
url_state_t
*
s
=
calloc
(
1
,
sizeof
(
url_state_t
));
if
(
!
s
)
return
js_mkerr
(
js
,
"out of memory"
);
if
(
parse_url_to_state
(
url_str
,
base_str
,
s
)
!=
0
)
{
free
(
s
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'URL': Invalid URL."
);
}
ant_value_t
obj
=
js_mkobj
(
js
);
js_set_proto_init
(
obj
,
g_url_proto
);
js_set_slot
(
obj
,
SLOT_DATA
,
ANT_PTR
(
s
));
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
=
js_mkobj
(
js
);
js_set_proto_init
(
obj
,
g_url_proto
);
js_set_slot
(
obj
,
SLOT_DATA
,
ANT_PTR
(
s
));
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_false
;
ant_value_t
url_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
url_sv
))
return
js_false
;
const
char
*
url_str
=
js_getstr
(
js
,
url_sv
,
NULL
);
if
(
!
url_str
)
return
js_false
;
const
char
*
base_str
=
NULL
;
if
(
nargs
>
1
&&
!
is_undefined
(
args
[
1
])
&&
!
is_null
(
args
[
1
]))
base_str
=
coerce_to_string
(
js
,
args
[
1
],
NULL
);
url_state_t
s
;
if
(
parse_url_to_state
(
url_str
,
base_str
,
&
s
)
!=
0
)
return
js_false
;
url_state_clear
(
&
s
);
return
js_true
;
}
static
ant_value_t
url_parse
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mknull
();
ant_value_t
url_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
url_sv
))
return
js_mknull
();
const
char
*
url_str
=
js_getstr
(
js
,
url_sv
,
NULL
);
if
(
!
url_str
)
return
js_mknull
();
const
char
*
base_str
=
NULL
;
if
(
nargs
>
1
&&
!
is_undefined
(
args
[
1
])
&&
!
is_null
(
args
[
1
]))
base_str
=
coerce_to_string
(
js
,
args
[
1
],
NULL
);
url_state_t
*
s
=
calloc
(
1
,
sizeof
(
url_state_t
));
if
(
!
s
)
return
js_mknull
();
if
(
parse_url_to_state
(
url_str
,
base_str
,
s
)
!=
0
)
{
free
(
s
);
return
js_mknull
();
}
return
make_url_obj
(
js
,
s
);
}
static
ant_value_t
usp_get
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mknull
();
ant_value_t
key_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
key_sv
))
return
js_mknull
();
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
result
=
js_mkarr
(
js
);
if
(
nargs
<
1
)
return
result
;
ant_value_t
key_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
key_sv
))
return
result
;
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_false
;
ant_value_t
key_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
key_sv
))
return
js_false
;
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
])
==
T_STR
)
?
args
[
1
]
:
js_tostring_val
(
js
,
args
[
1
]);
if
(
!
is_err
(
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkundef
();
ant_value_t
key_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
key_sv
))
return
js_mkundef
();
ant_value_t
val_sv
=
(
vtype
(
args
[
1
])
==
T_STR
)
?
args
[
1
]
:
js_tostring_val
(
js
,
args
[
1
]);
if
(
is_err
(
val_sv
))
return
js_mkundef
();
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkundef
();
ant_value_t
key_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
key_sv
))
return
js_mkundef
();
ant_value_t
val_sv
=
(
vtype
(
args
[
1
])
==
T_STR
)
?
args
[
1
]
:
js_tostring_val
(
js
,
args
[
1
]);
if
(
is_err
(
val_sv
))
return
js_mkundef
();
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkundef
();
ant_value_t
key_sv
=
(
vtype
(
args
[
0
])
==
T_STR
)
?
args
[
0
]
:
js_tostring_val
(
js
,
args
[
0
]);
if
(
is_err
(
key_sv
))
return
js_mkundef
();
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
])
==
T_STR
)
?
args
[
1
]
:
js_tostring_val
(
js
,
args
[
1
]);
if
(
!
is_err
(
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
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
,
false
);
if
(
is_err
(
r
))
return
r
;
}
return
js_mkundef
();
}
static
ant_value_t
usp_size_get
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state_v
=
js_get_slot
(
js
->
this_val
,
SLOT_ITER_STATE
);
if
(
vtype
(
state_v
)
!=
T_NUM
)
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
=
js_mkobj
(
js
);
js_set_proto_init
(
iter
,
g_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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
make_usp_iter
(
js
,
js
->
this_val
,
USP_ITER_ENTRIES
);
}
static
ant_value_t
usp_keys_fn
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
make_usp_iter
(
js
,
js
->
this_val
,
USP_ITER_KEYS
);
}
static
ant_value_t
usp_values_fn
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
make_usp_iter
(
js
,
js
->
this_val
,
USP_ITER_VALUES
);
}
static
ant_value_t
js_URLSearchParams
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
is_undefined
(
js
->
new_target
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'URLSearchParams': Please use the 'new' operator."
);
ant_value_t
obj
=
js_mkobj
(
js
);
js_set_proto_init
(
obj
,
g_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
==
T_STR
)
{
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
;
}
if
(
t
==
T_ARR
)
{
ant_offset_t
len
=
js_arr_len
(
js
,
init
);
for
(
ant_offset_t
i
=
0
;
i
<
len
;
i
++
)
{
ant_value_t
pair
=
js_arr_get
(
js
,
init
,
i
);
if
(
vtype
(
pair
)
!=
T_ARR
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Failed to construct 'URLSearchParams': Each element must be an array."
);
ant_offset_t
plen
=
js_arr_len
(
js
,
pair
);
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
=
js_arr_get
(
js
,
pair
,
0
);
ant_value_t
pv
=
js_arr_get
(
js
,
pair
,
1
);
ant_value_t
ksv
=
(
vtype
(
pk
)
==
T_STR
)
?
pk
:
js_tostring_val
(
js
,
pk
);
if
(
is_err
(
ksv
))
return
ksv
;
ant_value_t
vsv
=
(
vtype
(
pv
)
==
T_STR
)
?
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
)
==
T_ARR
)
{
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
)
==
T_STR
)
?
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
(
void
)
{
ant_t
*
js
=
rt
->
js
;
ant_value_t
glob
=
js
->
global
;
g_usp_iter_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
g_usp_iter_proto
,
js
->
sym
.
iterator_proto
);
js_set
(
js
,
g_usp_iter_proto
,
"next"
,
js_mkfun
(
usp_iter_next
));
js_set_descriptor
(
js
,
g_usp_iter_proto
,
"next"
,
4
,
JS_DESC_W
|
JS_DESC_E
|
JS_DESC_C
);
js_set_sym
(
js
,
g_usp_iter_proto
,
get_iterator_sym
(),
js_mkfun
(
sym_this_cb
));
g_usp_proto
=
js_mkobj
(
js
);
js_set
(
js
,
g_usp_proto
,
"get"
,
js_mkfun
(
usp_get
));
js_set
(
js
,
g_usp_proto
,
"getAll"
,
js_mkfun
(
usp_getAll
));
js_set
(
js
,
g_usp_proto
,
"has"
,
js_mkfun
(
usp_has
));
js_set
(
js
,
g_usp_proto
,
"set"
,
js_mkfun
(
usp_set
));
js_set
(
js
,
g_usp_proto
,
"append"
,
js_mkfun
(
usp_append
));
js_set
(
js
,
g_usp_proto
,
"delete"
,
js_mkfun
(
usp_delete
));
js_set
(
js
,
g_usp_proto
,
"sort"
,
js_mkfun
(
usp_sort
));
js_set
(
js
,
g_usp_proto
,
"toString"
,
js_mkfun
(
usp_toString
));
js_set
(
js
,
g_usp_proto
,
"forEach"
,
js_mkfun
(
usp_forEach
));
js_set_getter_desc
(
js
,
g_usp_proto
,
"size"
,
4
,
js_mkfun
(
usp_size_get
),
JS_DESC_C
);
js_set
(
js
,
g_usp_proto
,
"entries"
,
js_mkfun
(
usp_entries_fn
));
js_set
(
js
,
g_usp_proto
,
"keys"
,
js_mkfun
(
usp_keys_fn
));
js_set
(
js
,
g_usp_proto
,
"values"
,
js_mkfun
(
usp_values_fn
));
js_set_sym
(
js
,
g_usp_proto
,
get_iterator_sym
(),
js_get
(
js
,
g_usp_proto
,
"entries"
));
js_set_sym
(
js
,
g_usp_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"URLSearchParams"
,
15
));
ant_value_t
usp_ctor
=
js_make_ctor
(
js
,
js_URLSearchParams
,
g_usp_proto
,
"URLSearchParams"
,
15
);
js_set
(
js
,
glob
,
"URLSearchParams"
,
usp_ctor
);
g_url_proto
=
js_mkobj
(
js
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"href"
,
4
,
js_mkfun
(
url_get_href
),
js_mkfun
(
url_set_href
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"protocol"
,
8
,
js_mkfun
(
url_get_protocol
),
js_mkfun
(
url_set_protocol
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"username"
,
8
,
js_mkfun
(
url_get_username
),
js_mkfun
(
url_set_username
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"password"
,
8
,
js_mkfun
(
url_get_password
),
js_mkfun
(
url_set_password
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"host"
,
4
,
js_mkfun
(
url_get_host
),
js_mkfun
(
url_set_host
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"hostname"
,
8
,
js_mkfun
(
url_get_hostname
),
js_mkfun
(
url_set_hostname
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"port"
,
4
,
js_mkfun
(
url_get_port
),
js_mkfun
(
url_set_port
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"pathname"
,
8
,
js_mkfun
(
url_get_pathname
),
js_mkfun
(
url_set_pathname
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"search"
,
6
,
js_mkfun
(
url_get_search
),
js_mkfun
(
url_set_search
),
JS_DESC_C
);
js_set_accessor_desc
(
js
,
g_url_proto
,
"hash"
,
4
,
js_mkfun
(
url_get_hash
),
js_mkfun
(
url_set_hash
),
JS_DESC_C
);
js_set_getter_desc
(
js
,
g_url_proto
,
"origin"
,
6
,
js_mkfun
(
url_get_origin
),
JS_DESC_C
);
js_set_getter_desc
(
js
,
g_url_proto
,
"searchParams"
,
12
,
js_mkfun
(
url_get_searchParams
),
JS_DESC_C
);
js_set
(
js
,
g_url_proto
,
"toString"
,
js_mkfun
(
url_toString
));
js_set
(
js
,
g_url_proto
,
"toJSON"
,
js_mkfun
(
url_toString
));
js_set_sym
(
js
,
g_url_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"URL"
,
3
));
ant_value_t
url_ctor
=
js_make_ctor
(
js
,
js_URL
,
g_url_proto
,
"URL"
,
3
);
js_set
(
js
,
url_ctor
,
"canParse"
,
js_mkfun
(
url_canParse
));
js_set
(
js
,
url_ctor
,
"parse"
,
js_mkfun
(
url_parse
));
js_set
(
js
,
glob
,
"URL"
,
url_ctor
);
}
static
ant_value_t
builtin_fileURLToPath
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
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_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
vtype
(
args
[
0
])
!=
T_STR
)
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
);
}
typedef
struct
{
char
*
buf
;
size_t
len
;
size_t
cap
;
}
url_fmt_buf_t
;
static
bool
url_fmt_reserve
(
url_fmt_buf_t
*
b
,
size_t
extra
)
{
if
(
extra
<=
b
->
cap
-
b
->
len
)
return
true
;
size_t
needed
=
b
->
len
+
extra
+
1
;
size_t
next
=
b
->
cap
?
b
->
cap
:
128
;
while
(
next
<
needed
)
next
*=
2
;
char
*
buf
=
realloc
(
b
->
buf
,
next
);
if
(
!
buf
)
return
false
;
b
->
buf
=
buf
;
b
->
cap
=
next
;
return
true
;
}
static
bool
url_fmt_append_n
(
url_fmt_buf_t
*
b
,
const
char
*
s
,
size_t
n
)
{
if
(
!
s
||
n
==
0
)
return
true
;
if
(
!
url_fmt_reserve
(
b
,
n
))
return
false
;
memcpy
(
b
->
buf
+
b
->
len
,
s
,
n
);
b
->
len
+=
n
;
b
->
buf
[
b
->
len
]
=
'\0'
;
return
true
;
}
static
bool
url_fmt_append
(
url_fmt_buf_t
*
b
,
const
char
*
s
)
{
return
url_fmt_append_n
(
b
,
s
,
s
?
strlen
(
s
)
:
0
);
}
static
bool
url_fmt_append_c
(
url_fmt_buf_t
*
b
,
char
c
)
{
if
(
!
url_fmt_reserve
(
b
,
1
))
return
false
;
b
->
buf
[
b
->
len
++
]
=
c
;
b
->
buf
[
b
->
len
]
=
'\0'
;
return
true
;
}
static
bool
url_fmt_append_value_string
(
ant_t
*
js
,
url_fmt_buf_t
*
b
,
ant_value_t
value
)
{
ant_value_t
str_val
=
vtype
(
value
)
==
T_STR
?
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_n
(
b
,
str
,
len
);
}
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_undefined
(
*
out
)
||
is_null
(
*
out
))
return
false
;
ant_value_t
str_val
=
vtype
(
*
out
)
==
T_STR
?
*
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_object
(
ant_t
*
js
,
url_fmt_buf_t
*
b
,
ant_value_t
query
)
{
if
(
!
is_special_object
(
query
))
return
true
;
bool
first
=
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
))
{
if
(
!
first
&&
!
url_fmt_append_c
(
b
,
'&'
))
{
js_prop_iter_end
(
&
it
);
return
false
;
}
first
=
false
;
if
(
!
url_fmt_append_query_component
(
b
,
key
,
key_len
))
{
js_prop_iter_end
(
&
it
);
return
false
;
}
if
(
!
url_fmt_append_c
(
b
,
'='
))
{
js_prop_iter_end
(
&
it
);
return
false
;
}
ant_value_t
str_val
=
vtype
(
val
)
==
T_STR
?
val
:
js_tostring_val
(
js
,
val
);
if
(
is_err
(
str_val
))
{
js_prop_iter_end
(
&
it
);
return
false
;
}
size_t
val_len
=
0
;
const
char
*
val_str
=
js_getstr
(
js
,
str_val
,
&
val_len
);
if
(
!
val_str
||
!
url_fmt_append_query_component
(
b
,
val_str
,
val_len
))
{
js_prop_iter_end
(
&
it
);
return
false
;
}
}
js_prop_iter_end
(
&
it
);
return
true
;
}
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_url_format
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
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
);
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
;
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
);
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"
);
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
(
vtype
(
query
)
==
T_STR
)
{
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
);
return
js_mkerr
(
js
,
"allocation failure"
);
}
ant_value_t
url_library
(
ant_t
*
js
)
{
ant_value_t
lib
=
js_mkobj
(
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"
,
js_mkfun
(
builtin_fileURLToPath
));
js_set
(
js
,
lib
,
"pathToFileURL"
,
js_mkfun
(
builtin_pathToFileURL
));
js_set
(
js
,
lib
,
"parse"
,
js_mkfun
(
url_parse
));
js_set
(
js
,
lib
,
"format"
,
js_mkfun
(
builtin_url_format
));
js_set
(
js
,
lib
,
"default"
,
lib
);
return
lib
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 1:59 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
543166
Default Alt Text
url.c (57 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment