Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4440119
collections.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
56 KB
Referenced Files
None
Subscribers
None
collections.c
View Options
#include
<stdlib.h>
#include
<string.h>
#include
<stdio.h>
#include
<math.h>
#include
"ant.h"
#include
"gc.h"
#include
"errors.h"
#include
"runtime.h"
#include
"internal.h"
#include
"silver/engine.h"
#include
"descriptors.h"
#include
"modules/bigint.h"
#include
"modules/collections.h"
#include
"modules/symbol.h"
ant_value_t
g_map_iter_proto
=
0
;
ant_value_t
g_set_iter_proto
=
0
;
typedef
struct
{
unsigned
char
stack
[
32
];
unsigned
char
*
bytes
;
size_t
len
;
}
collection_key_t
;
static
ant_value_t
normalize_map_key
(
ant_value_t
key
)
{
if
(
vtype
(
key
)
==
T_NUM
)
{
double
d
=
tod
(
key
);
if
(
d
==
0.0
&&
signbit
(
d
))
return
js_mknum
(
0.0
);
}
return
key
;
}
static
void
collection_key_reset
(
collection_key_t
*
key
)
{
key
->
bytes
=
key
->
stack
;
key
->
len
=
0
;
}
static
void
collection_key_free
(
collection_key_t
*
key
)
{
if
(
key
->
bytes
!=
key
->
stack
)
free
(
key
->
bytes
);
collection_key_reset
(
key
);
}
static
bool
collection_key_reserve
(
collection_key_t
*
key
,
size_t
len
)
{
if
(
len
<=
sizeof
(
key
->
stack
))
return
true
;
unsigned
char
*
heap
=
malloc
(
len
);
if
(
!
heap
)
return
false
;
key
->
bytes
=
heap
;
return
true
;
}
static
bool
collection_key_init
(
ant_t
*
js
,
ant_value_t
input
,
collection_key_t
*
out
)
{
collection_key_reset
(
out
);
ant_value_t
key
=
normalize_map_key
(
input
);
uint8_t
tag
=
(
uint8_t
)
vtype
(
key
);
if
(
vtype
(
key
)
==
T_STR
)
{
size_t
str_len
=
0
;
const
char
*
str
=
js_getstr
(
js
,
key
,
&
str_len
);
out
->
len
=
1
+
str_len
;
if
(
!
collection_key_reserve
(
out
,
out
->
len
))
return
false
;
out
->
bytes
[
0
]
=
tag
;
if
(
str_len
>
0
)
memcpy
(
out
->
bytes
+
1
,
str
,
str_len
);
return
true
;
}
if
(
vtype
(
key
)
==
T_BIGINT
)
{
size_t
str_len
=
bigint_digits_len
(
js
,
key
)
+
(
bigint_is_negative
(
js
,
key
)
?
1
:
0
);
out
->
len
=
1
+
str_len
;
if
(
!
collection_key_reserve
(
out
,
out
->
len
))
return
false
;
out
->
bytes
[
0
]
=
tag
;
if
(
str_len
>
0
)
strbigint
(
js
,
key
,
(
char
*
)(
out
->
bytes
+
1
),
str_len
+
1
);
return
true
;
}
out
->
len
=
1
+
sizeof
(
ant_value_t
);
if
(
!
collection_key_reserve
(
out
,
out
->
len
))
return
false
;
out
->
bytes
[
0
]
=
tag
;
memcpy
(
out
->
bytes
+
1
,
&
key
,
sizeof
(
ant_value_t
));
return
true
;
}
static
map_entry_t
*
map_find_entry
(
ant_t
*
js
,
map_entry_t
**
map_ptr
,
ant_value_t
key_val
)
{
collection_key_t
key
;
if
(
!
collection_key_init
(
js
,
key_val
,
&
key
))
return
NULL
;
map_entry_t
*
entry
=
NULL
;
HASH_FIND
(
hh
,
*
map_ptr
,
key
.
bytes
,
key
.
len
,
entry
);
collection_key_free
(
&
key
);
return
entry
;
}
static
set_entry_t
*
set_find_entry
(
ant_t
*
js
,
set_entry_t
**
set_ptr
,
ant_value_t
value
)
{
collection_key_t
key
;
if
(
!
collection_key_init
(
js
,
value
,
&
key
))
return
NULL
;
set_entry_t
*
entry
=
NULL
;
HASH_FIND
(
hh
,
*
set_ptr
,
key
.
bytes
,
key
.
len
,
entry
);
collection_key_free
(
&
key
);
return
entry
;
}
static
bool
map_store_entry
(
ant_t
*
js
,
map_entry_t
**
map_ptr
,
ant_value_t
raw_key
,
ant_value_t
key_val
,
ant_value_t
value
)
{
collection_key_t
key
;
if
(
!
collection_key_init
(
js
,
raw_key
,
&
key
))
return
false
;
map_entry_t
*
entry
=
NULL
;
HASH_FIND
(
hh
,
*
map_ptr
,
key
.
bytes
,
key
.
len
,
entry
);
if
(
entry
)
{
entry
->
key_val
=
key_val
;
entry
->
value
=
value
;
collection_key_free
(
&
key
);
return
true
;
}
entry
=
ant_calloc
(
sizeof
(
map_entry_t
));
if
(
!
entry
)
{
collection_key_free
(
&
key
);
return
false
;
}
entry
->
key
=
malloc
(
key
.
len
);
if
(
!
entry
->
key
)
{
collection_key_free
(
&
key
);
free
(
entry
);
return
false
;
}
memcpy
(
entry
->
key
,
key
.
bytes
,
key
.
len
);
entry
->
key_len
=
key
.
len
;
entry
->
key_val
=
key_val
;
entry
->
value
=
value
;
HASH_ADD_KEYPTR
(
hh
,
*
map_ptr
,
entry
->
key
,
entry
->
key_len
,
entry
);
collection_key_free
(
&
key
);
return
true
;
}
static
bool
set_store_entry
(
ant_t
*
js
,
set_entry_t
**
set_ptr
,
ant_value_t
value
)
{
collection_key_t
key
;
if
(
!
collection_key_init
(
js
,
value
,
&
key
))
return
false
;
set_entry_t
*
entry
=
NULL
;
HASH_FIND
(
hh
,
*
set_ptr
,
key
.
bytes
,
key
.
len
,
entry
);
if
(
entry
)
{
collection_key_free
(
&
key
);
return
true
;
}
entry
=
ant_calloc
(
sizeof
(
set_entry_t
));
if
(
!
entry
)
{
collection_key_free
(
&
key
);
return
false
;
}
entry
->
key
=
malloc
(
key
.
len
);
if
(
!
entry
->
key
)
{
collection_key_free
(
&
key
);
free
(
entry
);
return
false
;
}
memcpy
(
entry
->
key
,
key
.
bytes
,
key
.
len
);
entry
->
key_len
=
key
.
len
;
entry
->
value
=
value
;
HASH_ADD_KEYPTR
(
hh
,
*
set_ptr
,
entry
->
key
,
entry
->
key_len
,
entry
);
collection_key_free
(
&
key
);
return
true
;
}
static
ant_value_t
map_init_from_iterable
(
ant_t
*
js
,
map_entry_t
**
map_head
,
ant_value_t
iterable
)
{
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
iterable
,
&
it
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Map constructor argument is not iterable"
);
}
ant_value_t
entry
;
while
(
js_iter_next
(
js
,
&
it
,
&
entry
))
{
uint8_t
entry_t
=
vtype
(
entry
);
if
(
entry_t
!=
T_ARR
&&
entry_t
!=
T_OBJ
)
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Map iterable entries must be pair sequences"
);
}
ant_offset_t
entry_len
=
js_arr_len
(
js
,
entry
);
if
(
entry_len
<
2
)
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Map iterable entries must have at least 2 items"
);
}
ant_value_t
key
=
normalize_map_key
(
js_arr_get
(
js
,
entry
,
0
));
ant_value_t
value
=
js_arr_get
(
js
,
entry
,
1
);
if
(
!
map_store_entry
(
js
,
map_head
,
key
,
key
,
value
))
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr
(
js
,
"out of memory"
);
}}
return
js_mkundef
();
}
static
ant_value_t
set_init_from_iterable
(
ant_t
*
js
,
set_entry_t
**
set_head
,
ant_value_t
iterable
)
{
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
iterable
,
&
it
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set constructor argument is not iterable"
);
}
ant_value_t
value
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
if
(
!
set_store_entry
(
js
,
set_head
,
value
))
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr
(
js
,
"out of memory"
);
}
return
js_mkundef
();
}
static
ant_value_t
weakmap_init_from_iterable
(
ant_t
*
js
,
weakmap_entry_t
**
wm_head
,
ant_value_t
iterable
)
{
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
iterable
,
&
it
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakMap constructor argument is not iterable"
);
}
ant_value_t
entry
;
while
(
js_iter_next
(
js
,
&
it
,
&
entry
))
{
uint8_t
entry_t
=
vtype
(
entry
);
if
(
entry_t
!=
T_ARR
&&
entry_t
!=
T_OBJ
)
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakMap iterable entries must be pair sequences"
);
}
ant_offset_t
entry_len
=
js_arr_len
(
js
,
entry
);
if
(
entry_len
<
2
)
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakMap iterable entries must have at least 2 items"
);
}
ant_value_t
key
=
js_arr_get
(
js
,
entry
,
0
);
ant_value_t
value
=
js_arr_get
(
js
,
entry
,
1
);
if
(
!
is_object_type
(
key
))
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr
(
js
,
"WeakMap key must be an object"
);
}
weakmap_entry_t
*
wm_entry
;
HASH_FIND
(
hh
,
*
wm_head
,
&
key
,
sizeof
(
ant_value_t
),
wm_entry
);
if
(
wm_entry
)
{
wm_entry
->
value
=
value
;
continue
;
}
wm_entry
=
ant_calloc
(
sizeof
(
weakmap_entry_t
));
if
(
!
wm_entry
)
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr
(
js
,
"out of memory"
);
}
wm_entry
->
key_obj
=
key
;
wm_entry
->
value
=
value
;
HASH_ADD
(
hh
,
*
wm_head
,
key_obj
,
sizeof
(
ant_value_t
),
wm_entry
);
}
return
js_mkundef
();
}
static
ant_value_t
weakset_init_from_iterable
(
ant_t
*
js
,
weakset_entry_t
**
ws_head
,
ant_value_t
iterable
)
{
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
iterable
,
&
it
))
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakSet constructor argument is not iterable"
);
}
ant_value_t
value
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
{
if
(
!
is_object_type
(
value
))
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr
(
js
,
"WeakSet value must be an object"
);
}
weakset_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
ws_head
,
&
value
,
sizeof
(
ant_value_t
),
entry
);
if
(
entry
)
continue
;
entry
=
ant_calloc
(
sizeof
(
weakset_entry_t
));
if
(
!
entry
)
{
js_iter_close
(
js
,
&
it
);
return
js_mkerr
(
js
,
"out of memory"
);
}
entry
->
value_obj
=
value
;
HASH_ADD
(
hh
,
*
ws_head
,
value_obj
,
sizeof
(
ant_value_t
),
entry
);
}
return
js_mkundef
();
}
map_entry_t
**
get_map_from_obj
(
ant_value_t
obj
)
{
ant_object_t
*
ptr
=
js_obj_ptr
(
obj
);
if
(
!
ptr
||
ptr
->
type_tag
!=
T_MAP
)
return
NULL
;
return
(
map_entry_t
**
)(
uintptr_t
)
js_getnum
(
ptr
->
u
.
data
.
value
);
}
set_entry_t
**
get_set_from_obj
(
ant_value_t
obj
)
{
ant_object_t
*
ptr
=
js_obj_ptr
(
obj
);
if
(
!
ptr
||
ptr
->
type_tag
!=
T_SET
)
return
NULL
;
return
(
set_entry_t
**
)(
uintptr_t
)
js_getnum
(
ptr
->
u
.
data
.
value
);
}
static
weakmap_entry_t
**
get_weakmap_from_obj
(
ant_value_t
obj
)
{
ant_object_t
*
ptr
=
js_obj_ptr
(
obj
);
if
(
!
ptr
||
ptr
->
type_tag
!=
T_WEAKMAP
)
return
NULL
;
return
(
weakmap_entry_t
**
)(
uintptr_t
)
js_getnum
(
ptr
->
u
.
data
.
value
);
}
static
weakset_entry_t
**
get_weakset_from_obj
(
ant_value_t
obj
)
{
ant_object_t
*
ptr
=
js_obj_ptr
(
obj
);
if
(
!
ptr
||
ptr
->
type_tag
!=
T_WEAKSET
)
return
NULL
;
return
(
weakset_entry_t
**
)(
uintptr_t
)
js_getnum
(
ptr
->
u
.
data
.
value
);
}
static
map_iterator_state_t
*
get_map_iter_state
(
ant_value_t
obj
)
{
ant_value_t
state_val
=
js_get_slot
(
obj
,
SLOT_ITER_STATE
);
if
(
vtype
(
state_val
)
==
T_UNDEF
)
return
NULL
;
return
(
map_iterator_state_t
*
)(
uintptr_t
)
js_getnum
(
state_val
);
}
static
set_iterator_state_t
*
get_set_iter_state
(
ant_value_t
obj
)
{
ant_value_t
state_val
=
js_get_slot
(
obj
,
SLOT_ITER_STATE
);
if
(
vtype
(
state_val
)
==
T_UNDEF
)
return
NULL
;
return
(
set_iterator_state_t
*
)(
uintptr_t
)
js_getnum
(
state_val
);
}
static
ant_value_t
map_set
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"Map.set() requires 2 arguments"
);
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
!
map_ptr
)
return
js_mkerr
(
js
,
"Invalid Map object"
);
ant_value_t
key_val
=
normalize_map_key
(
args
[
0
]);
if
(
!
map_store_entry
(
js
,
map_ptr
,
args
[
0
],
key_val
,
args
[
1
]))
return
js_mkerr
(
js
,
"out of memory"
);
ant_object_t
*
map_obj
=
js_obj_ptr
(
this_val
);
if
(
map_obj
)
{
gc_write_barrier
(
js
,
map_obj
,
key_val
);
gc_write_barrier
(
js
,
map_obj
,
args
[
1
]);
}
return
this_val
;
}
static
ant_value_t
map_get
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"Map.get() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
!
map_ptr
)
return
js_mkundef
();
map_entry_t
*
entry
=
map_find_entry
(
js
,
map_ptr
,
args
[
0
]);
return
entry
?
entry
->
value
:
js_mkundef
();
}
static
ant_value_t
map_has
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"Map.has() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
!
map_ptr
)
return
js_false
;
map_entry_t
*
entry
=
map_find_entry
(
js
,
map_ptr
,
args
[
0
]);
return
js_bool
(
entry
!=
NULL
);
}
static
ant_value_t
map_upsert
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
3
)
return
js_mkerr
(
js
,
"Map.upsert() requires 3 arguments"
);
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
!
map_ptr
)
return
js_mkerr
(
js
,
"Invalid Map object"
);
ant_value_t
update_fn
=
args
[
1
];
ant_value_t
insert_fn
=
args
[
2
];
if
(
!
is_callable
(
update_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Map.upsert update callback must be callable"
);
if
(
!
is_callable
(
insert_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Map.upsert insert callback must be callable"
);
map_entry_t
*
entry
=
map_find_entry
(
js
,
map_ptr
,
args
[
0
]);
ant_value_t
value
;
if
(
entry
)
{
ant_value_t
call_args
[
3
]
=
{
entry
->
value
,
args
[
0
],
this_val
};
value
=
sv_vm_call
(
js
->
vm
,
js
,
update_fn
,
js_mkundef
(),
call_args
,
3
,
NULL
,
false
);
}
else
{
ant_value_t
call_args
[
2
]
=
{
args
[
0
],
this_val
};
value
=
sv_vm_call
(
js
->
vm
,
js
,
insert_fn
,
js_mkundef
(),
call_args
,
2
,
NULL
,
false
);
}
if
(
is_err
(
value
))
return
value
;
ant_value_t
key_val
=
normalize_map_key
(
args
[
0
]);
if
(
!
map_store_entry
(
js
,
map_ptr
,
args
[
0
],
key_val
,
value
))
return
js_mkerr
(
js
,
"out of memory"
);
ant_object_t
*
map_obj
=
js_obj_ptr
(
this_val
);
if
(
map_obj
)
{
gc_write_barrier
(
js
,
map_obj
,
key_val
);
gc_write_barrier
(
js
,
map_obj
,
value
);
}
return
value
;
}
static
ant_value_t
map_delete
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"Map.delete() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
!
map_ptr
)
return
js_false
;
map_entry_t
*
entry
=
map_find_entry
(
js
,
map_ptr
,
args
[
0
]);
if
(
entry
)
{
HASH_DEL
(
*
map_ptr
,
entry
);
free
(
entry
->
key
);
free
(
entry
);
return
js_true
;
}
return
js_false
;
}
static
ant_value_t
map_clear
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
!
map_ptr
)
return
js_mkundef
();
map_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
map_ptr
,
entry
,
tmp
)
{
HASH_DEL
(
*
map_ptr
,
entry
);
free
(
entry
->
key
);
free
(
entry
);
}
*
map_ptr
=
NULL
;
return
js_mkundef
();
}
static
ant_value_t
map_size
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
!
map_ptr
)
return
js_mknum
(
0
);
return
js_mknum
((
double
)
HASH_COUNT
(
*
map_ptr
));
}
static
ant_value_t
map_forEach
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_val
=
js
->
this_val
;
map_entry_t
**
map_ptr
=
get_map_from_obj
(
this_val
);
if
(
nargs
<
1
||
vtype
(
args
[
0
])
!=
T_FUNC
)
return
js_mkerr
(
js
,
"forEach requires a callback function"
);
ant_value_t
callback
=
args
[
0
];
if
(
map_ptr
&&
*
map_ptr
)
{
map_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
map_ptr
,
entry
,
tmp
)
{
ant_value_t
k
=
entry
->
key_val
;
ant_value_t
call_args
[
3
]
=
{
entry
->
value
,
k
,
this_val
};
ant_value_t
result
=
sv_vm_call
(
js
->
vm
,
js
,
callback
,
js_mkundef
(),
call_args
,
3
,
NULL
,
false
);
if
(
is_err
(
result
))
return
result
;
}}
return
js_mkundef
();
}
bool
advance_map
(
ant_t
*
js
,
js_iter_t
*
it
,
ant_value_t
*
out
)
{
map_iterator_state_t
*
state
=
get_map_iter_state
(
it
->
iterator
);
if
(
!
state
||
!
state
->
current
)
return
false
;
map_entry_t
*
entry
=
state
->
current
;
switch
(
state
->
type
)
{
case
ITER_TYPE_MAP_VALUES
:
*
out
=
entry
->
value
;
break
;
case
ITER_TYPE_MAP_KEYS
:
*
out
=
entry
->
key_val
;
break
;
case
ITER_TYPE_MAP_ENTRIES
:
{
ant_value_t
pair
=
js_mkarr
(
js
);
js_arr_push
(
js
,
pair
,
entry
->
key_val
);
js_arr_push
(
js
,
pair
,
entry
->
value
);
*
out
=
pair
;
break
;
}
default
:
*
out
=
js_mkundef
();
}
state
->
current
=
entry
->
hh
.
next
;
return
true
;
}
static
ant_value_t
map_iter_next
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
js_iter_t
it
=
{
.
iterator
=
js
->
this_val
};
ant_value_t
value
;
return
js_iter_result
(
js
,
advance_map
(
js
,
&
it
,
&
value
),
value
);
}
static
ant_value_t
create_map_iterator
(
ant_t
*
js
,
ant_value_t
map_obj
,
iter_type_t
type
)
{
map_entry_t
**
map_ptr
=
get_map_from_obj
(
map_obj
);
map_iterator_state_t
*
state
=
ant_calloc
(
sizeof
(
map_iterator_state_t
));
if
(
!
state
)
return
js_mkerr
(
js
,
"out of memory"
);
state
->
head
=
map_ptr
;
state
->
current
=
map_ptr
?
*
map_ptr
:
NULL
;
state
->
type
=
type
;
ant_value_t
iter
=
js_mkobj
(
js
);
js_set_proto_init
(
iter
,
g_map_iter_proto
);
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
ANT_PTR
(
state
));
return
iter
;
}
static
ant_value_t
map_values
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
return
create_map_iterator
(
js
,
js
->
this_val
,
ITER_TYPE_MAP_VALUES
);
}
static
ant_value_t
map_keys
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
return
create_map_iterator
(
js
,
js
->
this_val
,
ITER_TYPE_MAP_KEYS
);
}
static
ant_value_t
map_entries
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
return
create_map_iterator
(
js
,
js
->
this_val
,
ITER_TYPE_MAP_ENTRIES
);
}
bool
advance_set
(
ant_t
*
js
,
js_iter_t
*
it
,
ant_value_t
*
out
)
{
set_iterator_state_t
*
state
=
get_set_iter_state
(
it
->
iterator
);
if
(
!
state
||
!
state
->
current
)
return
false
;
set_entry_t
*
entry
=
state
->
current
;
if
(
state
->
type
==
ITER_TYPE_SET_ENTRIES
)
{
ant_value_t
pair
=
js_mkarr
(
js
);
js_arr_push
(
js
,
pair
,
entry
->
value
);
js_arr_push
(
js
,
pair
,
entry
->
value
);
*
out
=
pair
;
}
else
*
out
=
entry
->
value
;
state
->
current
=
entry
->
hh
.
next
;
return
true
;
}
static
ant_value_t
set_iter_next
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
js_iter_t
it
=
{
.
iterator
=
js
->
this_val
};
ant_value_t
value
;
return
js_iter_result
(
js
,
advance_set
(
js
,
&
it
,
&
value
),
value
);
}
static
ant_value_t
create_set_iterator
(
ant_t
*
js
,
ant_value_t
set_obj
,
iter_type_t
type
)
{
set_entry_t
**
set_ptr
=
get_set_from_obj
(
set_obj
);
set_iterator_state_t
*
state
=
ant_calloc
(
sizeof
(
set_iterator_state_t
));
if
(
!
state
)
return
js_mkerr
(
js
,
"out of memory"
);
state
->
head
=
set_ptr
;
state
->
current
=
set_ptr
?
*
set_ptr
:
NULL
;
state
->
type
=
type
;
ant_value_t
iter
=
js_mkobj
(
js
);
js_set_proto_init
(
iter
,
g_set_iter_proto
);
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
ANT_PTR
(
state
));
return
iter
;
}
static
ant_value_t
set_add
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"Set.add() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
set_entry_t
**
set_ptr
=
get_set_from_obj
(
this_val
);
if
(
!
set_ptr
)
return
js_mkerr
(
js
,
"Invalid Set object"
);
if
(
!
set_store_entry
(
js
,
set_ptr
,
args
[
0
]))
return
js_mkerr
(
js
,
"out of memory"
);
ant_object_t
*
set_obj
=
js_obj_ptr
(
this_val
);
if
(
set_obj
)
gc_write_barrier
(
js
,
set_obj
,
args
[
0
]);
return
this_val
;
}
static
ant_value_t
set_has
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"Set.has() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
set_entry_t
**
set_ptr
=
get_set_from_obj
(
this_val
);
if
(
!
set_ptr
)
return
js_false
;
set_entry_t
*
entry
=
set_find_entry
(
js
,
set_ptr
,
args
[
0
]);
return
js_bool
(
entry
!=
NULL
);
}
static
ant_value_t
set_delete
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"Set.delete() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
set_entry_t
**
set_ptr
=
get_set_from_obj
(
this_val
);
if
(
!
set_ptr
)
return
js_false
;
set_entry_t
*
entry
=
set_find_entry
(
js
,
set_ptr
,
args
[
0
]);
if
(
entry
)
{
HASH_DEL
(
*
set_ptr
,
entry
);
free
(
entry
->
key
);
free
(
entry
);
return
js_true
;
}
return
js_false
;
}
static
ant_value_t
set_clear
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
ant_value_t
this_val
=
js
->
this_val
;
set_entry_t
**
set_ptr
=
get_set_from_obj
(
this_val
);
if
(
!
set_ptr
)
return
js_mkundef
();
set_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
set_ptr
,
entry
,
tmp
)
{
HASH_DEL
(
*
set_ptr
,
entry
);
free
(
entry
->
key
);
free
(
entry
);
}
*
set_ptr
=
NULL
;
return
js_mkundef
();
}
static
ant_value_t
set_size
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
ant_value_t
this_val
=
js
->
this_val
;
set_entry_t
**
set_ptr
=
get_set_from_obj
(
this_val
);
if
(
!
set_ptr
)
return
js_mknum
(
0
);
return
js_mknum
((
double
)
HASH_COUNT
(
*
set_ptr
));
}
static
ant_value_t
set_values
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
return
create_set_iterator
(
js
,
js
->
this_val
,
ITER_TYPE_SET_VALUES
);
}
static
ant_value_t
set_entries
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
return
create_set_iterator
(
js
,
js
->
this_val
,
ITER_TYPE_SET_ENTRIES
);
}
static
ant_value_t
set_forEach
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_val
=
js
->
this_val
;
set_entry_t
**
set_ptr
=
get_set_from_obj
(
this_val
);
if
(
nargs
<
1
||
vtype
(
args
[
0
])
!=
T_FUNC
)
return
js_mkerr
(
js
,
"forEach requires a callback function"
);
ant_value_t
callback
=
args
[
0
];
if
(
set_ptr
&&
*
set_ptr
)
{
set_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
set_ptr
,
entry
,
tmp
)
{
ant_value_t
call_args
[
3
]
=
{
entry
->
value
,
entry
->
value
,
this_val
};
ant_value_t
result
=
sv_vm_call
(
js
->
vm
,
js
,
callback
,
js_mkundef
(),
call_args
,
3
,
NULL
,
false
);
if
(
is_err
(
result
))
return
result
;
}}
return
js_mkundef
();
}
static
ant_value_t
make_set_result
(
ant_t
*
js
,
set_entry_t
***
out_set
)
{
ant_value_t
set_obj
=
js_mkobj
(
js
);
if
(
is_err
(
set_obj
))
return
set_obj
;
js_obj_ptr
(
set_obj
)
->
type_tag
=
T_SET
;
ant_value_t
set_proto
=
js_get_ctor_proto
(
js
,
"Set"
,
3
);
if
(
is_special_object
(
set_proto
))
js_set_proto_init
(
set_obj
,
set_proto
);
set_entry_t
**
set_head
=
ant_calloc
(
sizeof
(
set_entry_t
*
));
if
(
!
set_head
)
return
js_mkerr
(
js
,
"out of memory"
);
*
set_head
=
NULL
;
js_set_slot
(
set_obj
,
SLOT_DATA
,
ANT_PTR
(
set_head
));
if
(
out_set
)
*
out_set
=
set_head
;
return
set_obj
;
}
static
bool
set_result_add
(
ant_t
*
js
,
ant_value_t
set_obj
,
set_entry_t
**
set_ptr
,
ant_value_t
value
)
{
if
(
!
set_store_entry
(
js
,
set_ptr
,
value
))
return
false
;
ant_object_t
*
obj
=
js_obj_ptr
(
set_obj
);
if
(
obj
)
gc_write_barrier
(
js
,
obj
,
value
);
return
true
;
}
static
void
set_result_delete
(
ant_t
*
js
,
set_entry_t
**
set_ptr
,
ant_value_t
value
)
{
set_entry_t
*
entry
=
set_find_entry
(
js
,
set_ptr
,
value
);
if
(
!
entry
)
return
;
HASH_DEL
(
*
set_ptr
,
entry
);
free
(
entry
->
key
);
free
(
entry
);
}
typedef
struct
{
ant_value_t
obj
;
ant_value_t
has
;
ant_value_t
keys
;
double
size
;
}
set_record_t
;
typedef
enum
{
SET_KEY_CONTINUE
,
SET_KEY_STOP
,
}
set_key_status_t
;
typedef
set_key_status_t
(
*
set_key_cb
)(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
result
,
void
*
ctx
);
static
ant_value_t
get_set_record
(
ant_t
*
js
,
ant_value_t
value
,
const
char
*
method
,
set_record_t
*
out
)
{
if
(
!
is_object_type
(
value
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.%s() requires a set-like object"
,
method
);
ant_value_t
size
=
js_getprop_fallback
(
js
,
value
,
"size"
);
if
(
is_err
(
size
))
return
size
;
if
(
vtype
(
size
)
==
T_BIGINT
||
vtype
(
size
)
==
T_SYMBOL
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.%s() requires a numeric size"
,
method
);
double
num_size
=
js_to_number
(
js
,
size
);
if
(
isnan
(
num_size
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.%s() requires a numeric size"
,
method
);
double
int_size
=
(
num_size
==
0.0
||
!
isfinite
(
num_size
))
?
num_size
:
(
num_size
<
0
?
-
floor
(
-
num_size
)
:
floor
(
num_size
));
if
(
int_size
<
0
)
return
js_mkerr_typed
(
js
,
JS_ERR_RANGE
,
"Set.%s() requires a non-negative size"
,
method
);
ant_value_t
has
=
js_getprop_fallback
(
js
,
value
,
"has"
);
if
(
is_err
(
has
))
return
has
;
if
(
!
is_callable
(
has
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.%s() requires a callable has method"
,
method
);
ant_value_t
keys
=
js_getprop_fallback
(
js
,
value
,
"keys"
);
if
(
is_err
(
keys
))
return
keys
;
if
(
!
is_callable
(
keys
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.%s() requires a callable keys method"
,
method
);
out
->
obj
=
value
;
out
->
has
=
has
;
out
->
keys
=
keys
;
out
->
size
=
int_size
;
return
js_mkundef
();
}
static
ant_value_t
set_record_has
(
ant_t
*
js
,
set_record_t
*
record
,
ant_value_t
value
,
bool
*
out
)
{
ant_value_t
result
=
sv_vm_call
(
js
->
vm
,
js
,
record
->
has
,
record
->
obj
,
&
value
,
1
,
NULL
,
false
);
if
(
is_err
(
result
))
return
result
;
*
out
=
js_truthy
(
js
,
result
);
return
js_mkundef
();
}
static
ant_value_t
set_record_close_keys_iterator
(
ant_t
*
js
,
ant_value_t
iterator
)
{
ant_value_t
return_fn
=
js_getprop_fallback
(
js
,
iterator
,
"return"
);
if
(
is_err
(
return_fn
))
return
return_fn
;
if
(
!
is_callable
(
return_fn
))
return
js_mkundef
();
return
sv_vm_call
(
js
->
vm
,
js
,
return_fn
,
iterator
,
NULL
,
0
,
NULL
,
false
);
}
static
ant_value_t
set_record_for_each_key
(
ant_t
*
js
,
set_record_t
*
record
,
set_key_cb
cb
,
void
*
ctx
)
{
ant_value_t
iterator
=
sv_vm_call
(
js
->
vm
,
js
,
record
->
keys
,
record
->
obj
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
iterator
))
return
iterator
;
if
(
!
is_object_type
(
iterator
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set keys() result is not an iterator"
);
ant_value_t
next_fn
=
js_getprop_fallback
(
js
,
iterator
,
"next"
);
if
(
is_err
(
next_fn
))
return
next_fn
;
if
(
!
is_callable
(
next_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set keys() iterator has no callable next method"
);
while
(
true
)
{
ant_value_t
next
=
sv_vm_call
(
js
->
vm
,
js
,
next_fn
,
iterator
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
next
))
return
next
;
if
(
!
is_object_type
(
next
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set keys() iterator result is not an object"
);
ant_value_t
done
=
js_getprop_fallback
(
js
,
next
,
"done"
);
if
(
is_err
(
done
))
return
done
;
if
(
js_truthy
(
js
,
done
))
return
js_mkundef
();
ant_value_t
value
=
js_getprop_fallback
(
js
,
next
,
"value"
);
if
(
is_err
(
value
))
return
value
;
ant_value_t
result
=
js_mkundef
();
set_key_status_t
status
=
cb
(
js
,
value
,
&
result
,
ctx
);
if
(
is_err
(
result
))
{
ant_value_t
close_result
=
set_record_close_keys_iterator
(
js
,
iterator
);
return
is_err
(
close_result
)
?
close_result
:
result
;
}
if
(
status
==
SET_KEY_STOP
)
{
ant_value_t
close_result
=
set_record_close_keys_iterator
(
js
,
iterator
);
return
is_err
(
close_result
)
?
close_result
:
js_mkundef
();
}
}
}
typedef
struct
{
ant_value_t
out
;
set_entry_t
**
out_set
;
}
set_build_ctx_t
;
static
set_key_status_t
set_add_key_cb
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
result
,
void
*
ctx
)
{
set_build_ctx_t
*
build
=
(
set_build_ctx_t
*
)
ctx
;
if
(
!
set_result_add
(
js
,
build
->
out
,
build
->
out_set
,
value
))
*
result
=
js_mkerr
(
js
,
"out of memory"
);
return
SET_KEY_CONTINUE
;
}
static
ant_value_t
set_union
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
set_entry_t
**
this_set
=
get_set_from_obj
(
js
->
this_val
);
if
(
!
this_set
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Set object"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.union() requires a set-like object"
);
set_record_t
other
;
ant_value_t
rec
=
get_set_record
(
js
,
args
[
0
],
"union"
,
&
other
);
if
(
is_err
(
rec
))
return
rec
;
set_entry_t
**
out_set
=
NULL
;
ant_value_t
out
=
make_set_result
(
js
,
&
out_set
);
if
(
is_err
(
out
))
return
out
;
set_build_ctx_t
build
=
{
out
,
out_set
};
set_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
this_set
,
entry
,
tmp
)
if
(
!
set_result_add
(
js
,
out
,
out_set
,
entry
->
value
))
return
js_mkerr
(
js
,
"out of memory"
);
ant_value_t
result
=
set_record_for_each_key
(
js
,
&
other
,
set_add_key_cb
,
&
build
);
return
is_err
(
result
)
?
result
:
out
;
}
typedef
struct
{
ant_value_t
out
;
set_entry_t
**
out_set
;
set_entry_t
**
this_set
;
}
set_compare_build_ctx_t
;
static
set_key_status_t
set_intersection_key_cb
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
result
,
void
*
ctx
)
{
set_compare_build_ctx_t
*
build
=
(
set_compare_build_ctx_t
*
)
ctx
;
if
(
set_find_entry
(
js
,
build
->
this_set
,
value
)
&&
!
set_result_add
(
js
,
build
->
out
,
build
->
out_set
,
value
)
)
*
result
=
js_mkerr
(
js
,
"out of memory"
);
return
SET_KEY_CONTINUE
;
}
static
ant_value_t
set_intersection
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
set_entry_t
**
this_set
=
get_set_from_obj
(
js
->
this_val
);
if
(
!
this_set
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Set object"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.intersection() requires a set-like object"
);
set_record_t
other
;
ant_value_t
rec
=
get_set_record
(
js
,
args
[
0
],
"intersection"
,
&
other
);
if
(
is_err
(
rec
))
return
rec
;
set_entry_t
**
out_set
=
NULL
;
ant_value_t
out
=
make_set_result
(
js
,
&
out_set
);
if
(
is_err
(
out
))
return
out
;
double
this_size
=
(
double
)
HASH_COUNT
(
*
this_set
);
if
(
this_size
<=
other
.
size
)
{
set_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
this_set
,
entry
,
tmp
)
{
bool
has
=
false
;
ant_value_t
result
=
set_record_has
(
js
,
&
other
,
entry
->
value
,
&
has
);
if
(
is_err
(
result
))
return
result
;
if
(
has
&&
!
set_result_add
(
js
,
out
,
out_set
,
entry
->
value
))
return
js_mkerr
(
js
,
"out of memory"
);
}
return
out
;
}
set_compare_build_ctx_t
build
=
{
out
,
out_set
,
this_set
};
ant_value_t
result
=
set_record_for_each_key
(
js
,
&
other
,
set_intersection_key_cb
,
&
build
);
return
is_err
(
result
)
?
result
:
out
;
}
typedef
struct
{
set_record_t
*
other
;
ant_value_t
out
;
set_entry_t
**
out_set
;
}
set_difference_ctx_t
;
static
ant_value_t
set_difference_key_cb
(
ant_t
*
js
,
ant_value_t
value
,
void
*
ctx
)
{
set_difference_ctx_t
*
build
=
(
set_difference_ctx_t
*
)
ctx
;
bool
has
=
false
;
ant_value_t
result
=
set_record_has
(
js
,
build
->
other
,
value
,
&
has
);
if
(
is_err
(
result
))
return
result
;
if
(
!
has
&&
!
set_result_add
(
js
,
build
->
out
,
build
->
out_set
,
value
))
return
js_mkerr
(
js
,
"out of memory"
);
return
js_mkundef
();
}
static
set_key_status_t
set_delete_key_cb
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
result
,
void
*
ctx
)
{
set_build_ctx_t
*
build
=
(
set_build_ctx_t
*
)
ctx
;
set_result_delete
(
js
,
build
->
out_set
,
value
);
return
SET_KEY_CONTINUE
;
}
static
ant_value_t
set_difference
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
set_entry_t
**
this_set
=
get_set_from_obj
(
js
->
this_val
);
if
(
!
this_set
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Set object"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.difference() requires a set-like object"
);
set_record_t
other
;
ant_value_t
rec
=
get_set_record
(
js
,
args
[
0
],
"difference"
,
&
other
);
if
(
is_err
(
rec
))
return
rec
;
set_entry_t
**
out_set
=
NULL
;
ant_value_t
out
=
make_set_result
(
js
,
&
out_set
);
if
(
is_err
(
out
))
return
out
;
set_entry_t
*
entry
,
*
tmp
;
if
((
double
)
HASH_COUNT
(
*
this_set
)
<=
other
.
size
)
{
set_difference_ctx_t
diff
=
{
&
other
,
out
,
out_set
};
HASH_ITER
(
hh
,
*
this_set
,
entry
,
tmp
)
{
ant_value_t
result
=
set_difference_key_cb
(
js
,
entry
->
value
,
&
diff
);
if
(
is_err
(
result
))
return
result
;
}
return
out
;
}
HASH_ITER
(
hh
,
*
this_set
,
entry
,
tmp
)
{
if
(
!
set_result_add
(
js
,
out
,
out_set
,
entry
->
value
))
return
js_mkerr
(
js
,
"out of memory"
);
}
set_build_ctx_t
build
=
{
out
,
out_set
};
ant_value_t
result
=
set_record_for_each_key
(
js
,
&
other
,
set_delete_key_cb
,
&
build
);
return
is_err
(
result
)
?
result
:
out
;
}
typedef
struct
{
ant_value_t
out
;
set_entry_t
**
out_set
;
set_entry_t
**
this_set
;
}
set_symdiff_ctx_t
;
static
set_key_status_t
set_symmetric_difference_key_cb
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
result
,
void
*
ctx
)
{
set_symdiff_ctx_t
*
build
=
(
set_symdiff_ctx_t
*
)
ctx
;
if
(
set_find_entry
(
js
,
build
->
this_set
,
value
))
{
set_result_delete
(
js
,
build
->
out_set
,
value
);
}
else
if
(
!
set_find_entry
(
js
,
build
->
out_set
,
value
))
if
(
!
set_result_add
(
js
,
build
->
out
,
build
->
out_set
,
value
))
*
result
=
js_mkerr
(
js
,
"out of memory"
);
return
SET_KEY_CONTINUE
;
}
static
ant_value_t
set_symmetricDifference
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
set_entry_t
**
this_set
=
get_set_from_obj
(
js
->
this_val
);
if
(
!
this_set
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Set object"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.symmetricDifference() requires a set-like object"
);
set_record_t
other
;
ant_value_t
rec
=
get_set_record
(
js
,
args
[
0
],
"symmetricDifference"
,
&
other
);
if
(
is_err
(
rec
))
return
rec
;
set_entry_t
**
out_set
=
NULL
;
ant_value_t
out
=
make_set_result
(
js
,
&
out_set
);
if
(
is_err
(
out
))
return
out
;
set_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
this_set
,
entry
,
tmp
)
{
if
(
!
set_result_add
(
js
,
out
,
out_set
,
entry
->
value
))
return
js_mkerr
(
js
,
"out of memory"
);
}
set_symdiff_ctx_t
build
=
{
out
,
out_set
,
this_set
};
ant_value_t
result
=
set_record_for_each_key
(
js
,
&
other
,
set_symmetric_difference_key_cb
,
&
build
);
return
is_err
(
result
)
?
result
:
out
;
}
static
ant_value_t
set_isSubsetOf
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
set_entry_t
**
this_set
=
get_set_from_obj
(
js
->
this_val
);
if
(
!
this_set
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Set object"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.isSubsetOf() requires a set-like object"
);
set_record_t
other
;
ant_value_t
rec
=
get_set_record
(
js
,
args
[
0
],
"isSubsetOf"
,
&
other
);
if
(
is_err
(
rec
))
return
rec
;
if
((
double
)
HASH_COUNT
(
*
this_set
)
>
other
.
size
)
return
js_false
;
set_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
this_set
,
entry
,
tmp
)
{
bool
has
=
false
;
ant_value_t
result
=
set_record_has
(
js
,
&
other
,
entry
->
value
,
&
has
);
if
(
is_err
(
result
))
return
result
;
if
(
!
has
)
return
js_false
;
}
return
js_true
;
}
typedef
struct
{
set_entry_t
**
this_set
;
bool
result
;
}
set_predicate_ctx_t
;
static
set_key_status_t
set_superset_key_cb
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
result
,
void
*
ctx
)
{
set_predicate_ctx_t
*
pred
=
(
set_predicate_ctx_t
*
)
ctx
;
if
(
!
set_find_entry
(
js
,
pred
->
this_set
,
value
))
{
pred
->
result
=
false
;
return
SET_KEY_STOP
;
}
return
SET_KEY_CONTINUE
;
}
static
ant_value_t
set_isSupersetOf
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
set_entry_t
**
this_set
=
get_set_from_obj
(
js
->
this_val
);
if
(
!
this_set
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Set object"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.isSupersetOf() requires a set-like object"
);
set_record_t
other
;
ant_value_t
rec
=
get_set_record
(
js
,
args
[
0
],
"isSupersetOf"
,
&
other
);
if
(
is_err
(
rec
))
return
rec
;
if
((
double
)
HASH_COUNT
(
*
this_set
)
<
other
.
size
)
return
js_false
;
set_predicate_ctx_t
pred
=
{
this_set
,
true
};
ant_value_t
result
=
set_record_for_each_key
(
js
,
&
other
,
set_superset_key_cb
,
&
pred
);
if
(
is_err
(
result
))
return
result
;
return
js_bool
(
pred
.
result
);
}
static
set_key_status_t
set_disjoint_key_cb
(
ant_t
*
js
,
ant_value_t
value
,
ant_value_t
*
result
,
void
*
ctx
)
{
set_predicate_ctx_t
*
pred
=
(
set_predicate_ctx_t
*
)
ctx
;
if
(
set_find_entry
(
js
,
pred
->
this_set
,
value
))
{
pred
->
result
=
false
;
return
SET_KEY_STOP
;
}
return
SET_KEY_CONTINUE
;
}
static
ant_value_t
set_isDisjointFrom
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
set_entry_t
**
this_set
=
get_set_from_obj
(
js
->
this_val
);
if
(
!
this_set
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Invalid Set object"
);
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set.isDisjointFrom() requires a set-like object"
);
set_record_t
other
;
ant_value_t
rec
=
get_set_record
(
js
,
args
[
0
],
"isDisjointFrom"
,
&
other
);
if
(
is_err
(
rec
))
return
rec
;
if
((
double
)
HASH_COUNT
(
*
this_set
)
<=
other
.
size
)
{
set_entry_t
*
entry
,
*
tmp
;
HASH_ITER
(
hh
,
*
this_set
,
entry
,
tmp
)
{
bool
has
=
false
;
ant_value_t
result
=
set_record_has
(
js
,
&
other
,
entry
->
value
,
&
has
);
if
(
is_err
(
result
))
return
result
;
if
(
has
)
return
js_false
;
}
return
js_true
;
}
set_predicate_ctx_t
pred
=
{
this_set
,
true
};
ant_value_t
result
=
set_record_for_each_key
(
js
,
&
other
,
set_disjoint_key_cb
,
&
pred
);
if
(
is_err
(
result
))
return
result
;
return
js_bool
(
pred
.
result
);
}
static
ant_value_t
weakmap_set
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkerr
(
js
,
"WeakMap.set() requires 2 arguments"
);
ant_value_t
this_val
=
js
->
this_val
;
weakmap_entry_t
**
wm_ptr
=
get_weakmap_from_obj
(
this_val
);
if
(
!
wm_ptr
)
return
js_mkerr
(
js
,
"Invalid WeakMap object"
);
if
(
!
is_object_type
(
args
[
0
]))
return
js_mkerr
(
js
,
"WeakMap key must be an object"
);
ant_value_t
key_obj
=
args
[
0
];
weakmap_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
wm_ptr
,
&
key_obj
,
sizeof
(
ant_value_t
),
entry
);
if
(
entry
)
entry
->
value
=
args
[
1
];
else
{
entry
=
ant_calloc
(
sizeof
(
weakmap_entry_t
));
if
(
!
entry
)
return
js_mkerr
(
js
,
"out of memory"
);
entry
->
key_obj
=
key_obj
;
entry
->
value
=
args
[
1
];
HASH_ADD
(
hh
,
*
wm_ptr
,
key_obj
,
sizeof
(
ant_value_t
),
entry
);
}
ant_object_t
*
wm_obj
=
js_obj_ptr
(
this_val
);
if
(
wm_obj
)
{
gc_write_barrier
(
js
,
wm_obj
,
key_obj
);
gc_write_barrier
(
js
,
wm_obj
,
args
[
1
]);
}
return
this_val
;
}
static
ant_value_t
weakmap_get
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"WeakMap.get() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
weakmap_entry_t
**
wm_ptr
=
get_weakmap_from_obj
(
this_val
);
if
(
!
wm_ptr
)
return
js_mkundef
();
if
(
!
is_object_type
(
args
[
0
]))
return
js_mkundef
();
ant_value_t
key_obj
=
args
[
0
];
weakmap_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
wm_ptr
,
&
key_obj
,
sizeof
(
ant_value_t
),
entry
);
return
entry
?
entry
->
value
:
js_mkundef
();
}
static
ant_value_t
weakmap_has
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"WeakMap.has() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
weakmap_entry_t
**
wm_ptr
=
get_weakmap_from_obj
(
this_val
);
if
(
!
wm_ptr
)
return
js_false
;
if
(
!
is_object_type
(
args
[
0
]))
return
js_false
;
ant_value_t
key_obj
=
args
[
0
];
weakmap_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
wm_ptr
,
&
key_obj
,
sizeof
(
ant_value_t
),
entry
);
return
js_bool
(
entry
!=
NULL
);
}
static
ant_value_t
weakmap_upsert
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
3
)
return
js_mkerr
(
js
,
"WeakMap.upsert() requires 3 arguments"
);
ant_value_t
this_val
=
js
->
this_val
;
weakmap_entry_t
**
wm_ptr
=
get_weakmap_from_obj
(
this_val
);
if
(
!
wm_ptr
)
return
js_mkerr
(
js
,
"Invalid WeakMap object"
);
if
(
!
is_object_type
(
args
[
0
]))
return
js_mkerr
(
js
,
"WeakMap key must be an object"
);
ant_value_t
update_fn
=
args
[
1
];
ant_value_t
insert_fn
=
args
[
2
];
if
(
!
is_callable
(
update_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakMap.upsert update callback must be callable"
);
if
(
!
is_callable
(
insert_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakMap.upsert insert callback must be callable"
);
ant_value_t
key_obj
=
args
[
0
];
weakmap_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
wm_ptr
,
&
key_obj
,
sizeof
(
ant_value_t
),
entry
);
ant_value_t
value
;
if
(
entry
)
{
ant_value_t
call_args
[
3
]
=
{
entry
->
value
,
key_obj
,
this_val
};
value
=
sv_vm_call
(
js
->
vm
,
js
,
update_fn
,
js_mkundef
(),
call_args
,
3
,
NULL
,
false
);
}
else
{
ant_value_t
call_args
[
2
]
=
{
key_obj
,
this_val
};
value
=
sv_vm_call
(
js
->
vm
,
js
,
insert_fn
,
js_mkundef
(),
call_args
,
2
,
NULL
,
false
);
}
if
(
is_err
(
value
))
return
value
;
HASH_FIND
(
hh
,
*
wm_ptr
,
&
key_obj
,
sizeof
(
ant_value_t
),
entry
);
if
(
entry
)
entry
->
value
=
value
;
else
{
entry
=
ant_calloc
(
sizeof
(
weakmap_entry_t
));
if
(
!
entry
)
return
js_mkerr
(
js
,
"out of memory"
);
entry
->
key_obj
=
key_obj
;
entry
->
value
=
value
;
HASH_ADD
(
hh
,
*
wm_ptr
,
key_obj
,
sizeof
(
ant_value_t
),
entry
);
}
ant_object_t
*
wm_obj
=
js_obj_ptr
(
this_val
);
if
(
wm_obj
)
{
gc_write_barrier
(
js
,
wm_obj
,
key_obj
);
gc_write_barrier
(
js
,
wm_obj
,
value
);
}
return
value
;
}
static
ant_value_t
weakmap_delete
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"WeakMap.delete() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
weakmap_entry_t
**
wm_ptr
=
get_weakmap_from_obj
(
this_val
);
if
(
!
wm_ptr
)
return
js_false
;
if
(
!
is_object_type
(
args
[
0
]))
return
js_false
;
ant_value_t
key_obj
=
args
[
0
];
weakmap_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
wm_ptr
,
&
key_obj
,
sizeof
(
ant_value_t
),
entry
);
if
(
entry
)
{
HASH_DEL
(
*
wm_ptr
,
entry
);
free
(
entry
);
return
js_true
;
}
return
js_false
;
}
static
ant_value_t
weakset_add
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"WeakSet.add() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
weakset_entry_t
**
ws_ptr
=
get_weakset_from_obj
(
this_val
);
if
(
!
ws_ptr
)
return
js_mkerr
(
js
,
"Invalid WeakSet object"
);
if
(
!
is_object_type
(
args
[
0
]))
return
js_mkerr
(
js
,
"WeakSet value must be an object"
);
ant_value_t
value_obj
=
args
[
0
];
weakset_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
ws_ptr
,
&
value_obj
,
sizeof
(
ant_value_t
),
entry
);
if
(
!
entry
)
{
entry
=
ant_calloc
(
sizeof
(
weakset_entry_t
));
if
(
!
entry
)
return
js_mkerr
(
js
,
"out of memory"
);
entry
->
value_obj
=
value_obj
;
HASH_ADD
(
hh
,
*
ws_ptr
,
value_obj
,
sizeof
(
ant_value_t
),
entry
);
}
return
this_val
;
}
static
ant_value_t
weakset_has
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"WeakSet.has() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
weakset_entry_t
**
ws_ptr
=
get_weakset_from_obj
(
this_val
);
if
(
!
ws_ptr
)
return
js_false
;
if
(
!
is_object_type
(
args
[
0
]))
return
js_false
;
ant_value_t
value_obj
=
args
[
0
];
weakset_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
ws_ptr
,
&
value_obj
,
sizeof
(
ant_value_t
),
entry
);
return
js_bool
(
entry
!=
NULL
);
}
static
ant_value_t
weakset_delete
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr
(
js
,
"WeakSet.delete() requires 1 argument"
);
ant_value_t
this_val
=
js
->
this_val
;
weakset_entry_t
**
ws_ptr
=
get_weakset_from_obj
(
this_val
);
if
(
!
ws_ptr
)
return
js_false
;
if
(
!
is_object_type
(
args
[
0
]))
return
js_false
;
ant_value_t
value_obj
=
args
[
0
];
weakset_entry_t
*
entry
;
HASH_FIND
(
hh
,
*
ws_ptr
,
&
value_obj
,
sizeof
(
ant_value_t
),
entry
);
if
(
entry
)
{
HASH_DEL
(
*
ws_ptr
,
entry
);
free
(
entry
);
return
js_true
;
}
return
js_false
;
}
static
ant_value_t
builtin_WeakRef
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
!
is_object_type
(
args
[
0
]))
{
return
js_mkerr
(
js
,
"WeakRef target must be an object"
);
}
ant_value_t
wr_obj
=
js_mkobj
(
js
);
ant_value_t
wr_proto
=
js_get_ctor_proto
(
js
,
"WeakRef"
,
7
);
if
(
is_special_object
(
wr_proto
))
js_set_proto_init
(
wr_obj
,
wr_proto
);
js_set_slot
(
wr_obj
,
SLOT_DATA
,
args
[
0
]);
return
wr_obj
;
}
static
ant_value_t
weakref_deref
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
ant_value_t
this_val
=
js
->
this_val
;
if
(
vtype
(
this_val
)
!=
T_OBJ
)
return
js_mkundef
();
ant_value_t
target
=
js_get_slot
(
this_val
,
SLOT_DATA
);
if
(
vtype
(
target
)
!=
T_OBJ
)
return
js_mkundef
();
return
target
;
}
static
ant_value_t
builtin_FinalizationRegistry
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
(
vtype
(
args
[
0
])
!=
T_FUNC
&&
vtype
(
args
[
0
])
!=
T_CFUNC
))
{
return
js_mkerr
(
js
,
"FinalizationRegistry callback must be a function"
);
}
ant_value_t
fr_obj
=
js_mkobj
(
js
);
ant_value_t
fr_proto
=
js_get_ctor_proto
(
js
,
"FinalizationRegistry"
,
20
);
if
(
is_special_object
(
fr_proto
))
js_set_proto_init
(
fr_obj
,
fr_proto
);
js_set_slot
(
fr_obj
,
SLOT_MAP
,
js_mkarr
(
js
));
js_set_slot
(
fr_obj
,
SLOT_DATA
,
args
[
0
]);
return
fr_obj
;
}
static
ant_value_t
finreg_register
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_val
=
js
->
this_val
;
if
(
vtype
(
this_val
)
!=
T_OBJ
)
return
js_mkundef
();
if
(
nargs
<
1
||
vtype
(
args
[
0
])
!=
T_OBJ
)
{
return
js_mkerr
(
js
,
"FinalizationRegistry.register target must be an object"
);
}
ant_value_t
target
=
args
[
0
];
ant_value_t
held_value
=
nargs
>
1
?
args
[
1
]
:
js_mkundef
();
ant_value_t
unregister_token
=
nargs
>
2
?
args
[
2
]
:
js_mkundef
();
if
(
vdata
(
target
)
==
vdata
(
held_value
)
&&
vtype
(
held_value
)
==
T_OBJ
)
{
return
js_mkerr
(
js
,
"target and held value must not be the same"
);
}
ant_value_t
registrations
=
js_get_slot
(
this_val
,
SLOT_MAP
);
if
(
vtype
(
registrations
)
!=
T_ARR
)
return
js_mkundef
();
ant_value_t
entry
=
js_mkarr
(
js
);
ant_offset_t
len
=
js_arr_len
(
js
,
registrations
);
char
idx
[
16
];
size_t
idx_len
=
uint_to_str
(
idx
,
sizeof
(
idx
),
0
);
js_setprop
(
js
,
entry
,
js_mkstr
(
js
,
idx
,
idx_len
),
target
);
idx_len
=
uint_to_str
(
idx
,
sizeof
(
idx
),
1
);
js_setprop
(
js
,
entry
,
js_mkstr
(
js
,
idx
,
idx_len
),
held_value
);
idx_len
=
uint_to_str
(
idx
,
sizeof
(
idx
),
2
);
js_setprop
(
js
,
entry
,
js_mkstr
(
js
,
idx
,
idx_len
),
unregister_token
);
js_setprop
(
js
,
entry
,
js
->
length_str
,
tov
(
3.0
));
idx_len
=
uint_to_str
(
idx
,
sizeof
(
idx
),
len
);
js_setprop
(
js
,
registrations
,
js_mkstr
(
js
,
idx
,
idx_len
),
entry
);
js_setprop
(
js
,
registrations
,
js
->
length_str
,
tov
((
double
)(
len
+
1
)));
return
js_mkundef
();
}
static
ant_value_t
finreg_unregister
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
this_val
=
js
->
this_val
;
if
(
vtype
(
this_val
)
!=
T_OBJ
)
return
js_false
;
if
(
nargs
<
1
||
vtype
(
args
[
0
])
!=
T_OBJ
)
{
return
js_mkerr
(
js
,
"FinalizationRegistry.unregister token must be an object"
);
}
ant_value_t
token
=
args
[
0
];
ant_value_t
registrations
=
js_get_slot
(
this_val
,
SLOT_MAP
);
if
(
vtype
(
registrations
)
!=
T_ARR
)
return
js_false
;
ant_offset_t
len
=
js_arr_len
(
js
,
registrations
);
bool
removed
=
false
;
for
(
ant_offset_t
i
=
0
;
i
<
len
;
i
++
)
{
ant_value_t
entry
=
js_arr_get
(
js
,
registrations
,
i
);
if
(
vtype
(
entry
)
!=
T_ARR
)
continue
;
ant_value_t
entry_token
=
js_arr_get
(
js
,
entry
,
2
);
if
(
vtype
(
entry_token
)
==
T_OBJ
&&
vdata
(
entry_token
)
==
vdata
(
token
))
{
char
idx
[
16
];
size_t
idx_len
=
uint_to_str
(
idx
,
sizeof
(
idx
),
i
);
js_setprop
(
js
,
registrations
,
js_mkstr
(
js
,
idx
,
idx_len
),
js_mkundef
());
removed
=
true
;
}
}
return
js_bool
(
removed
);
}
static
ant_value_t
map_groupBy
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
2
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Map.groupBy requires 2 arguments"
);
ant_value_t
items
=
args
[
0
];
ant_value_t
callback
=
args
[
1
];
if
(
vtype
(
callback
)
!=
T_FUNC
&&
vtype
(
callback
)
!=
T_CFUNC
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"callback is not a function"
);
ant_value_t
map_obj
=
js_mkobj
(
js
);
js_obj_ptr
(
map_obj
)
->
type_tag
=
T_MAP
;
ant_value_t
map_proto
=
js_get_ctor_proto
(
js
,
"Map"
,
3
);
if
(
is_special_object
(
map_proto
))
js_set_proto_init
(
map_obj
,
map_proto
);
map_entry_t
**
map_head
=
ant_calloc
(
sizeof
(
map_entry_t
*
));
if
(
!
map_head
)
return
js_mkerr
(
js
,
"out of memory"
);
*
map_head
=
NULL
;
js_set_slot
(
map_obj
,
SLOT_DATA
,
ANT_PTR
(
map_head
));
ant_offset_t
len
=
js_arr_len
(
js
,
items
);
for
(
ant_offset_t
i
=
0
;
i
<
len
;
i
++
)
{
ant_value_t
val
=
js_arr_get
(
js
,
items
,
i
);
ant_value_t
cb_args
[
2
]
=
{
val
,
tov
((
double
)
i
)
};
ant_value_t
key
=
normalize_map_key
(
sv_vm_call
(
js
->
vm
,
js
,
callback
,
js_mkundef
(),
cb_args
,
2
,
NULL
,
false
)
);
if
(
is_err
(
key
))
return
key
;
map_entry_t
*
entry
=
map_find_entry
(
js
,
map_head
,
key
);
ant_value_t
group
;
if
(
entry
)
group
=
entry
->
value
;
else
{
group
=
js_mkarr
(
js
);
if
(
!
map_store_entry
(
js
,
map_head
,
key
,
key
,
group
))
return
js_mkerr
(
js
,
"out of memory"
);
}
js_arr_push
(
js
,
group
,
val
);
}
return
map_obj
;
}
static
ant_value_t
builtin_Map
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Map constructor requires 'new'"
);
}
ant_value_t
map_obj
=
js
->
this_val
;
if
(
vtype
(
map_obj
)
!=
T_OBJ
)
map_obj
=
js_mkobj
(
js
);
if
(
is_err
(
map_obj
))
return
map_obj
;
js_obj_ptr
(
map_obj
)
->
type_tag
=
T_MAP
;
ant_value_t
map_proto
=
js_get_ctor_proto
(
js
,
"Map"
,
3
);
ant_value_t
instance_proto
=
js_instance_proto_from_new_target
(
js
,
map_proto
);
if
(
is_special_object
(
instance_proto
))
js_set_proto_init
(
map_obj
,
instance_proto
);
map_entry_t
**
map_head
=
ant_calloc
(
sizeof
(
map_entry_t
*
));
if
(
!
map_head
)
return
js_mkerr
(
js
,
"out of memory"
);
*
map_head
=
NULL
;
if
(
vtype
(
js
->
new_target
)
==
T_FUNC
||
vtype
(
js
->
new_target
)
==
T_CFUNC
)
js_set_slot
(
map_obj
,
SLOT_CTOR
,
js
->
new_target
);
js_set_slot
(
map_obj
,
SLOT_DATA
,
ANT_PTR
(
map_head
));
if
(
nargs
==
0
||
vtype
(
args
[
0
])
==
T_UNDEF
||
vtype
(
args
[
0
])
==
T_NULL
)
return
map_obj
;
ant_value_t
init_result
=
map_init_from_iterable
(
js
,
map_head
,
args
[
0
]);
if
(
is_err
(
init_result
))
return
init_result
;
return
map_obj
;
}
static
ant_value_t
builtin_Set
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Set constructor requires 'new'"
);
}
ant_value_t
set_obj
=
js
->
this_val
;
if
(
vtype
(
set_obj
)
!=
T_OBJ
)
set_obj
=
js_mkobj
(
js
);
if
(
is_err
(
set_obj
))
return
set_obj
;
js_obj_ptr
(
set_obj
)
->
type_tag
=
T_SET
;
ant_value_t
set_proto
=
js_get_ctor_proto
(
js
,
"Set"
,
3
);
ant_value_t
instance_proto
=
js_instance_proto_from_new_target
(
js
,
set_proto
);
if
(
is_special_object
(
instance_proto
))
js_set_proto_init
(
set_obj
,
instance_proto
);
set_entry_t
**
set_head
=
ant_calloc
(
sizeof
(
set_entry_t
*
));
if
(
!
set_head
)
return
js_mkerr
(
js
,
"out of memory"
);
*
set_head
=
NULL
;
if
(
vtype
(
js
->
new_target
)
==
T_FUNC
||
vtype
(
js
->
new_target
)
==
T_CFUNC
)
js_set_slot
(
set_obj
,
SLOT_CTOR
,
js
->
new_target
);
js_set_slot
(
set_obj
,
SLOT_DATA
,
ANT_PTR
(
set_head
));
if
(
nargs
==
0
||
vtype
(
args
[
0
])
==
T_UNDEF
||
vtype
(
args
[
0
])
==
T_NULL
)
return
set_obj
;
ant_value_t
init_result
=
set_init_from_iterable
(
js
,
set_head
,
args
[
0
]);
if
(
is_err
(
init_result
))
return
init_result
;
return
set_obj
;
}
static
ant_value_t
builtin_WeakMap
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakMap constructor requires 'new'"
);
}
ant_value_t
wm_obj
=
js
->
this_val
;
if
(
vtype
(
wm_obj
)
!=
T_OBJ
)
wm_obj
=
js_mkobj
(
js
);
if
(
is_err
(
wm_obj
))
return
wm_obj
;
js_obj_ptr
(
wm_obj
)
->
type_tag
=
T_WEAKMAP
;
ant_value_t
wm_proto
=
js_get_ctor_proto
(
js
,
"WeakMap"
,
7
);
ant_value_t
instance_proto
=
js_instance_proto_from_new_target
(
js
,
wm_proto
);
if
(
is_special_object
(
instance_proto
))
js_set_proto_init
(
wm_obj
,
instance_proto
);
weakmap_entry_t
**
wm_head
=
ant_calloc
(
sizeof
(
weakmap_entry_t
*
));
if
(
!
wm_head
)
return
js_mkerr
(
js
,
"out of memory"
);
*
wm_head
=
NULL
;
if
(
vtype
(
js
->
new_target
)
==
T_FUNC
||
vtype
(
js
->
new_target
)
==
T_CFUNC
)
js_set_slot
(
wm_obj
,
SLOT_CTOR
,
js
->
new_target
);
js_set_slot
(
wm_obj
,
SLOT_DATA
,
ANT_PTR
(
wm_head
));
if
(
nargs
==
0
||
vtype
(
args
[
0
])
==
T_UNDEF
||
vtype
(
args
[
0
])
==
T_NULL
)
return
wm_obj
;
ant_value_t
init_result
=
weakmap_init_from_iterable
(
js
,
wm_head
,
args
[
0
]);
if
(
is_err
(
init_result
))
return
init_result
;
return
wm_obj
;
}
static
ant_value_t
builtin_WeakSet
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
{
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"WeakSet constructor requires 'new'"
);
}
ant_value_t
ws_obj
=
js
->
this_val
;
if
(
vtype
(
ws_obj
)
!=
T_OBJ
)
ws_obj
=
js_mkobj
(
js
);
if
(
is_err
(
ws_obj
))
return
ws_obj
;
js_obj_ptr
(
ws_obj
)
->
type_tag
=
T_WEAKSET
;
ant_value_t
ws_proto
=
js_get_ctor_proto
(
js
,
"WeakSet"
,
7
);
ant_value_t
instance_proto
=
js_instance_proto_from_new_target
(
js
,
ws_proto
);
if
(
is_special_object
(
instance_proto
))
js_set_proto_init
(
ws_obj
,
instance_proto
);
weakset_entry_t
**
ws_head
=
ant_calloc
(
sizeof
(
weakset_entry_t
*
));
if
(
!
ws_head
)
return
js_mkerr
(
js
,
"out of memory"
);
*
ws_head
=
NULL
;
if
(
vtype
(
js
->
new_target
)
==
T_FUNC
||
vtype
(
js
->
new_target
)
==
T_CFUNC
)
js_set_slot
(
ws_obj
,
SLOT_CTOR
,
js
->
new_target
);
js_set_slot
(
ws_obj
,
SLOT_DATA
,
ANT_PTR
(
ws_head
));
if
(
nargs
==
0
||
vtype
(
args
[
0
])
==
T_UNDEF
||
vtype
(
args
[
0
])
==
T_NULL
)
return
ws_obj
;
ant_value_t
init_result
=
weakset_init_from_iterable
(
js
,
ws_head
,
args
[
0
]);
if
(
is_err
(
init_result
))
return
init_result
;
return
ws_obj
;
}
void
init_collections_module
(
void
)
{
ant_t
*
js
=
rt
->
js
;
ant_value_t
glob
=
js
->
global
;
ant_value_t
object_proto
=
js
->
sym
.
object_proto
;
ant_value_t
iter_sym
=
get_iterator_sym
();
ant_value_t
tag_sym
=
get_toStringTag_sym
();
g_map_iter_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
g_map_iter_proto
,
js
->
sym
.
iterator_proto
);
js_set
(
js
,
g_map_iter_proto
,
"next"
,
js_mkfun
(
map_iter_next
));
js_iter_register_advance
(
g_map_iter_proto
,
advance_map
);
g_set_iter_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
g_set_iter_proto
,
js
->
sym
.
iterator_proto
);
js_set
(
js
,
g_set_iter_proto
,
"next"
,
js_mkfun
(
set_iter_next
));
js_iter_register_advance
(
g_set_iter_proto
,
advance_set
);
ant_value_t
map_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
map_proto
,
object_proto
);
js_set
(
js
,
map_proto
,
"set"
,
js_mkfun
(
map_set
));
js_set
(
js
,
map_proto
,
"get"
,
js_mkfun
(
map_get
));
js_set
(
js
,
map_proto
,
"has"
,
js_mkfun
(
map_has
));
js_set
(
js
,
map_proto
,
"upsert"
,
js_mkfun
(
map_upsert
));
js_set
(
js
,
map_proto
,
"delete"
,
js_mkfun
(
map_delete
));
js_set
(
js
,
map_proto
,
"clear"
,
js_mkfun
(
map_clear
));
js_set_getter_desc
(
js
,
map_proto
,
"size"
,
4
,
js_mkfun
(
map_size
),
JS_DESC_C
);
js_set
(
js
,
map_proto
,
"entries"
,
js_mkfun
(
map_entries
));
js_set
(
js
,
map_proto
,
"keys"
,
js_mkfun
(
map_keys
));
js_set
(
js
,
map_proto
,
"values"
,
js_mkfun
(
map_values
));
js_set
(
js
,
map_proto
,
"forEach"
,
js_mkfun
(
map_forEach
));
js_set_sym
(
js
,
map_proto
,
iter_sym
,
js_get
(
js
,
map_proto
,
"entries"
));
js_set_sym
(
js
,
map_proto
,
tag_sym
,
js_mkstr
(
js
,
"Map"
,
3
));
ant_value_t
map_ctor
=
js_mkobj
(
js
);
js_set_slot
(
map_ctor
,
SLOT_CFUNC
,
js_mkfun
(
builtin_Map
));
js_mkprop_fast
(
js
,
map_ctor
,
"prototype"
,
9
,
map_proto
);
js_mkprop_fast
(
js
,
map_ctor
,
"name"
,
4
,
ANT_STRING
(
"Map"
));
js_set_descriptor
(
js
,
map_ctor
,
"name"
,
4
,
0
);
js_set
(
js
,
map_ctor
,
"groupBy"
,
js_mkfun
(
map_groupBy
));
js_define_species_getter
(
js
,
map_ctor
);
js_set
(
js
,
glob
,
"Map"
,
js_obj_to_func
(
map_ctor
));
ant_value_t
set_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
set_proto
,
object_proto
);
js_set
(
js
,
set_proto
,
"add"
,
js_mkfun
(
set_add
));
js_set
(
js
,
set_proto
,
"has"
,
js_mkfun
(
set_has
));
js_set
(
js
,
set_proto
,
"delete"
,
js_mkfun
(
set_delete
));
js_set
(
js
,
set_proto
,
"clear"
,
js_mkfun
(
set_clear
));
js_set_getter_desc
(
js
,
set_proto
,
"size"
,
4
,
js_mkfun
(
set_size
),
JS_DESC_C
);
js_set
(
js
,
set_proto
,
"values"
,
js_mkfun
(
set_values
));
js_set_exact
(
js
,
set_proto
,
"keys"
,
js_get
(
js
,
set_proto
,
"values"
));
js_set
(
js
,
set_proto
,
"entries"
,
js_mkfun
(
set_entries
));
js_set
(
js
,
set_proto
,
"forEach"
,
js_mkfun
(
set_forEach
));
js_set
(
js
,
set_proto
,
"union"
,
js_mkfun
(
set_union
));
js_set
(
js
,
set_proto
,
"intersection"
,
js_mkfun
(
set_intersection
));
js_set
(
js
,
set_proto
,
"difference"
,
js_mkfun
(
set_difference
));
js_set
(
js
,
set_proto
,
"symmetricDifference"
,
js_mkfun
(
set_symmetricDifference
));
js_set
(
js
,
set_proto
,
"isSubsetOf"
,
js_mkfun
(
set_isSubsetOf
));
js_set
(
js
,
set_proto
,
"isSupersetOf"
,
js_mkfun
(
set_isSupersetOf
));
js_set
(
js
,
set_proto
,
"isDisjointFrom"
,
js_mkfun
(
set_isDisjointFrom
));
js_set_sym
(
js
,
set_proto
,
iter_sym
,
js_get
(
js
,
set_proto
,
"values"
));
js_set_sym
(
js
,
set_proto
,
tag_sym
,
js_mkstr
(
js
,
"Set"
,
3
));
ant_value_t
set_ctor
=
js_mkobj
(
js
);
js_set_slot
(
set_ctor
,
SLOT_CFUNC
,
js_mkfun
(
builtin_Set
));
js_mkprop_fast
(
js
,
set_ctor
,
"prototype"
,
9
,
set_proto
);
js_mkprop_fast
(
js
,
set_ctor
,
"name"
,
4
,
ANT_STRING
(
"Set"
));
js_set_descriptor
(
js
,
set_ctor
,
"name"
,
4
,
0
);
js_define_species_getter
(
js
,
set_ctor
);
js_set
(
js
,
glob
,
"Set"
,
js_obj_to_func
(
set_ctor
));
ant_value_t
weakmap_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
weakmap_proto
,
object_proto
);
js_set
(
js
,
weakmap_proto
,
"set"
,
js_mkfun
(
weakmap_set
));
js_set
(
js
,
weakmap_proto
,
"get"
,
js_mkfun
(
weakmap_get
));
js_set
(
js
,
weakmap_proto
,
"has"
,
js_mkfun
(
weakmap_has
));
js_set
(
js
,
weakmap_proto
,
"upsert"
,
js_mkfun
(
weakmap_upsert
));
js_set
(
js
,
weakmap_proto
,
"delete"
,
js_mkfun
(
weakmap_delete
));
js_set_sym
(
js
,
weakmap_proto
,
tag_sym
,
js_mkstr
(
js
,
"WeakMap"
,
7
));
ant_value_t
weakmap_ctor
=
js_mkobj
(
js
);
js_set_slot
(
weakmap_ctor
,
SLOT_CFUNC
,
js_mkfun
(
builtin_WeakMap
));
js_mkprop_fast
(
js
,
weakmap_ctor
,
"prototype"
,
9
,
weakmap_proto
);
js_mkprop_fast
(
js
,
weakmap_ctor
,
"name"
,
4
,
ANT_STRING
(
"WeakMap"
));
js_set_descriptor
(
js
,
weakmap_ctor
,
"name"
,
4
,
0
);
js_set
(
js
,
glob
,
"WeakMap"
,
js_obj_to_func
(
weakmap_ctor
));
ant_value_t
weakset_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
weakset_proto
,
object_proto
);
js_set
(
js
,
weakset_proto
,
"add"
,
js_mkfun
(
weakset_add
));
js_set
(
js
,
weakset_proto
,
"has"
,
js_mkfun
(
weakset_has
));
js_set
(
js
,
weakset_proto
,
"delete"
,
js_mkfun
(
weakset_delete
));
js_set_sym
(
js
,
weakset_proto
,
tag_sym
,
js_mkstr
(
js
,
"WeakSet"
,
7
));
ant_value_t
weakset_ctor
=
js_mkobj
(
js
);
js_set_slot
(
weakset_ctor
,
SLOT_CFUNC
,
js_mkfun
(
builtin_WeakSet
));
js_mkprop_fast
(
js
,
weakset_ctor
,
"prototype"
,
9
,
weakset_proto
);
js_mkprop_fast
(
js
,
weakset_ctor
,
"name"
,
4
,
ANT_STRING
(
"WeakSet"
));
js_set_descriptor
(
js
,
weakset_ctor
,
"name"
,
4
,
0
);
js_set
(
js
,
glob
,
"WeakSet"
,
js_obj_to_func
(
weakset_ctor
));
ant_value_t
weakref_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
weakref_proto
,
object_proto
);
js_set
(
js
,
weakref_proto
,
"deref"
,
js_mkfun
(
weakref_deref
));
js_set_sym
(
js
,
weakref_proto
,
tag_sym
,
js_mkstr
(
js
,
"WeakRef"
,
7
));
ant_value_t
weakref_ctor
=
js_mkobj
(
js
);
js_set_slot
(
weakref_ctor
,
SLOT_CFUNC
,
js_mkfun
(
builtin_WeakRef
));
js_mkprop_fast
(
js
,
weakref_ctor
,
"prototype"
,
9
,
weakref_proto
);
js_mkprop_fast
(
js
,
weakref_ctor
,
"name"
,
4
,
ANT_STRING
(
"WeakRef"
));
js_set_descriptor
(
js
,
weakref_ctor
,
"name"
,
4
,
0
);
js_set
(
js
,
glob
,
"WeakRef"
,
js_obj_to_func
(
weakref_ctor
));
ant_value_t
finreg_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
finreg_proto
,
object_proto
);
js_set
(
js
,
finreg_proto
,
"register"
,
js_mkfun
(
finreg_register
));
js_set
(
js
,
finreg_proto
,
"unregister"
,
js_mkfun
(
finreg_unregister
));
js_set_sym
(
js
,
finreg_proto
,
tag_sym
,
js_mkstr
(
js
,
"FinalizationRegistry"
,
20
));
ant_value_t
finreg_ctor
=
js_mkobj
(
js
);
js_set_slot
(
finreg_ctor
,
SLOT_CFUNC
,
js_mkfun
(
builtin_FinalizationRegistry
));
js_mkprop_fast
(
js
,
finreg_ctor
,
"prototype"
,
9
,
finreg_proto
);
js_mkprop_fast
(
js
,
finreg_ctor
,
"name"
,
4
,
ANT_STRING
(
"FinalizationRegistry"
));
js_set_descriptor
(
js
,
finreg_ctor
,
"name"
,
4
,
0
);
js_set
(
js
,
glob
,
"FinalizationRegistry"
,
js_obj_to_func
(
finreg_ctor
));
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 8:29 AM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
543265
Default Alt Text
collections.c (56 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment