Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4435323
iterator.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
46 KB
Referenced Files
None
Subscribers
None
iterator.c
View Options
#include
<stdlib.h>
#include
<string.h>
#include
"ant.h"
#include
"errors.h"
#include
"runtime.h"
#include
"internal.h"
#include
"ptr.h"
#include
"silver/engine.h"
#include
"descriptors.h"
#include
"modules/assert.h"
#include
"modules/iterator.h"
#include
"modules/symbol.h"
enum
{
WRAP_MAP
=
0
,
WRAP_FILTER
=
1
,
WRAP_TAKE
=
2
,
WRAP_DROP
=
3
,
WRAP_FLATMAP
=
4
,
WRAP_PASS
=
5
,
WRAP_FROM_SYNC
=
6
,
};
enum
{
ASYNC_TERM_EVERY
=
0
,
ASYNC_TERM_SOME
=
1
,
ASYNC_TERM_FIND
=
2
,
ASYNC_TERM_FOREACH
=
3
,
ASYNC_TERM_REDUCE
=
4
,
ASYNC_TERM_TOARRAY
=
5
,
};
static
ant_value_t
g_wrap_iter_proto
=
0
;
static
ant_value_t
g_async_wrap_iter_proto
=
0
;
enum
{
ASYNC_TERMINAL_STATE_TAG
=
0x41544954u
};
// ATIT
typedef
struct
{
double
index
;
int
mode
;
bool
has_acc
;
}
async_terminal_state_t
;
static
inline
ant_value_t
call_indexed_callback
(
ant_t
*
js
,
ant_value_t
fn
,
ant_value_t
value
,
double
index
)
{
ant_value_t
call_args
[
2
]
=
{
value
,
js_mknum
(
index
)
};
return
sv_vm_call
(
js
->
vm
,
js
,
fn
,
js_mkundef
(),
call_args
,
2
,
NULL
,
false
);
}
static
inline
ant_value_t
set_iter_result
(
ant_t
*
js
,
ant_value_t
result
,
ant_value_t
value
,
bool
done
)
{
js_set
(
js
,
result
,
"done"
,
done
?
js_true
:
js_false
);
js_set
(
js
,
result
,
"value"
,
value
);
return
result
;
}
static
ant_value_t
wrap_iter_next
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
self
=
js
->
this_val
;
ant_value_t
source
=
js_get_slot
(
self
,
SLOT_DATA
);
ant_value_t
state_v
=
js_get_slot
(
self
,
SLOT_ITER_STATE
);
uint32_t
state
=
(
vtype
(
state_v
)
==
T_NUM
)
?
(
uint32_t
)
js_getnum
(
state_v
)
:
0
;
uint32_t
kind
=
ITER_STATE_KIND
(
state
);
uint32_t
count
=
ITER_STATE_INDEX
(
state
);
ant_value_t
result
=
js_mkobj
(
js
);
ant_value_t
cb
=
js_get_slot
(
self
,
SLOT_CTOR
);
ant_value_t
next_fn
=
js_getprop_fallback
(
js
,
source
,
"next"
);
for
(;;)
{
if
(
kind
==
WRAP_FLATMAP
)
{
ant_value_t
inner
=
js_get_slot
(
self
,
SLOT_ENTRIES
);
if
(
vtype
(
inner
)
!=
T_UNDEF
)
{
ant_value_t
inner_next
=
js_getprop_fallback
(
js
,
inner
,
"next"
);
ant_value_t
inner_step
=
sv_vm_call
(
js
->
vm
,
js
,
inner_next
,
inner
,
NULL
,
0
,
NULL
,
false
);
if
(
!
is_err
(
inner_step
))
{
ant_value_t
inner_done
=
js_getprop_fallback
(
js
,
inner_step
,
"done"
);
if
(
!
js_truthy
(
js
,
inner_done
))
return
set_iter_result
(
js
,
result
,
js_getprop_fallback
(
js
,
inner_step
,
"value"
),
false
);
}
js_set_slot
(
self
,
SLOT_ENTRIES
,
js_mkundef
());
}}
ant_value_t
step
;
if
(
vtype
(
next_fn
)
==
T_CFUNC
)
{
ant_value_t
old_this
=
js
->
this_val
;
js
->
this_val
=
source
;
step
=
js_as_cfunc
(
next_fn
)(
js
,
NULL
,
0
);
js
->
this_val
=
old_this
;
}
else
step
=
sv_vm_call
(
js
->
vm
,
js
,
next_fn
,
source
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
step
))
return
step
;
ant_value_t
done
=
js_getprop_fallback
(
js
,
step
,
"done"
);
if
(
js_truthy
(
js
,
done
))
{
if
(
kind
==
WRAP_FLATMAP
)
{
ant_value_t
inner
=
js_get_slot
(
self
,
SLOT_ENTRIES
);
if
(
vtype
(
inner
)
!=
T_UNDEF
)
{
js_set_slot
(
self
,
SLOT_ENTRIES
,
js_mkundef
());
}}
return
set_iter_result
(
js
,
result
,
js_mkundef
(),
true
);
}
ant_value_t
value
=
js_getprop_fallback
(
js
,
step
,
"value"
);
switch
(
kind
)
{
case
WRAP_MAP
:
{
ant_value_t
out_val
;
if
(
is_callable
(
cb
))
{
out_val
=
call_indexed_callback
(
js
,
cb
,
value
,
(
double
)
count
);
if
(
is_err
(
out_val
))
return
out_val
;
}
else
out_val
=
value
;
count
++
;
js_set_slot
(
self
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
)));
return
set_iter_result
(
js
,
result
,
out_val
,
false
);
}
case
WRAP_FILTER
:
{
ant_value_t
test
=
call_indexed_callback
(
js
,
cb
,
value
,
(
double
)
count
);
if
(
is_err
(
test
))
return
test
;
count
++
;
js_set_slot
(
self
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
)));
if
(
js_truthy
(
js
,
test
))
{
return
set_iter_result
(
js
,
result
,
value
,
false
);
}
continue
;
}
case
WRAP_TAKE
:
{
uint32_t
limit
=
(
vtype
(
cb
)
==
T_NUM
)
?
(
uint32_t
)
js_getnum
(
cb
)
:
0
;
if
(
count
>=
limit
)
{
return
set_iter_result
(
js
,
result
,
js_mkundef
(),
true
);
}
js_set_slot
(
self
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
+
1
)));
return
set_iter_result
(
js
,
result
,
value
,
false
);
}
case
WRAP_DROP
:
{
uint32_t
limit
=
(
vtype
(
cb
)
==
T_NUM
)
?
(
uint32_t
)
js_getnum
(
cb
)
:
0
;
count
++
;
js_set_slot
(
self
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
)));
if
(
count
<=
limit
)
continue
;
return
set_iter_result
(
js
,
result
,
value
,
false
);
}
case
WRAP_FLATMAP
:
{
ant_value_t
mapped
=
call_indexed_callback
(
js
,
cb
,
value
,
(
double
)
count
);
if
(
is_err
(
mapped
))
return
mapped
;
count
++
;
js_set_slot
(
self
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
)));
ant_value_t
iter_fn
=
js_get_sym
(
js
,
mapped
,
get_iterator_sym
());
if
(
!
is_callable
(
iter_fn
))
{
return
set_iter_result
(
js
,
result
,
mapped
,
false
);
}
ant_value_t
inner
=
sv_vm_call
(
js
->
vm
,
js
,
iter_fn
,
mapped
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
inner
))
return
inner
;
ant_value_t
inner_next
=
js_getprop_fallback
(
js
,
inner
,
"next"
);
ant_value_t
inner_step
=
sv_vm_call
(
js
->
vm
,
js
,
inner_next
,
inner
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
inner_step
))
return
inner_step
;
ant_value_t
inner_done
=
js_getprop_fallback
(
js
,
inner_step
,
"done"
);
if
(
!
js_truthy
(
js
,
inner_done
))
{
js_set_slot_wb
(
js
,
self
,
SLOT_ENTRIES
,
inner
);
return
set_iter_result
(
js
,
result
,
js_getprop_fallback
(
js
,
inner_step
,
"value"
),
false
);
}
continue
;
}
default
:
return
set_iter_result
(
js
,
result
,
value
,
false
);
}
}
}
static
ant_value_t
make_wrap_iter
(
ant_t
*
js
,
ant_value_t
source
,
int
kind
,
ant_value_t
cb
)
{
ant_value_t
iter
=
js_mkobj
(
js
);
js_set_proto_init
(
iter
,
g_wrap_iter_proto
);
js_set_slot_wb
(
js
,
iter
,
SLOT_DATA
,
source
);
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
0
)));
js_set_slot_wb
(
js
,
iter
,
SLOT_CTOR
,
cb
);
return
iter
;
}
static
ant_value_t
get_source_iter
(
ant_t
*
js
)
{
ant_value_t
self
=
js
->
this_val
;
ant_value_t
next
=
js_getprop_fallback
(
js
,
self
,
"next"
);
if
(
is_callable
(
next
))
return
self
;
ant_value_t
iter_fn
=
js_get_sym
(
js
,
self
,
get_iterator_sym
());
if
(
!
is_callable
(
iter_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
return
sv_vm_call
(
js
->
vm
,
js
,
iter_fn
,
self
,
NULL
,
0
,
NULL
,
false
);
}
static
ant_value_t
iter_make_helper
(
ant_t
*
js
,
int
kind
,
ant_value_t
cb
)
{
ant_value_t
source
=
get_source_iter
(
js
);
if
(
is_err
(
source
))
return
source
;
return
make_wrap_iter
(
js
,
source
,
kind
,
cb
);
}
static
ant_value_t
iter_make_callable_helper
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
,
int
kind
,
const
char
*
method
)
{
if
(
nargs
<
1
||
!
is_callable
(
args
[
0
]))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"%s requires a callable"
,
method
);
return
iter_make_helper
(
js
,
kind
,
args
[
0
]);
}
static
ant_value_t
iter_make_count_helper
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
,
int
kind
,
const
char
*
method
)
{
double
limit
=
(
nargs
>=
1
&&
vtype
(
args
[
0
])
==
T_NUM
)
?
js_getnum
(
args
[
0
])
:
0
;
if
(
limit
<
0
)
return
js_mkerr
(
js
,
"%s requires a non-negative number"
,
method
);
return
iter_make_helper
(
js
,
kind
,
js_mknum
(
limit
));
}
static
ant_value_t
iter_map
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
iter_make_callable_helper
(
js
,
args
,
nargs
,
WRAP_MAP
,
"Iterator.prototype.map"
);
}
static
ant_value_t
iter_filter
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
iter_make_callable_helper
(
js
,
args
,
nargs
,
WRAP_FILTER
,
"Iterator.prototype.filter"
);
}
static
ant_value_t
iter_take
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
iter_make_count_helper
(
js
,
args
,
nargs
,
WRAP_TAKE
,
"Iterator.prototype.take"
);
}
static
ant_value_t
iter_drop
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
iter_make_count_helper
(
js
,
args
,
nargs
,
WRAP_DROP
,
"Iterator.prototype.drop"
);
}
static
ant_value_t
iter_flatMap
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
iter_make_callable_helper
(
js
,
args
,
nargs
,
WRAP_FLATMAP
,
"Iterator.prototype.flatMap"
);
}
static
ant_value_t
iter_every
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
!
is_callable
(
args
[
0
]))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator.prototype.every requires a callable"
);
ant_value_t
fn
=
args
[
0
];
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
js
->
this_val
,
&
it
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
ant_value_t
value
;
uint32_t
counter
=
0
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
{
ant_value_t
test
=
call_indexed_callback
(
js
,
fn
,
value
,
(
double
)
counter
++
);
if
(
is_err
(
test
))
{
js_iter_close
(
js
,
&
it
);
return
test
;
}
if
(
!
js_truthy
(
js
,
test
))
{
js_iter_close
(
js
,
&
it
);
return
js_false
;
}
}
return
js_true
;
}
static
ant_value_t
iter_some
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
!
is_callable
(
args
[
0
]))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator.prototype.some requires a callable"
);
ant_value_t
fn
=
args
[
0
];
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
js
->
this_val
,
&
it
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
ant_value_t
value
;
uint32_t
counter
=
0
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
{
ant_value_t
test
=
call_indexed_callback
(
js
,
fn
,
value
,
(
double
)
counter
++
);
if
(
is_err
(
test
))
{
js_iter_close
(
js
,
&
it
);
return
test
;
}
if
(
js_truthy
(
js
,
test
))
{
js_iter_close
(
js
,
&
it
);
return
js_true
;
}
}
return
js_false
;
}
static
ant_value_t
iter_find
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
!
is_callable
(
args
[
0
]))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator.prototype.find requires a callable"
);
ant_value_t
fn
=
args
[
0
];
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
js
->
this_val
,
&
it
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
ant_value_t
value
;
uint32_t
counter
=
0
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
{
ant_value_t
test
=
call_indexed_callback
(
js
,
fn
,
value
,
(
double
)
counter
++
);
if
(
is_err
(
test
))
{
js_iter_close
(
js
,
&
it
);
return
test
;
}
if
(
js_truthy
(
js
,
test
))
{
js_iter_close
(
js
,
&
it
);
return
value
;
}
}
return
js_mkundef
();
}
static
ant_value_t
iter_forEach
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
!
is_callable
(
args
[
0
]))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator.prototype.forEach requires a callable"
);
ant_value_t
fn
=
args
[
0
];
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
js
->
this_val
,
&
it
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
ant_value_t
value
;
uint32_t
counter
=
0
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
{
ant_value_t
r
=
call_indexed_callback
(
js
,
fn
,
value
,
(
double
)
counter
++
);
if
(
is_err
(
r
))
{
js_iter_close
(
js
,
&
it
);
return
r
;
}
}
return
js_mkundef
();
}
static
ant_value_t
iter_reduce
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
!
is_callable
(
args
[
0
]))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator.prototype.reduce requires a callable"
);
ant_value_t
fn
=
args
[
0
];
bool
has_init
=
(
nargs
>=
2
);
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
js
->
this_val
,
&
it
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
ant_value_t
acc
=
has_init
?
args
[
1
]
:
js_mkundef
();
bool
first
=
!
has_init
;
ant_value_t
value
;
uint32_t
counter
=
0
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
{
if
(
first
)
{
acc
=
value
;
first
=
false
;
counter
++
;
continue
;
}
ant_value_t
call_args
[
3
]
=
{
acc
,
value
,
js_mknum
((
double
)
counter
++
)
};
acc
=
sv_vm_call
(
js
->
vm
,
js
,
fn
,
js_mkundef
(),
call_args
,
3
,
NULL
,
false
);
if
(
is_err
(
acc
))
{
js_iter_close
(
js
,
&
it
);
return
acc
;
}
}
if
(
first
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"reduce of empty iterator with no initial value"
);
return
acc
;
}
static
ant_value_t
iter_toArray
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
js_iter_t
it
;
if
(
!
js_iter_open
(
js
,
js
->
this_val
,
&
it
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
ant_value_t
arr
=
js_mkarr
(
js
);
ant_value_t
value
;
while
(
js_iter_next
(
js
,
&
it
,
&
value
))
js_arr_push
(
js
,
arr
,
value
);
return
arr
;
}
static
ant_value_t
iter_from
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator.from requires an argument"
);
ant_value_t
obj
=
args
[
0
];
ant_value_t
next
=
js_getprop_fallback
(
js
,
obj
,
"next"
);
if
(
is_callable
(
next
))
{
return
make_wrap_iter
(
js
,
obj
,
WRAP_MAP
,
js_mkundef
());
}
ant_value_t
iter_fn
=
js_get_sym
(
js
,
obj
,
get_iterator_sym
());
if
(
!
is_callable
(
iter_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not iterable"
);
ant_value_t
iterator
=
sv_vm_call
(
js
->
vm
,
js
,
iter_fn
,
obj
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
iterator
))
return
iterator
;
return
iterator
;
}
static
ant_value_t
iter_ctor
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator is not directly constructable"
);
ant_value_t
obj
=
js_mkobj
(
js
);
ant_value_t
proto
=
js_instance_proto_from_new_target
(
js
,
js
->
sym
.
iterator_proto
);
if
(
is_object_type
(
proto
))
js_set_proto_init
(
obj
,
proto
);
return
obj
;
}
static
ant_value_t
async_iter_ctor
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"AsyncIterator constructor requires 'new'"
);
ant_value_t
obj
=
js_mkobj
(
js
);
ant_value_t
proto
=
js_instance_proto_from_new_target
(
js
,
js
->
sym
.
async_iterator_proto
);
if
(
is_object_type
(
proto
))
js_set_proto_init
(
obj
,
proto
);
return
obj
;
}
static
inline
ant_value_t
iter_result
(
ant_t
*
js
,
ant_value_t
value
,
bool
done
)
{
ant_value_t
result
=
js_mkobj
(
js
);
js_set
(
js
,
result
,
"done"
,
done
?
js_true
:
js_false
);
js_set
(
js
,
result
,
"value"
,
value
);
return
result
;
}
static
inline
ant_value_t
fulfilled_promise
(
ant_t
*
js
,
ant_value_t
value
)
{
ant_value_t
promise
=
js_mkpromise
(
js
);
js_resolve_promise
(
js
,
promise
,
value
);
return
promise
;
}
static
inline
ant_value_t
rejected_promise
(
ant_t
*
js
,
ant_value_t
reason
)
{
ant_value_t
promise
=
js_mkpromise
(
js
);
js_reject_promise
(
js
,
promise
,
reason
);
return
promise
;
}
static
inline
ant_value_t
promise_from_call_result
(
ant_t
*
js
,
ant_value_t
result
)
{
if
(
vtype
(
result
)
==
T_PROMISE
)
return
result
;
if
(
is_err
(
result
))
return
rejected_promise
(
js
,
result
);
return
fulfilled_promise
(
js
,
result
);
}
static
ant_value_t
async_iter_call_method
(
ant_t
*
js
,
ant_value_t
receiver
,
const
char
*
name
,
ant_value_t
*
args
,
int
nargs
,
bool
*
missing
)
{
ant_value_t
fn
=
js_getprop_fallback
(
js
,
receiver
,
name
);
if
(
missing
)
*
missing
=
!
is_callable
(
fn
);
if
(
!
is_callable
(
fn
))
return
js_mkundef
();
return
sv_vm_call
(
js
->
vm
,
js
,
fn
,
receiver
,
args
,
nargs
,
NULL
,
false
);
}
static
ant_value_t
make_async_wrap_iter
(
ant_t
*
js
,
ant_value_t
source
,
int
kind
,
ant_value_t
cb
)
{
ant_value_t
iter
=
js_mkobj
(
js
);
js_set_proto_init
(
iter
,
g_async_wrap_iter_proto
);
js_set_slot_wb
(
js
,
iter
,
SLOT_DATA
,
source
);
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
0
)));
js_set_slot_wb
(
js
,
iter
,
SLOT_CTOR
,
cb
);
js_set_slot
(
iter
,
SLOT_ENTRIES
,
js_mkundef
());
return
iter
;
}
static
ant_value_t
async_wrap_advance
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
);
static
ant_value_t
async_wrap_handle_step
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
,
ant_value_t
step
);
static
ant_value_t
async_wrap_handle_callback_result
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
,
uint32_t
kind
,
ant_value_t
value
,
ant_value_t
result
);
static
ant_value_t
async_wrap_on_step
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
iter
=
js_get
(
js
,
state
,
"iter"
);
ant_value_t
promise
=
js_get
(
js
,
state
,
"promise"
);
ant_value_t
step
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
return
async_wrap_handle_step
(
js
,
iter
,
promise
,
step
);
}
static
ant_value_t
async_wrap_on_reject
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
promise
=
js_get
(
js
,
state
,
"promise"
);
js_reject_promise
(
js
,
promise
,
nargs
>
0
?
args
[
0
]
:
js_mkundef
());
return
js_mkundef
();
}
static
ant_value_t
async_wrap_on_callback
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
iter
=
js_get
(
js
,
state
,
"iter"
);
ant_value_t
promise
=
js_get
(
js
,
state
,
"promise"
);
uint32_t
kind
=
(
uint32_t
)
js_getnum
(
js_get
(
js
,
state
,
"kind"
));
ant_value_t
value
=
js_get
(
js
,
state
,
"value"
);
ant_value_t
result
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
return
async_wrap_handle_callback_result
(
js
,
iter
,
promise
,
kind
,
value
,
result
);
}
static
ant_value_t
async_wrap_on_sync_value
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
promise
=
js_get
(
js
,
state
,
"promise"
);
bool
done
=
js_truthy
(
js
,
js_get
(
js
,
state
,
"done"
));
ant_value_t
value
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
value
,
done
));
return
js_mkundef
();
}
static
bool
async_wrap_chain_step
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
,
ant_value_t
next_result
)
{
if
(
is_err
(
next_result
))
{
js_reject_promise
(
js
,
promise
,
next_result
);
return
true
;
}
if
(
vtype
(
next_result
)
!=
T_PROMISE
)
{
async_wrap_handle_step
(
js
,
iter
,
promise
,
next_result
);
return
true
;
}
ant_value_t
state
=
js_mkobj
(
js
);
js_set
(
js
,
state
,
"iter"
,
iter
);
js_set
(
js
,
state
,
"promise"
,
promise
);
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_wrap_on_step
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_wrap_on_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
next_result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
true
;
}
static
ant_value_t
async_wrap_handle_inner_step
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
,
ant_value_t
step
)
{
if
(
!
is_object_type
(
step
))
{
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"iterator result is not an object"
));
return
js_mkundef
();
}
if
(
!
js_truthy
(
js
,
js_getprop_fallback
(
js
,
step
,
"done"
)))
{
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
js_getprop_fallback
(
js
,
step
,
"value"
),
false
));
return
js_mkundef
();
}
js_set_slot
(
iter
,
SLOT_ENTRIES
,
js_mkundef
());
async_wrap_advance
(
js
,
iter
,
promise
);
return
js_mkundef
();
}
static
ant_value_t
async_wrap_on_inner_step
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
iter
=
js_get
(
js
,
state
,
"iter"
);
ant_value_t
promise
=
js_get
(
js
,
state
,
"promise"
);
ant_value_t
step
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
return
async_wrap_handle_inner_step
(
js
,
iter
,
promise
,
step
);
}
static
ant_value_t
async_wrap_advance_inner
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
)
{
ant_value_t
inner
=
js_get_slot
(
iter
,
SLOT_ENTRIES
);
if
(
vtype
(
inner
)
==
T_UNDEF
||
vtype
(
inner
)
==
T_NULL
)
return
async_wrap_advance
(
js
,
iter
,
promise
);
bool
missing
=
false
;
ant_value_t
next_result
=
async_iter_call_method
(
js
,
inner
,
"next"
,
NULL
,
0
,
&
missing
);
if
(
missing
)
{
js_set_slot
(
iter
,
SLOT_ENTRIES
,
js_mkundef
());
return
async_wrap_advance
(
js
,
iter
,
promise
);
}
if
(
is_err
(
next_result
))
{
js_reject_promise
(
js
,
promise
,
next_result
);
return
js_mkundef
();
}
if
(
vtype
(
next_result
)
==
T_PROMISE
)
{
ant_value_t
state
=
js_mkobj
(
js
);
js_set
(
js
,
state
,
"iter"
,
iter
);
js_set
(
js
,
state
,
"promise"
,
promise
);
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_wrap_on_inner_step
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_wrap_on_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
next_result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
js_mkundef
();
}
return
async_wrap_handle_inner_step
(
js
,
iter
,
promise
,
next_result
);
}
static
ant_value_t
async_wrap_handle_step
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
,
ant_value_t
step
)
{
if
(
!
is_object_type
(
step
))
{
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"iterator result is not an object"
));
return
js_mkundef
();
}
bool
done
=
js_truthy
(
js
,
js_getprop_fallback
(
js
,
step
,
"done"
));
ant_value_t
state_v
=
js_get_slot
(
iter
,
SLOT_ITER_STATE
);
uint32_t
state
=
(
vtype
(
state_v
)
==
T_NUM
)
?
(
uint32_t
)
js_getnum
(
state_v
)
:
0
;
uint32_t
kind
=
ITER_STATE_KIND
(
state
);
uint32_t
count
=
ITER_STATE_INDEX
(
state
);
ant_value_t
value
=
js_getprop_fallback
(
js
,
step
,
"value"
);
if
(
kind
==
WRAP_FROM_SYNC
&&
vtype
(
value
)
==
T_PROMISE
)
{
ant_value_t
state_obj
=
js_mkobj
(
js
);
js_set
(
js
,
state_obj
,
"promise"
,
promise
);
js_set
(
js
,
state_obj
,
"done"
,
done
?
js_true
:
js_false
);
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_wrap_on_sync_value
,
state_obj
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_wrap_on_reject
,
state_obj
);
ant_value_t
then_result
=
js_promise_then
(
js
,
value
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
js_mkundef
();
}
if
(
done
)
{
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
kind
==
WRAP_FROM_SYNC
?
value
:
js_mkundef
(),
true
)
);
return
js_mkundef
();
}
ant_value_t
cb
=
js_get_slot
(
iter
,
SLOT_CTOR
);
switch
(
kind
)
{
case
WRAP_MAP
:
{
ant_value_t
mapped
=
call_indexed_callback
(
js
,
cb
,
value
,
(
double
)
count
);
if
(
is_err
(
mapped
))
{
js_reject_promise
(
js
,
promise
,
mapped
);
return
js_mkundef
();
}
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
+
1
)));
return
async_wrap_handle_callback_result
(
js
,
iter
,
promise
,
kind
,
value
,
mapped
);
}
case
WRAP_FILTER
:
{
ant_value_t
test
=
call_indexed_callback
(
js
,
cb
,
value
,
(
double
)
count
);
if
(
is_err
(
test
))
{
js_reject_promise
(
js
,
promise
,
test
);
return
js_mkundef
();
}
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
+
1
)));
return
async_wrap_handle_callback_result
(
js
,
iter
,
promise
,
kind
,
value
,
test
);
}
case
WRAP_TAKE
:
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
+
1
)));
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
value
,
false
));
return
js_mkundef
();
case
WRAP_DROP
:
{
double
limit
=
(
vtype
(
cb
)
==
T_NUM
)
?
js_getnum
(
cb
)
:
0
;
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
+
1
)));
if
((
double
)(
count
+
1
)
<=
limit
)
async_wrap_advance
(
js
,
iter
,
promise
);
else
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
value
,
false
));
return
js_mkundef
();
}
case
WRAP_FLATMAP
:
{
ant_value_t
mapped
=
call_indexed_callback
(
js
,
cb
,
value
,
(
double
)
count
);
if
(
is_err
(
mapped
))
{
js_reject_promise
(
js
,
promise
,
mapped
);
return
js_mkundef
();
}
js_set_slot
(
iter
,
SLOT_ITER_STATE
,
js_mknum
((
double
)
ITER_STATE_PACK
(
kind
,
count
+
1
)));
return
async_wrap_handle_callback_result
(
js
,
iter
,
promise
,
kind
,
value
,
mapped
);
}
default
:
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
value
,
false
));
return
js_mkundef
();
}
}
static
ant_value_t
async_wrap_handle_callback_result
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
,
uint32_t
kind
,
ant_value_t
value
,
ant_value_t
result
)
{
if
(
vtype
(
result
)
==
T_PROMISE
)
{
ant_value_t
state
=
js_mkobj
(
js
);
js_set
(
js
,
state
,
"iter"
,
iter
);
js_set
(
js
,
state
,
"promise"
,
promise
);
js_set
(
js
,
state
,
"kind"
,
js_mknum
((
double
)
kind
));
js_set
(
js
,
state
,
"value"
,
value
);
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_wrap_on_callback
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_wrap_on_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
js_mkundef
();
}
if
(
kind
==
WRAP_MAP
)
{
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
result
,
false
));
return
js_mkundef
();
}
if
(
kind
==
WRAP_FILTER
)
{
if
(
js_truthy
(
js
,
result
))
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
value
,
false
));
else
async_wrap_advance
(
js
,
iter
,
promise
);
return
js_mkundef
();
}
if
(
kind
==
WRAP_FLATMAP
)
{
ant_value_t
iter_fn
=
js_get_sym
(
js
,
result
,
get_asyncIterator_sym
());
if
(
!
is_callable
(
iter_fn
))
iter_fn
=
js_get_sym
(
js
,
result
,
get_iterator_sym
());
if
(
!
is_callable
(
iter_fn
))
{
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
result
,
false
));
return
js_mkundef
();
}
ant_value_t
inner
=
sv_vm_call
(
js
->
vm
,
js
,
iter_fn
,
result
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
inner
))
{
js_reject_promise
(
js
,
promise
,
inner
);
return
js_mkundef
();
}
js_set_slot_wb
(
js
,
iter
,
SLOT_ENTRIES
,
inner
);
return
async_wrap_advance_inner
(
js
,
iter
,
promise
);
}
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
result
,
false
));
return
js_mkundef
();
}
static
ant_value_t
async_wrap_advance
(
ant_t
*
js
,
ant_value_t
iter
,
ant_value_t
promise
)
{
ant_value_t
state_v
=
js_get_slot
(
iter
,
SLOT_ITER_STATE
);
uint32_t
state
=
(
vtype
(
state_v
)
==
T_NUM
)
?
(
uint32_t
)
js_getnum
(
state_v
)
:
0
;
uint32_t
kind
=
ITER_STATE_KIND
(
state
);
ant_value_t
cb
=
js_get_slot
(
iter
,
SLOT_CTOR
);
if
(
kind
==
WRAP_TAKE
)
{
double
limit
=
(
vtype
(
cb
)
==
T_NUM
)
?
js_getnum
(
cb
)
:
0
;
if
((
double
)
ITER_STATE_INDEX
(
state
)
>=
limit
)
{
js_resolve_promise
(
js
,
promise
,
iter_result
(
js
,
js_mkundef
(),
true
));
return
js_mkundef
();
}
}
if
(
kind
==
WRAP_FLATMAP
&&
vtype
(
js_get_slot
(
iter
,
SLOT_ENTRIES
))
!=
T_UNDEF
)
return
async_wrap_advance_inner
(
js
,
iter
,
promise
);
ant_value_t
source
=
js_get_slot
(
iter
,
SLOT_DATA
);
bool
missing
=
false
;
ant_value_t
next_result
=
async_iter_call_method
(
js
,
source
,
"next"
,
NULL
,
0
,
&
missing
);
if
(
missing
)
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not async iterable"
));
else
async_wrap_chain_step
(
js
,
iter
,
promise
,
next_result
);
return
js_mkundef
();
}
static
ant_value_t
async_wrap_next
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
(
void
)
args
;
(
void
)
nargs
;
ant_value_t
promise
=
js_mkpromise
(
js
);
async_wrap_advance
(
js
,
js
->
this_val
,
promise
);
return
promise
;
}
static
ant_value_t
async_wrap_return
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
source
=
js_get_slot
(
js
->
this_val
,
SLOT_DATA
);
bool
missing
=
false
;
ant_value_t
result
=
async_iter_call_method
(
js
,
source
,
"return"
,
args
,
nargs
,
&
missing
);
if
(
missing
)
return
fulfilled_promise
(
js
,
iter_result
(
js
,
nargs
>
0
?
args
[
0
]
:
js_mkundef
(),
true
));
ant_value_t
state_v
=
js_get_slot
(
js
->
this_val
,
SLOT_ITER_STATE
);
uint32_t
state
=
(
vtype
(
state_v
)
==
T_NUM
)
?
(
uint32_t
)
js_getnum
(
state_v
)
:
0
;
if
(
ITER_STATE_KIND
(
state
)
==
WRAP_FROM_SYNC
)
{
ant_value_t
promise
=
js_mkpromise
(
js
);
async_wrap_chain_step
(
js
,
js
->
this_val
,
promise
,
result
);
return
promise
;
}
return
promise_from_call_result
(
js
,
result
);
}
static
ant_value_t
async_wrap_throw
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
source
=
js_get_slot
(
js
->
this_val
,
SLOT_DATA
);
bool
missing
=
false
;
ant_value_t
result
=
async_iter_call_method
(
js
,
source
,
"throw"
,
args
,
nargs
,
&
missing
);
if
(
missing
)
return
rejected_promise
(
js
,
nargs
>
0
?
args
[
0
]
:
js_mkundef
());
ant_value_t
state_v
=
js_get_slot
(
js
->
this_val
,
SLOT_ITER_STATE
);
uint32_t
state
=
(
vtype
(
state_v
)
==
T_NUM
)
?
(
uint32_t
)
js_getnum
(
state_v
)
:
0
;
if
(
ITER_STATE_KIND
(
state
)
==
WRAP_FROM_SYNC
)
{
ant_value_t
promise
=
js_mkpromise
(
js
);
async_wrap_chain_step
(
js
,
js
->
this_val
,
promise
,
result
);
return
promise
;
}
return
promise_from_call_result
(
js
,
result
);
}
static
ant_value_t
async_iter_from
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
if
(
nargs
<
1
||
vtype
(
args
[
0
])
==
T_UNDEF
||
vtype
(
args
[
0
])
==
T_NULL
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"AsyncIterator.from requires an object"
);
ant_value_t
obj
=
args
[
0
];
ant_value_t
iter_fn
=
js_get_sym
(
js
,
obj
,
get_asyncIterator_sym
());
if
(
is_callable
(
iter_fn
))
{
ant_value_t
iterator
=
sv_vm_call
(
js
->
vm
,
js
,
iter_fn
,
obj
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
iterator
))
return
iterator
;
return
make_async_wrap_iter
(
js
,
iterator
,
WRAP_PASS
,
js_mkundef
());
}
iter_fn
=
js_get_sym
(
js
,
obj
,
get_iterator_sym
());
if
(
is_callable
(
iter_fn
))
{
ant_value_t
iterator
=
sv_vm_call
(
js
->
vm
,
js
,
iter_fn
,
obj
,
NULL
,
0
,
NULL
,
false
);
if
(
is_err
(
iterator
))
return
iterator
;
return
make_async_wrap_iter
(
js
,
iterator
,
WRAP_FROM_SYNC
,
js_mkundef
());
}
ant_value_t
next
=
js_getprop_fallback
(
js
,
obj
,
"next"
);
if
(
is_callable
(
next
))
return
make_async_wrap_iter
(
js
,
obj
,
WRAP_FROM_SYNC
,
js_mkundef
());
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not async iterable"
);
}
static
ant_value_t
get_async_source_iter
(
ant_t
*
js
)
{
ant_value_t
self
=
js
->
this_val
;
ant_value_t
next
=
js_getprop_fallback
(
js
,
self
,
"next"
);
if
(
is_callable
(
next
))
return
self
;
ant_value_t
iter_fn
=
js_get_sym
(
js
,
self
,
get_asyncIterator_sym
());
if
(
!
is_callable
(
iter_fn
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not async iterable"
);
return
sv_vm_call
(
js
->
vm
,
js
,
iter_fn
,
self
,
NULL
,
0
,
NULL
,
false
);
}
static
ant_value_t
async_iter_make_helper
(
ant_t
*
js
,
int
kind
,
ant_value_t
cb
)
{
ant_value_t
source
=
get_async_source_iter
(
js
);
if
(
is_err
(
source
))
return
source
;
return
make_async_wrap_iter
(
js
,
source
,
kind
,
cb
);
}
static
ant_value_t
async_iter_make_callable_helper
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
,
int
kind
,
const
char
*
method
)
{
if
(
nargs
<
1
||
!
is_callable
(
args
[
0
]))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"%s requires a callable"
,
method
);
return
async_iter_make_helper
(
js
,
kind
,
args
[
0
]);
}
static
ant_value_t
async_iter_make_count_helper
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
,
int
kind
,
const
char
*
method
)
{
double
limit
=
(
nargs
>=
1
&&
vtype
(
args
[
0
])
==
T_NUM
)
?
js_getnum
(
args
[
0
])
:
0
;
if
(
limit
<
0
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"%s requires a non-negative number"
,
method
);
return
async_iter_make_helper
(
js
,
kind
,
js_mknum
(
limit
));
}
static
ant_value_t
async_iter_map
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_make_callable_helper
(
js
,
args
,
nargs
,
WRAP_MAP
,
"AsyncIterator.prototype.map"
);
}
static
ant_value_t
async_iter_filter
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_make_callable_helper
(
js
,
args
,
nargs
,
WRAP_FILTER
,
"AsyncIterator.prototype.filter"
);
}
static
ant_value_t
async_iter_take
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_make_count_helper
(
js
,
args
,
nargs
,
WRAP_TAKE
,
"AsyncIterator.prototype.take"
);
}
static
ant_value_t
async_iter_drop
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_make_count_helper
(
js
,
args
,
nargs
,
WRAP_DROP
,
"AsyncIterator.prototype.drop"
);
}
static
ant_value_t
async_iter_flatMap
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_make_callable_helper
(
js
,
args
,
nargs
,
WRAP_FLATMAP
,
"AsyncIterator.prototype.flatMap"
);
}
static
ant_value_t
async_terminal_advance
(
ant_t
*
js
,
ant_value_t
state
);
static
ant_value_t
async_terminal_finish_callback
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
result
);
static
void
async_terminal_close_and_reject
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
reason
);
static
void
async_terminal_state_finalize
(
ant_t
*
js
,
ant_object_t
*
obj
)
{
free
(
obj
->
native
.
ptr
);
obj
->
native
.
ptr
=
NULL
;
}
static
inline
async_terminal_state_t
*
async_terminal_state
(
ant_value_t
state
)
{
if
(
!
js_check_native_tag
(
state
,
ASYNC_TERMINAL_STATE_TAG
))
return
NULL
;
return
(
async_terminal_state_t
*
)
js_get_native_ptr
(
state
);
}
static
inline
int
async_terminal_mode
(
ant_value_t
state
)
{
async_terminal_state_t
*
st
=
async_terminal_state
(
state
);
return
st
?
st
->
mode
:
ASYNC_TERM_TOARRAY
;
}
static
inline
double
async_terminal_index
(
ant_value_t
state
)
{
async_terminal_state_t
*
st
=
async_terminal_state
(
state
);
return
st
?
st
->
index
:
0
;
}
static
inline
bool
async_terminal_has_acc
(
ant_value_t
state
)
{
async_terminal_state_t
*
st
=
async_terminal_state
(
state
);
return
st
&&
st
->
has_acc
;
}
static
ant_value_t
async_terminal_on_reject
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
promise
=
js_get_slot
(
state
,
SLOT_CTOR
);
js_reject_promise
(
js
,
promise
,
nargs
>
0
?
args
[
0
]
:
js_mkundef
());
return
js_mkundef
();
}
static
ant_value_t
async_terminal_on_callback_reject
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
async_terminal_close_and_reject
(
js
,
state
,
nargs
>
0
?
args
[
0
]
:
js_mkundef
());
return
js_mkundef
();
}
static
ant_value_t
async_terminal_on_callback
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
result
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
return
async_terminal_finish_callback
(
js
,
state
,
result
);
}
static
ant_value_t
async_terminal_on_close
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
promise
=
js_get_slot
(
state
,
SLOT_CTOR
);
js_resolve_promise
(
js
,
promise
,
js_get_slot
(
state
,
SLOT_AUX
));
return
js_mkundef
();
}
static
ant_value_t
async_terminal_on_close_reject
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
promise
=
js_get_slot
(
state
,
SLOT_CTOR
);
js_reject_promise
(
js
,
promise
,
js_get_slot
(
state
,
SLOT_AUX
));
return
js_mkundef
();
}
static
void
async_terminal_close_and_resolve
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
value
)
{
ant_value_t
promise
=
js_get_slot
(
state
,
SLOT_CTOR
);
ant_value_t
iter
=
js_get_slot
(
state
,
SLOT_DATA
);
js_set_slot_wb
(
js
,
state
,
SLOT_AUX
,
value
);
bool
missing
=
false
;
ant_value_t
result
=
async_iter_call_method
(
js
,
iter
,
"return"
,
NULL
,
0
,
&
missing
);
if
(
missing
)
{
js_resolve_promise
(
js
,
promise
,
value
);
return
;
}
if
(
is_err
(
result
))
{
js_reject_promise
(
js
,
promise
,
result
);
return
;
}
if
(
vtype
(
result
)
==
T_PROMISE
)
{
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_terminal_on_close
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_terminal_on_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
;
}
js_resolve_promise
(
js
,
promise
,
value
);
}
static
void
async_terminal_close_and_reject
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
reason
)
{
ant_value_t
promise
=
js_get_slot
(
state
,
SLOT_CTOR
);
ant_value_t
iter
=
js_get_slot
(
state
,
SLOT_DATA
);
js_set_slot_wb
(
js
,
state
,
SLOT_AUX
,
reason
);
bool
missing
=
false
;
ant_value_t
result
=
async_iter_call_method
(
js
,
iter
,
"return"
,
NULL
,
0
,
&
missing
);
if
(
missing
)
{
js_reject_promise
(
js
,
promise
,
reason
);
return
;
}
if
(
is_err
(
result
))
{
js_reject_promise
(
js
,
promise
,
result
);
return
;
}
if
(
vtype
(
result
)
==
T_PROMISE
)
{
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_terminal_on_close_reject
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_terminal_on_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
;
}
js_reject_promise
(
js
,
promise
,
reason
);
}
static
bool
async_terminal_apply_callback_result
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
result
)
{
int
mode
=
async_terminal_mode
(
state
);
if
(
mode
==
ASYNC_TERM_REDUCE
)
{
js_set_slot_wb
(
js
,
state
,
SLOT_SET
,
result
);
async_terminal_state_t
*
st
=
async_terminal_state
(
state
);
if
(
st
)
st
->
has_acc
=
true
;
return
true
;
}
switch
(
mode
)
{
case
ASYNC_TERM_EVERY
:
if
(
!
js_truthy
(
js
,
result
))
{
async_terminal_close_and_resolve
(
js
,
state
,
js_false
);
return
false
;
}
return
true
;
case
ASYNC_TERM_SOME
:
if
(
js_truthy
(
js
,
result
))
{
async_terminal_close_and_resolve
(
js
,
state
,
js_true
);
return
false
;
}
return
true
;
case
ASYNC_TERM_FIND
:
if
(
js_truthy
(
js
,
result
))
{
async_terminal_close_and_resolve
(
js
,
state
,
js_get_slot
(
state
,
SLOT_AUX
));
return
false
;
}
return
true
;
default
:
return
true
;
}
}
static
bool
async_terminal_handle_step
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
step
)
{
ant_value_t
promise
=
js_get_slot
(
state
,
SLOT_CTOR
);
if
(
!
is_object_type
(
step
))
{
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"iterator result is not an object"
));
return
false
;
}
int
mode
=
async_terminal_mode
(
state
);
ant_value_t
done
=
js_getprop_fallback
(
js
,
step
,
"done"
);
ant_value_t
value
=
js_getprop_fallback
(
js
,
step
,
"value"
);
if
(
js_truthy
(
js
,
done
))
{
switch
(
mode
)
{
case
ASYNC_TERM_EVERY
:
js_resolve_promise
(
js
,
promise
,
js_true
);
break
;
case
ASYNC_TERM_SOME
:
js_resolve_promise
(
js
,
promise
,
js_false
);
break
;
case
ASYNC_TERM_FIND
:
js_resolve_promise
(
js
,
promise
,
js_mkundef
());
break
;
case
ASYNC_TERM_FOREACH
:
js_resolve_promise
(
js
,
promise
,
js_mkundef
());
break
;
case
ASYNC_TERM_REDUCE
:
if
(
!
async_terminal_has_acc
(
state
))
{
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"reduce of empty iterator with no initial value"
));
}
else
js_resolve_promise
(
js
,
promise
,
js_get_slot
(
state
,
SLOT_SET
));
break
;
default
:
js_resolve_promise
(
js
,
promise
,
js_get_slot
(
state
,
SLOT_ENTRIES
));
break
;
}
return
false
;
}
double
index
=
async_terminal_index
(
state
);
async_terminal_state_t
*
st
=
async_terminal_state
(
state
);
if
(
st
)
st
->
index
=
index
+
1
;
if
(
mode
==
ASYNC_TERM_TOARRAY
)
{
js_arr_push
(
js
,
js_get_slot
(
state
,
SLOT_ENTRIES
),
value
);
return
true
;
}
ant_value_t
fn
=
js_get_slot
(
state
,
SLOT_MAP
);
if
(
!
is_callable
(
fn
))
{
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"callback is not callable"
));
return
false
;
}
if
(
mode
==
ASYNC_TERM_REDUCE
)
{
if
(
!
async_terminal_has_acc
(
state
))
{
js_set_slot_wb
(
js
,
state
,
SLOT_SET
,
value
);
if
(
st
)
st
->
has_acc
=
true
;
return
true
;
}
ant_value_t
call_args
[
3
]
=
{
js_get_slot
(
state
,
SLOT_SET
),
value
,
js_mknum
(
index
)
};
ant_value_t
next_acc
=
sv_vm_call
(
js
->
vm
,
js
,
fn
,
js_mkundef
(),
call_args
,
3
,
NULL
,
false
);
if
(
is_err
(
next_acc
))
{
async_terminal_close_and_reject
(
js
,
state
,
next_acc
);
return
false
;
}
if
(
vtype
(
next_acc
)
==
T_PROMISE
)
{
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_terminal_on_callback
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_terminal_on_callback_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
next_acc
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
false
;
}
return
async_terminal_apply_callback_result
(
js
,
state
,
next_acc
);
}
ant_value_t
result
=
call_indexed_callback
(
js
,
fn
,
value
,
(
double
)
index
);
if
(
is_err
(
result
))
{
async_terminal_close_and_reject
(
js
,
state
,
result
);
return
false
;
}
js_set_slot_wb
(
js
,
state
,
SLOT_AUX
,
value
);
if
(
vtype
(
result
)
==
T_PROMISE
)
{
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_terminal_on_callback
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_terminal_on_callback_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
false
;
}
return
async_terminal_apply_callback_result
(
js
,
state
,
result
);
}
static
ant_value_t
async_terminal_on_step
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
ant_value_t
state
=
js_get_slot
(
js_getcurrentfunc
(
js
),
SLOT_DATA
);
ant_value_t
step
=
nargs
>
0
?
args
[
0
]
:
js_mkundef
();
if
(
async_terminal_handle_step
(
js
,
state
,
step
))
return
async_terminal_advance
(
js
,
state
);
return
js_mkundef
();
}
static
ant_value_t
async_terminal_finish_callback
(
ant_t
*
js
,
ant_value_t
state
,
ant_value_t
result
)
{
if
(
vtype
(
result
)
==
T_PROMISE
)
{
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_terminal_on_callback
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_terminal_on_callback_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
js_mkundef
();
}
if
(
async_terminal_apply_callback_result
(
js
,
state
,
result
))
return
async_terminal_advance
(
js
,
state
);
return
js_mkundef
();
}
static
ant_value_t
async_terminal_advance
(
ant_t
*
js
,
ant_value_t
state
)
{
for
(;;)
{
ant_value_t
iter
=
js_get_slot
(
state
,
SLOT_DATA
);
ant_value_t
promise
=
js_get_slot
(
state
,
SLOT_CTOR
);
bool
missing
=
false
;
ant_value_t
next_result
=
async_iter_call_method
(
js
,
iter
,
"next"
,
NULL
,
0
,
&
missing
);
if
(
missing
)
{
js_reject_promise
(
js
,
promise
,
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"object is not async iterable"
));
return
js_mkundef
();
}
if
(
is_err
(
next_result
))
{
js_reject_promise
(
js
,
promise
,
next_result
);
return
js_mkundef
();
}
if
(
vtype
(
next_result
)
==
T_PROMISE
)
{
ant_value_t
on_resolve
=
js_heavy_mkfun
(
js
,
async_terminal_on_step
,
state
);
ant_value_t
on_reject
=
js_heavy_mkfun
(
js
,
async_terminal_on_reject
,
state
);
ant_value_t
then_result
=
js_promise_then
(
js
,
next_result
,
on_resolve
,
on_reject
);
promise_mark_handled
(
then_result
);
return
js_mkundef
();
}
if
(
!
async_terminal_handle_step
(
js
,
state
,
next_result
))
return
js_mkundef
();
}}
static
ant_value_t
async_iter_terminal
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
,
int
mode
)
{
if
(
mode
!=
ASYNC_TERM_TOARRAY
&&
(
nargs
<
1
||
!
is_callable
(
args
[
0
])))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"AsyncIterator helper requires a callable"
);
ant_value_t
iter
=
get_async_source_iter
(
js
);
if
(
is_err
(
iter
))
return
rejected_promise
(
js
,
iter
);
ant_value_t
promise
=
js_mkpromise
(
js
);
ant_value_t
state
=
js_mkobj
(
js
);
async_terminal_state_t
*
st
=
calloc
(
1
,
sizeof
(
*
st
));
if
(
!
st
)
{
js_reject_promise
(
js
,
promise
,
js_mkerr
(
js
,
"out of memory"
));
return
promise
;
}
st
->
mode
=
mode
;
st
->
index
=
0
;
st
->
has_acc
=
(
mode
==
ASYNC_TERM_REDUCE
&&
nargs
>
1
);
js_set_native_tag
(
state
,
ASYNC_TERMINAL_STATE_TAG
);
js_set_native_ptr
(
state
,
st
);
js_set_finalizer
(
state
,
async_terminal_state_finalize
);
js_set_slot_wb
(
js
,
state
,
SLOT_DATA
,
iter
);
js_set_slot_wb
(
js
,
state
,
SLOT_CTOR
,
promise
);
js_set_slot_wb
(
js
,
state
,
SLOT_MAP
,
mode
==
ASYNC_TERM_TOARRAY
?
js_mkundef
()
:
args
[
0
]);
js_set_slot_wb
(
js
,
state
,
SLOT_ENTRIES
,
js_mkarr
(
js
));
js_set_slot_wb
(
js
,
state
,
SLOT_SET
,
(
mode
==
ASYNC_TERM_REDUCE
&&
nargs
>
1
)
?
args
[
1
]
:
js_mkundef
());
js_set_slot
(
state
,
SLOT_AUX
,
js_mkundef
());
async_terminal_advance
(
js
,
state
);
return
promise
;
}
static
ant_value_t
async_iter_every
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_terminal
(
js
,
args
,
nargs
,
ASYNC_TERM_EVERY
);
}
static
ant_value_t
async_iter_some
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_terminal
(
js
,
args
,
nargs
,
ASYNC_TERM_SOME
);
}
static
ant_value_t
async_iter_find
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_terminal
(
js
,
args
,
nargs
,
ASYNC_TERM_FIND
);
}
static
ant_value_t
async_iter_forEach
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_terminal
(
js
,
args
,
nargs
,
ASYNC_TERM_FOREACH
);
}
static
ant_value_t
async_iter_reduce
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_terminal
(
js
,
args
,
nargs
,
ASYNC_TERM_REDUCE
);
}
static
ant_value_t
async_iter_toArray
(
ant_t
*
js
,
ant_value_t
*
args
,
int
nargs
)
{
return
async_iter_terminal
(
js
,
args
,
nargs
,
ASYNC_TERM_TOARRAY
);
}
void
init_iterator_module
(
void
)
{
ant_t
*
js
=
rt
->
js
;
ant_value_t
g
=
js_glob
(
js
);
ant_value_t
iter_proto
=
js
->
sym
.
iterator_proto
;
g_wrap_iter_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
g_wrap_iter_proto
,
iter_proto
);
js_set
(
js
,
g_wrap_iter_proto
,
"next"
,
js_mkfun
(
wrap_iter_next
));
js_set
(
js
,
iter_proto
,
"map"
,
js_mkfun
(
iter_map
));
js_set
(
js
,
iter_proto
,
"filter"
,
js_mkfun
(
iter_filter
));
js_set
(
js
,
iter_proto
,
"take"
,
js_mkfun
(
iter_take
));
js_set
(
js
,
iter_proto
,
"drop"
,
js_mkfun
(
iter_drop
));
js_set
(
js
,
iter_proto
,
"flatMap"
,
js_mkfun
(
iter_flatMap
));
js_set
(
js
,
iter_proto
,
"every"
,
js_mkfun
(
iter_every
));
js_set
(
js
,
iter_proto
,
"some"
,
js_mkfun
(
iter_some
));
js_set
(
js
,
iter_proto
,
"find"
,
js_mkfun
(
iter_find
));
js_set
(
js
,
iter_proto
,
"forEach"
,
js_mkfun
(
iter_forEach
));
js_set
(
js
,
iter_proto
,
"reduce"
,
js_mkfun
(
iter_reduce
));
js_set
(
js
,
iter_proto
,
"toArray"
,
js_mkfun
(
iter_toArray
));
js_set_sym
(
js
,
iter_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"Iterator"
,
8
));
ant_value_t
ctor_obj
=
js_mkobj
(
js
);
js_set_slot
(
ctor_obj
,
SLOT_CFUNC
,
js_mkfun
(
iter_ctor
));
js_mkprop_fast
(
js
,
ctor_obj
,
"prototype"
,
9
,
iter_proto
);
js_mkprop_fast
(
js
,
ctor_obj
,
"name"
,
4
,
js_mkstr
(
js
,
"Iterator"
,
8
));
js_set_descriptor
(
js
,
ctor_obj
,
"name"
,
4
,
0
);
ant_value_t
ctor
=
js_obj_to_func
(
ctor_obj
);
js_set
(
js
,
ctor
,
"from"
,
js_mkfun
(
iter_from
));
js_set
(
js
,
iter_proto
,
"constructor"
,
ctor
);
js_set
(
js
,
g
,
"Iterator"
,
ctor
);
ant_value_t
async_iter_proto
=
js_mkobj
(
js
);
js
->
sym
.
async_iterator_proto
=
async_iter_proto
;
js_set_proto_init
(
async_iter_proto
,
js
->
sym
.
object_proto
);
js_set_sym
(
js
,
async_iter_proto
,
get_asyncIterator_sym
(),
js_mkfun
(
sym_this_cb
));
js_set_sym
(
js
,
async_iter_proto
,
get_toStringTag_sym
(),
js_mkstr
(
js
,
"AsyncIterator"
,
13
));
ant_value_t
async_ctor_obj
=
js_mkobj
(
js
);
js_set_slot
(
async_ctor_obj
,
SLOT_CFUNC
,
js_mkfun
(
async_iter_ctor
));
js_mkprop_fast
(
js
,
async_ctor_obj
,
"prototype"
,
9
,
async_iter_proto
);
js_mkprop_fast
(
js
,
async_ctor_obj
,
"name"
,
4
,
js_mkstr
(
js
,
"AsyncIterator"
,
13
));
js_set_descriptor
(
js
,
async_ctor_obj
,
"name"
,
4
,
0
);
ant_value_t
async_ctor
=
js_obj_to_func
(
async_ctor_obj
);
js_set
(
js
,
async_iter_proto
,
"constructor"
,
async_ctor
);
js_set
(
js
,
g
,
"AsyncIterator"
,
async_ctor
);
g_async_wrap_iter_proto
=
js_mkobj
(
js
);
js_set_proto_init
(
g_async_wrap_iter_proto
,
async_iter_proto
);
js_set
(
js
,
g_async_wrap_iter_proto
,
"next"
,
js_mkfun
(
async_wrap_next
));
js_set
(
js
,
g_async_wrap_iter_proto
,
"return"
,
js_mkfun
(
async_wrap_return
));
js_set
(
js
,
g_async_wrap_iter_proto
,
"throw"
,
js_mkfun
(
async_wrap_throw
));
}
void
init_async_iterator_helpers
(
void
)
{
ant_t
*
js
=
rt
->
js
;
ant_value_t
g
=
js_glob
(
js
);
ant_value_t
ctor
=
js_get
(
js
,
g
,
"AsyncIterator"
);
ant_value_t
proto
=
js
->
sym
.
async_iterator_proto
;
js_set
(
js
,
ctor
,
"from"
,
js_mkfun
(
async_iter_from
));
js_set
(
js
,
proto
,
"map"
,
js_mkfun
(
async_iter_map
));
js_set
(
js
,
proto
,
"filter"
,
js_mkfun
(
async_iter_filter
));
js_set
(
js
,
proto
,
"take"
,
js_mkfun
(
async_iter_take
));
js_set
(
js
,
proto
,
"drop"
,
js_mkfun
(
async_iter_drop
));
js_set
(
js
,
proto
,
"flatMap"
,
js_mkfun
(
async_iter_flatMap
));
js_set
(
js
,
proto
,
"every"
,
js_mkfun
(
async_iter_every
));
js_set
(
js
,
proto
,
"some"
,
js_mkfun
(
async_iter_some
));
js_set
(
js
,
proto
,
"find"
,
js_mkfun
(
async_iter_find
));
js_set
(
js
,
proto
,
"forEach"
,
js_mkfun
(
async_iter_forEach
));
js_set
(
js
,
proto
,
"reduce"
,
js_mkfun
(
async_iter_reduce
));
js_set
(
js
,
proto
,
"toArray"
,
js_mkfun
(
async_iter_toArray
));
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 6:23 AM (1 d, 23 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
543748
Default Alt Text
iterator.c (46 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment