Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4440063
engine.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
69 KB
Referenced Files
None
Subscribers
None
engine.c
View Options
#include
"gc.h"
#include
"errors.h"
#include
<stdlib.h>
#include
"silver/engine.h"
#include
"silver/swarm.h"
#include
"modules/regex.h"
#include
"ops/literals.h"
#include
"ops/stack.h"
#include
"ops/locals.h"
#include
"ops/upvalues.h"
#include
"ops/globals.h"
#include
"ops/property.h"
#include
"ops/optional.h"
#include
"ops/private.h"
#include
"ops/super.h"
#include
"ops/arithmetic.h"
#include
"ops/comparison.h"
#include
"ops/bitwise.h"
#include
"ops/unary.h"
#include
"ops/control.h"
#include
"ops/calls.h"
#include
"ops/returns.h"
#include
"ops/exceptions.h"
#include
"ops/async.h"
#include
"ops/iteration.h"
#include
"ops/using.h"
#include
"ops/objects.h"
#include
"ops/coercion.h"
sv_vm_t
*
sv_vm_create
(
ant_t
*
js
,
sv_vm_kind_t
kind
)
{
int
stack_size
,
max_frames
;
sv_vm_limits
(
kind
,
&
stack_size
,
&
max_frames
);
sv_vm_t
*
vm
=
calloc
(
1
,
sizeof
(
*
vm
));
if
(
!
vm
)
return
NULL
;
vm
->
js
=
js
;
vm
->
fp
=
-1
;
vm
->
stack_size
=
stack_size
;
vm
->
max_frames
=
max_frames
;
vm
->
suspended_entry_fp
=
-1
;
vm
->
suspended_saved_fp
=
-1
;
vm
->
stack
=
calloc
((
size_t
)
stack_size
,
sizeof
(
ant_value_t
));
vm
->
frames
=
calloc
((
size_t
)
max_frames
,
sizeof
(
sv_frame_t
));
if
(
!
vm
->
stack
||
!
vm
->
frames
)
{
sv_vm_destroy
(
vm
);
return
NULL
;
}
return
vm
;
}
void
sv_vm_destroy
(
sv_vm_t
*
vm
)
{
if
(
!
vm
)
return
;
free
(
vm
->
stack
);
free
(
vm
->
frames
);
free
(
vm
);
}
static
bool
sv_vm_grow_frames
(
sv_vm_t
*
vm
)
{
int
new_max
=
vm
->
max_frames
*
2
;
if
(
new_max
>
SV_FRAMES_HARD_MAX
)
new_max
=
SV_FRAMES_HARD_MAX
;
if
(
new_max
<=
vm
->
max_frames
)
return
false
;
sv_frame_t
*
nf
=
realloc
(
vm
->
frames
,
(
size_t
)
new_max
*
sizeof
(
sv_frame_t
));
if
(
!
nf
)
return
false
;
vm
->
frames
=
nf
;
vm
->
max_frames
=
new_max
;
return
true
;
}
static
bool
sv_vm_grow_stack
(
sv_vm_t
*
vm
)
{
int
new_size
=
vm
->
stack_size
*
2
;
if
(
new_size
>
SV_STACK_HARD_MAX
)
new_size
=
SV_STACK_HARD_MAX
;
if
(
new_size
<=
vm
->
stack_size
)
return
false
;
ant_value_t
*
old
=
vm
->
stack
;
int
old_size
=
vm
->
stack_size
;
ant_value_t
*
ns
=
realloc
(
vm
->
stack
,
(
size_t
)
new_size
*
sizeof
(
ant_value_t
));
if
(
!
ns
)
return
false
;
ptrdiff_t
delta
=
ns
-
old
;
vm
->
stack
=
ns
;
vm
->
stack_size
=
new_size
;
if
(
delta
!=
0
)
{
for
(
int
i
=
0
;
i
<=
vm
->
fp
;
i
++
)
{
if
(
vm
->
frames
[
i
].
bp
)
vm
->
frames
[
i
].
bp
+=
delta
;
if
(
vm
->
frames
[
i
].
lp
)
vm
->
frames
[
i
].
lp
+=
delta
;
}
for
(
sv_upvalue_t
*
uv
=
vm
->
open_upvalues
;
uv
;
uv
=
uv
->
next
)
if
(
uv
->
location
!=
&
uv
->
closed
&&
sv_slot_in_range
(
old
,
(
size_t
)
old_size
,
uv
->
location
)
)
uv
->
location
+=
delta
;
}
return
true
;
}
bool
sv_lookup_srcpos
(
sv_func_t
*
func
,
int
bc_offset
,
uint32_t
*
line
,
uint32_t
*
col
)
{
if
(
!
func
||
!
func
->
srcpos
||
func
->
srcpos_count
<=
0
)
return
false
;
int
best
=
-1
;
for
(
int
i
=
0
;
i
<
func
->
srcpos_count
;
i
++
)
{
if
((
int
)
func
->
srcpos
[
i
].
bc_offset
<=
bc_offset
)
best
=
i
;
else
break
;
}
if
(
best
<
0
)
return
false
;
if
(
line
)
*
line
=
func
->
srcpos
[
best
].
line
;
if
(
col
)
*
col
=
func
->
srcpos
[
best
].
col
;
return
true
;
}
bool
sv_lookup_srcspan
(
sv_func_t
*
func
,
int
bc_offset
,
uint32_t
*
src_off
,
uint32_t
*
src_end
)
{
if
(
!
func
||
!
func
->
srcpos
||
func
->
srcpos_count
<=
0
)
return
false
;
int
best
=
-1
;
for
(
int
i
=
0
;
i
<
func
->
srcpos_count
;
i
++
)
{
if
((
int
)
func
->
srcpos
[
i
].
bc_offset
<=
bc_offset
)
best
=
i
;
else
break
;
}
if
(
best
<
0
)
return
false
;
uint32_t
off
=
func
->
srcpos
[
best
].
src_off
;
uint32_t
end
=
func
->
srcpos
[
best
].
src_end
;
if
(
end
<
off
)
end
=
off
;
if
(
src_off
)
*
src_off
=
off
;
if
(
src_end
)
*
src_end
=
end
;
return
true
;
}
static
ant_offset_t
sv_srcpos_to_offset_local
(
const
char
*
code
,
ant_offset_t
clen
,
uint32_t
line
,
uint32_t
col
)
{
ant_offset_t
off
=
0
;
uint32_t
cur
=
1
;
while
(
off
<
clen
&&
cur
<
line
)
{
if
(
code
[
off
]
==
'\n'
)
cur
++
;
off
++
;
}
if
(
col
>
0
)
off
+=
col
-
1
;
if
(
off
>
clen
)
off
=
clen
;
return
off
;
}
void
js_set_error_site_from_bc
(
ant_t
*
js
,
sv_func_t
*
func
,
int
bc_offset
,
const
char
*
filename
)
{
if
(
!
js
||
!
func
||
!
func
->
source
||
func
->
source_len
<=
0
)
return
;
uint32_t
src_off
=
0
,
src_end
=
0
;
if
(
sv_lookup_srcspan
(
func
,
bc_offset
,
&
src_off
,
&
src_end
))
{
ant_offset_t
off
=
(
ant_offset_t
)
src_off
;
ant_offset_t
span_len
=
(
ant_offset_t
)(
src_end
>
src_off
?
(
src_end
-
src_off
)
:
0
);
if
(
span_len
<=
0
&&
off
<
(
ant_offset_t
)
func
->
source_len
)
span_len
=
1
;
js_set_error_site
(
js
,
func
->
source
,
(
ant_offset_t
)
func
->
source_len
,
filename
?
filename
:
func
->
filename
,
off
,
span_len
);
return
;
}
uint32_t
line
=
0
,
col
=
0
;
if
(
sv_lookup_srcpos
(
func
,
bc_offset
,
&
line
,
&
col
))
{
ant_offset_t
off
=
sv_srcpos_to_offset_local
(
func
->
source
,
(
ant_offset_t
)
func
->
source_len
,
line
,
col
);
js_set_error_site
(
js
,
func
->
source
,
(
ant_offset_t
)
func
->
source_len
,
filename
?
filename
:
func
->
filename
,
off
,
0
);
}
}
void
js_set_error_site_from_vm_top
(
ant_t
*
js
)
{
sv_vm_t
*
vm
=
sv_vm_get_active
(
js
);
if
(
!
js
||
!
vm
||
vm
->
fp
<
0
)
return
;
sv_frame_t
*
frame
=
&
vm
->
frames
[
vm
->
fp
];
sv_func_t
*
func
=
frame
->
func
;
if
(
!
func
)
return
;
int
bc_off
=
0
;
if
(
frame
->
ip
&&
func
->
code
)
bc_off
=
(
int
)(
frame
->
ip
-
func
->
code
);
js_set_error_site_from_bc
(
js
,
func
,
bc_off
,
func
->
filename
);
}
// TODO: move to strings.c
static
inline
bool
sv_builder_has_cached_value
(
const
ant_string_builder_t
*
builder
)
{
return
builder
&&
builder
->
cached
!=
js_mkundef
();
}
static
inline
ant_flat_string_t
*
sv_string_builder_flat_ptr
(
ant_value_t
value
)
{
if
(
vtype
(
value
)
!=
T_STR
||
str_is_heap_rope
(
value
)
||
str_is_heap_builder
(
value
))
return
NULL
;
return
(
ant_flat_string_t
*
)(
uintptr_t
)
vdata
(
value
);
}
static
inline
ant_string_builder_t
*
sv_string_builder_heap_ptr
(
ant_value_t
value
)
{
if
(
vtype
(
value
)
!=
T_STR
||
!
str_is_heap_builder
(
value
))
return
NULL
;
return
ant_str_builder_ptr
(
value
);
}
static
inline
uint8_t
sv_builder_chunk_ascii_state
(
ant_flat_string_t
*
flat
)
{
if
(
!
flat
)
return
STR_ASCII_UNKNOWN
;
if
(
flat
->
is_ascii
==
STR_ASCII_UNKNOWN
)
flat
->
is_ascii
=
str_detect_ascii_bytes
(
flat
->
bytes
,
(
size_t
)
flat
->
len
);
return
flat
->
is_ascii
;
}
static
inline
void
sv_builder_note_ascii
(
ant_string_builder_t
*
builder
,
uint8_t
state
)
{
if
(
!
builder
)
return
;
if
(
state
==
STR_ASCII_NO
)
builder
->
ascii_state
=
STR_ASCII_NO
;
else
if
(
builder
->
ascii_state
!=
STR_ASCII_NO
&&
state
==
STR_ASCII_YES
)
builder
->
ascii_state
=
STR_ASCII_YES
;
}
static
inline
void
sv_builder_record_flat
(
ant_string_builder_t
*
builder
,
ant_flat_string_t
*
flat
)
{
if
(
!
builder
||
!
flat
)
return
;
builder
->
len
+=
flat
->
len
;
sv_builder_note_ascii
(
builder
,
sv_builder_chunk_ascii_state
(
flat
));
}
static
ant_value_t
sv_builder_normalize_chunk
(
ant_t
*
js
,
ant_value_t
value
)
{
if
(
vtype
(
value
)
!=
T_STR
)
return
js_mkerr
(
js
,
"string builder expects string chunk"
);
return
str_materialize
(
js
,
value
);
}
static
ant_value_t
sv_builder_push_chunk_value
(
ant_t
*
js
,
ant_string_builder_t
*
builder
,
ant_value_t
chunk
)
{
if
(
!
builder
)
return
js_mkerr
(
js
,
"string builder chunk allocation failed"
);
ant_builder_chunk_t
*
node
=
(
ant_builder_chunk_t
*
)
js_type_alloc
(
js
,
ANT_ALLOC_ROPE
,
sizeof
(
*
node
),
_Alignof
(
ant_builder_chunk_t
)
);
if
(
!
node
)
return
js_mkerr
(
js
,
"string builder chunk allocation failed"
);
node
->
next
=
NULL
;
node
->
value
=
chunk
;
if
(
builder
->
chunk_tail
)
builder
->
chunk_tail
->
next
=
node
;
else
builder
->
head
=
node
;
builder
->
chunk_tail
=
node
;
return
js_mkundef
();
}
static
ant_value_t
sv_builder_flush_tail
(
ant_t
*
js
,
ant_string_builder_t
*
builder
)
{
if
(
!
builder
||
builder
->
tail_len
==
0
)
return
js_mkundef
();
ant_value_t
tail
=
js_mkstr
(
js
,
builder
->
tail
,
builder
->
tail_len
);
if
(
is_err
(
tail
))
return
tail
;
ant_value_t
push
=
sv_builder_push_chunk_value
(
js
,
builder
,
tail
);
if
(
is_err
(
push
))
return
push
;
builder
->
tail_len
=
0
;
return
js_mkundef
();
}
static
ant_value_t
sv_builder_append_flat
(
ant_t
*
js
,
ant_string_builder_t
*
builder
,
ant_value_t
chunk
)
{
ant_flat_string_t
*
flat
=
sv_string_builder_flat_ptr
(
chunk
);
if
(
!
flat
)
return
js_mkerr
(
js
,
"string builder received non-flat string"
);
if
(
flat
->
len
==
0
)
return
js_mkundef
();
if
(
flat
->
len
<=
STR_BUILDER_TAIL_CAP
&&
builder
->
tail_len
+
flat
->
len
<=
STR_BUILDER_TAIL_CAP
)
{
memcpy
(
builder
->
tail
+
builder
->
tail_len
,
flat
->
bytes
,
(
size_t
)
flat
->
len
);
builder
->
tail_len
=
(
uint16_t
)(
builder
->
tail_len
+
flat
->
len
);
sv_builder_record_flat
(
builder
,
flat
);
return
js_mkundef
();
}
ant_value_t
flush
=
sv_builder_flush_tail
(
js
,
builder
);
if
(
is_err
(
flush
))
return
flush
;
ant_value_t
push
=
sv_builder_push_chunk_value
(
js
,
builder
,
chunk
);
if
(
is_err
(
push
))
return
push
;
sv_builder_record_flat
(
builder
,
flat
);
return
js_mkundef
();
}
static
ant_value_t
sv_string_builder_new
(
ant_t
*
js
)
{
ant_string_builder_t
*
builder
=
(
ant_string_builder_t
*
)
js_type_alloc
(
js
,
ANT_ALLOC_ROPE
,
sizeof
(
*
builder
),
_Alignof
(
ant_string_builder_t
)
);
if
(
!
builder
)
return
js_mkerr
(
js
,
"string builder allocation failed"
);
memset
(
builder
,
0
,
sizeof
(
*
builder
));
builder
->
cached
=
js_mkundef
();
builder
->
ascii_state
=
STR_ASCII_YES
;
return
ant_mkbuilder_value
(
builder
);
}
static
inline
void
sv_record_slot_feedback
(
sv_frame_t
*
frame
,
sv_func_t
*
func
,
uint16_t
slot_idx
,
ant_value_t
value
)
{
if
(
!
frame
||
!
func
)
return
;
if
((
int
)
slot_idx
<
func
->
param_count
)
return
;
sv_tfb_record_local
(
func
,
(
int
)(
slot_idx
-
func
->
param_count
),
value
);
}
bool
sv_slot_has_open_upvalue
(
sv_vm_t
*
vm
,
ant_value_t
*
slot
)
{
if
(
!
vm
||
!
slot
)
return
false
;
for
(
sv_upvalue_t
*
uv
=
vm
->
open_upvalues
;
uv
;
uv
=
uv
->
next
)
if
(
uv
->
location
==
slot
)
return
true
;
return
false
;
}
ant_value_t
sv_string_builder_read_value
(
ant_t
*
js
,
ant_value_t
value
)
{
if
(
vtype
(
value
)
==
T_STR
&&
str_is_heap_builder
(
value
))
return
str_materialize
(
js
,
value
);
return
value
;
}
static
ant_value_t
sv_slot_generic_add_store
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
*
slot
,
ant_value_t
lhs
,
ant_value_t
rhs
)
{
vm
->
stack
[
vm
->
sp
++
]
=
lhs
;
vm
->
stack
[
vm
->
sp
++
]
=
rhs
;
ant_value_t
err
=
sv_op_add
(
vm
,
js
);
if
(
is_err
(
err
))
return
err
;
*
slot
=
vm
->
stack
[
--
vm
->
sp
];
return
js_mkundef
();
}
ant_value_t
sv_string_builder_flush_slot
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_frame_t
*
frame
,
uint16_t
slot_idx
)
{
ant_value_t
*
slot
=
sv_frame_slot_ptr
(
frame
,
slot_idx
);
if
(
!
slot
||
vtype
(
*
slot
)
!=
T_STR
||
!
str_is_heap_builder
(
*
slot
))
return
js_mkundef
();
ant_value_t
out
=
str_materialize
(
js
,
*
slot
);
if
(
is_err
(
out
))
return
out
;
*
slot
=
out
;
sv_record_slot_feedback
(
frame
,
frame
->
func
,
slot_idx
,
out
);
return
out
;
}
ant_value_t
sv_string_builder_append_slot
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_frame_t
*
frame
,
sv_func_t
*
func
,
uint16_t
slot_idx
,
ant_value_t
rhs
)
{
ant_value_t
*
slot
=
sv_frame_slot_ptr
(
frame
,
slot_idx
);
if
(
!
slot
)
return
js_mkerr
(
js
,
"invalid string builder slot"
);
ant_value_t
lhs
=
*
slot
;
ant_string_builder_t
*
builder
=
sv_string_builder_heap_ptr
(
lhs
);
if
(
builder
)
{
ant_value_t
rhs_str
=
coerce_to_str_concat
(
js
,
rhs
);
if
(
is_err
(
rhs_str
))
return
rhs_str
;
rhs_str
=
sv_builder_normalize_chunk
(
js
,
rhs_str
);
if
(
is_err
(
rhs_str
))
return
rhs_str
;
builder
->
cached
=
js_mkundef
();
ant_value_t
append_err
=
sv_builder_append_flat
(
js
,
builder
,
rhs_str
);
if
(
is_err
(
append_err
))
return
append_err
;
sv_record_slot_feedback
(
frame
,
func
,
slot_idx
,
lhs
);
return
js_mkundef
();
}
if
(
vtype
(
lhs
)
==
T_NUM
&&
vtype
(
rhs
)
==
T_NUM
)
{
*
slot
=
tov
(
tod
(
lhs
)
+
tod
(
rhs
));
sv_record_slot_feedback
(
frame
,
func
,
slot_idx
,
*
slot
);
return
js_mkundef
();
}
ant_value_t
lu
=
unwrap_primitive
(
js
,
lhs
);
ant_value_t
ru
=
unwrap_primitive
(
js
,
rhs
);
bool
string_concat
=
is_non_numeric
(
lu
)
||
is_non_numeric
(
ru
);
if
(
!
string_concat
)
{
ant_value_t
add_err
=
sv_slot_generic_add_store
(
vm
,
js
,
slot
,
lhs
,
rhs
);
if
(
is_err
(
add_err
))
return
add_err
;
sv_record_slot_feedback
(
frame
,
func
,
slot_idx
,
*
slot
);
return
js_mkundef
();
}
ant_value_t
lhs_str
=
coerce_to_str_concat
(
js
,
lhs
);
if
(
is_err
(
lhs_str
))
return
lhs_str
;
ant_value_t
rhs_str
=
coerce_to_str_concat
(
js
,
rhs
);
if
(
is_err
(
rhs_str
))
return
rhs_str
;
lhs_str
=
sv_builder_normalize_chunk
(
js
,
lhs_str
);
if
(
is_err
(
lhs_str
))
return
lhs_str
;
rhs_str
=
sv_builder_normalize_chunk
(
js
,
rhs_str
);
if
(
is_err
(
rhs_str
))
return
rhs_str
;
ant_value_t
builder_value
=
sv_string_builder_new
(
js
);
if
(
is_err
(
builder_value
))
return
builder_value
;
builder
=
sv_string_builder_heap_ptr
(
builder_value
);
ant_value_t
append_lhs
=
sv_builder_append_flat
(
js
,
builder
,
lhs_str
);
if
(
is_err
(
append_lhs
))
return
append_lhs
;
ant_value_t
append_rhs
=
sv_builder_append_flat
(
js
,
builder
,
rhs_str
);
if
(
is_err
(
append_rhs
))
return
append_rhs
;
*
slot
=
builder_value
;
sv_record_slot_feedback
(
frame
,
func
,
slot_idx
,
*
slot
);
return
js_mkundef
();
}
ant_value_t
sv_string_builder_append_snapshot_slot
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_frame_t
*
frame
,
sv_func_t
*
func
,
uint16_t
slot_idx
,
ant_value_t
lhs
,
ant_value_t
rhs
)
{
ant_value_t
*
slot
=
sv_frame_slot_ptr
(
frame
,
slot_idx
);
if
(
!
slot
)
return
js_mkerr
(
js
,
"invalid string builder slot"
);
// the snapshot path is only semantically required if the slot changed while
// evaluating the RHS. when it did not, delegate to the normal append path so
// active builders can keep appending in place instead of rebuilding.
if
(
*
slot
==
lhs
)
return
sv_string_builder_append_slot
(
vm
,
js
,
frame
,
func
,
slot_idx
,
rhs
);
if
(
vtype
(
lhs
)
==
T_NUM
&&
vtype
(
rhs
)
==
T_NUM
)
{
*
slot
=
tov
(
tod
(
lhs
)
+
tod
(
rhs
));
sv_record_slot_feedback
(
frame
,
func
,
slot_idx
,
*
slot
);
return
js_mkundef
();
}
ant_value_t
lu
=
unwrap_primitive
(
js
,
lhs
);
ant_value_t
ru
=
unwrap_primitive
(
js
,
rhs
);
bool
string_concat
=
is_non_numeric
(
lu
)
||
is_non_numeric
(
ru
);
if
(
!
string_concat
)
{
ant_value_t
add_err
=
sv_slot_generic_add_store
(
vm
,
js
,
slot
,
lhs
,
rhs
);
if
(
is_err
(
add_err
))
return
add_err
;
sv_record_slot_feedback
(
frame
,
func
,
slot_idx
,
*
slot
);
return
js_mkundef
();
}
ant_value_t
lhs_str
=
coerce_to_str_concat
(
js
,
lhs
);
if
(
is_err
(
lhs_str
))
return
lhs_str
;
ant_value_t
rhs_str
=
coerce_to_str_concat
(
js
,
rhs
);
if
(
is_err
(
rhs_str
))
return
rhs_str
;
lhs_str
=
sv_builder_normalize_chunk
(
js
,
lhs_str
);
if
(
is_err
(
lhs_str
))
return
lhs_str
;
rhs_str
=
sv_builder_normalize_chunk
(
js
,
rhs_str
);
if
(
is_err
(
rhs_str
))
return
rhs_str
;
ant_value_t
builder_value
=
sv_string_builder_new
(
js
);
if
(
is_err
(
builder_value
))
return
builder_value
;
ant_string_builder_t
*
builder
=
sv_string_builder_heap_ptr
(
builder_value
);
ant_value_t
append_lhs
=
sv_builder_append_flat
(
js
,
builder
,
lhs_str
);
if
(
is_err
(
append_lhs
))
return
append_lhs
;
ant_value_t
append_rhs
=
sv_builder_append_flat
(
js
,
builder
,
rhs_str
);
if
(
is_err
(
append_rhs
))
return
append_rhs
;
*
slot
=
builder_value
;
sv_record_slot_feedback
(
frame
,
func
,
slot_idx
,
*
slot
);
return
js_mkundef
();
}
void
sv_vm_visit_frame_funcs
(
sv_vm_t
*
vm
,
void
(
*
visitor
)(
void
*
,
sv_func_t
*
),
void
*
ctx
)
{
if
(
!
vm
)
return
;
for
(
int
i
=
0
;
i
<=
vm
->
fp
;
i
++
)
if
(
vm
->
frames
[
i
].
func
)
visitor
(
ctx
,
vm
->
frames
[
i
].
func
);
}
ant_value_t
sv_call_async_closure_dispatch
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
ant_value_t
super_val
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
)
{
return
sv_start_async_closure
(
vm
,
js
,
closure
,
callee_func
,
super_val
,
this_val
,
args
,
argc
);
}
ant_value_t
sv_execute_entry_tla
(
ant_t
*
js
,
sv_func_t
*
func
,
ant_value_t
this_val
)
{
return
sv_start_tla
(
js
,
func
,
this_val
);
}
static
inline
void
sv_sync_frame_locals
(
sv_vm_t
*
vm
,
sv_frame_t
**
frame
,
sv_func_t
**
func
,
ant_value_t
**
bp
,
ant_value_t
**
lp
)
{
*
frame
=
&
vm
->
frames
[
vm
->
fp
];
*
func
=
(
*
frame
)
->
func
;
*
bp
=
(
*
frame
)
->
bp
;
*
lp
=
(
*
frame
)
->
lp
;
}
static
inline
void
sv_drop_frame_runtime_state
(
ant_t
*
js
,
sv_frame_t
*
frame
)
{
if
(
frame
&&
vtype
(
frame
->
arguments_obj
)
!=
T_UNDEF
)
{
js_arguments_detach
(
js
,
frame
->
arguments_obj
);
frame
->
arguments_obj
=
js_mkundef
();
}}
static
inline
ant_value_t
sv_stage_frame_args
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_func_t
*
func
,
ant_value_t
*
args
,
int
argc
,
ant_value_t
**
out_bp
,
ant_value_t
**
out_lp
)
{
int
arg_slots
=
(
argc
>
func
->
param_count
)
?
argc
:
func
->
param_count
;
int
need
=
arg_slots
+
func
->
max_locals
;
if
(
vm
->
sp
+
need
>
vm
->
stack_size
)
{
int
args_idx
=
(
args
&&
args
>=
vm
->
stack
&&
args
<
vm
->
stack
+
vm
->
stack_size
)
?
(
int
)(
args
-
vm
->
stack
)
:
-1
;
while
(
vm
->
sp
+
need
>
vm
->
stack_size
)
{
if
(
!
sv_vm_grow_stack
(
vm
))
return
js_mkerr
(
js
,
"stack overflow"
);
}
if
(
args_idx
>=
0
)
args
=
&
vm
->
stack
[
args_idx
];
}
ant_value_t
*
base
=
&
vm
->
stack
[
vm
->
sp
];
*
out_bp
=
base
;
*
out_lp
=
base
+
arg_slots
;
if
(
argc
>
0
&&
args
)
memmove
(
base
,
args
,
(
size_t
)
argc
*
sizeof
(
ant_value_t
));
for
(
int
i
=
argc
;
i
<
arg_slots
;
i
++
)
base
[
i
]
=
js_mkundef
();
if
(
func
->
max_locals
>
0
)
for
(
int
i
=
0
;
i
<
func
->
max_locals
;
i
++
)
(
*
out_lp
)[
i
]
=
js_mkundef
();
vm
->
sp
+=
need
;
return
js_mkundef
();
}
static
inline
void
sv_yield_star_store_state
(
sv_vm_t
*
vm
,
ant_value_t
*
lp
,
uint16_t
base
)
{
lp
[
base
+
0
]
=
vm
->
stack
[
vm
->
sp
-
3
];
lp
[
base
+
1
]
=
vm
->
stack
[
vm
->
sp
-
2
];
lp
[
base
+
2
]
=
vm
->
stack
[
vm
->
sp
-
1
];
}
static
inline
void
sv_yield_star_push_state
(
sv_vm_t
*
vm
,
ant_value_t
*
lp
,
uint16_t
base
)
{
vm
->
stack
[
vm
->
sp
++
]
=
lp
[
base
+
0
];
vm
->
stack
[
vm
->
sp
++
]
=
lp
[
base
+
1
];
vm
->
stack
[
vm
->
sp
++
]
=
lp
[
base
+
2
];
}
static
inline
void
sv_yield_star_clear_state
(
ant_t
*
js
,
ant_value_t
*
lp
,
uint16_t
base
)
{
lp
[
base
+
0
]
=
js_mkundef
();
lp
[
base
+
1
]
=
js_mkundef
();
lp
[
base
+
2
]
=
js_mkundef
();
lp
[
base
+
3
]
=
js_false
;
}
static
inline
ant_value_t
sv_yield_star_unpack_result
(
ant_t
*
js
,
ant_value_t
result
,
ant_value_t
*
out_value
,
bool
*
out_done
)
{
if
(
!
is_object_type
(
result
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Iterator result is not an object"
);
ant_value_t
done
=
js_mkundef
();
sv_iter_result_unpack
(
js
,
result
,
&
done
,
out_value
);
if
(
is_err
(
done
))
return
done
;
if
(
is_err
(
*
out_value
))
return
*
out_value
;
*
out_done
=
js_truthy
(
js
,
done
);
return
js_mkundef
();
}
static
inline
ant_value_t
sv_yield_star_call_method
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
iterator
,
const
char
*
name
,
ant_value_t
arg
,
ant_value_t
*
out_value
,
bool
*
out_done
)
{
ant_value_t
method
=
js_getprop_fallback
(
js
,
iterator
,
name
);
uint8_t
mt
=
vtype
(
method
);
if
(
mt
!=
T_FUNC
&&
mt
!=
T_CFUNC
)
{
*
out_value
=
js_mkundef
();
*
out_done
=
true
;
return
js_mkundef
();
}
ant_value_t
call_args
[
1
]
=
{
arg
};
ant_value_t
result
=
sv_vm_call
(
vm
,
js
,
method
,
iterator
,
call_args
,
1
,
NULL
,
false
);
if
(
is_err
(
result
))
return
result
;
return
sv_yield_star_unpack_result
(
js
,
result
,
out_value
,
out_done
);
}
static
inline
ant_value_t
sv_yield_star_close_iterator
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
iterator
)
{
ant_value_t
close_value
=
js_mkundef
();
bool
close_done
=
true
;
return
sv_yield_star_call_method
(
vm
,
js
,
iterator
,
"return"
,
js_mkundef
(),
&
close_value
,
&
close_done
);
}
static
inline
ant_value_t
sv_yield_star_next
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
*
lp
,
uint16_t
base
,
ant_value_t
sent
,
ant_value_t
*
out_value
,
bool
*
out_done
)
{
int
tag
=
(
int
)
js_getnum
(
lp
[
base
+
2
]);
bool
first
=
js_truthy
(
js
,
lp
[
base
+
3
]);
lp
[
base
+
3
]
=
js_false
;
if
(
tag
==
SV_ITER_GENERIC
)
{
ant_value_t
iterator
=
lp
[
base
+
0
];
ant_value_t
next_method
=
lp
[
base
+
1
];
uint8_t
nt
=
vtype
(
next_method
);
if
(
nt
!=
T_FUNC
&&
nt
!=
T_CFUNC
)
return
js_mkerr
(
js
,
"iterator.next is not a function"
);
ant_value_t
call_args
[
1
]
=
{
sent
};
ant_value_t
result
=
sv_vm_call
(
vm
,
js
,
next_method
,
iterator
,
first
?
NULL
:
call_args
,
first
?
0
:
1
,
NULL
,
false
);
if
(
is_err
(
result
))
return
result
;
return
sv_yield_star_unpack_result
(
js
,
result
,
out_value
,
out_done
);
}
sv_yield_star_push_state
(
vm
,
lp
,
base
);
ant_value_t
status
=
sv_iter_advance
(
vm
,
js
,
0
,
out_value
,
out_done
);
if
(
is_err
(
status
))
return
status
;
sv_yield_star_store_state
(
vm
,
lp
,
base
);
vm
->
sp
-=
3
;
return
js_mkundef
();
}
static
inline
ant_value_t
sv_yield_star_throw
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
*
lp
,
uint16_t
base
,
ant_value_t
thrown
,
ant_value_t
*
out_value
,
bool
*
out_done
)
{
int
tag
=
(
int
)
js_getnum
(
lp
[
base
+
2
]);
if
(
tag
!=
SV_ITER_GENERIC
)
return
js_throw
(
js
,
thrown
);
ant_value_t
iterator
=
lp
[
base
+
0
];
ant_value_t
throw_method
=
js_getprop_fallback
(
js
,
iterator
,
"throw"
);
uint8_t
tt
=
vtype
(
throw_method
);
if
(
tt
!=
T_FUNC
&&
tt
!=
T_CFUNC
)
{
ant_value_t
close_status
=
sv_yield_star_close_iterator
(
vm
,
js
,
iterator
);
if
(
is_err
(
close_status
))
return
close_status
;
return
js_throw
(
js
,
thrown
);
}
ant_value_t
call_args
[
1
]
=
{
thrown
};
ant_value_t
result
=
sv_vm_call
(
vm
,
js
,
throw_method
,
iterator
,
call_args
,
1
,
NULL
,
false
);
if
(
is_err
(
result
))
return
result
;
return
sv_yield_star_unpack_result
(
js
,
result
,
out_value
,
out_done
);
}
static
inline
ant_value_t
sv_yield_star_return
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
*
lp
,
uint16_t
base
,
ant_value_t
value
,
ant_value_t
*
out_value
,
bool
*
out_done
)
{
int
tag
=
(
int
)
js_getnum
(
lp
[
base
+
2
]);
if
(
tag
!=
SV_ITER_GENERIC
)
{
*
out_value
=
value
;
*
out_done
=
true
;
return
js_mkundef
();
}
return
sv_yield_star_call_method
(
vm
,
js
,
lp
[
base
+
0
],
"return"
,
value
,
out_value
,
out_done
);
}
static
inline
ant_value_t
sv_execute_entry_common
(
sv_vm_t
*
vm
,
sv_func_t
*
func
,
sv_upvalue_t
**
upvalues
,
int
upvalue_count
,
ant_value_t
callee_func
,
ant_value_t
super_val
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
,
ant_value_t
*
out_this
)
{
if
(
!
vm
||
!
vm
->
js
||
!
func
)
return
mkval
(
T_ERR
,
0
);
ant_t
*
js
=
vm
->
js
;
if
(
vm
->
fp
+
1
>=
vm
->
max_frames
&&
!
sv_vm_grow_frames
(
vm
))
return
js_mkerr_typed
(
js
,
JS_ERR_RANGE
|
JS_ERR_NO_STACK
,
"Maximum call stack size exceeded"
);
int
saved_fp
=
vm
->
fp
;
vm
->
fp
=
saved_fp
+
1
;
vm
->
frames
[
vm
->
fp
].
upvalues
=
upvalues
;
vm
->
frames
[
vm
->
fp
].
upvalue_count
=
upvalue_count
;
vm
->
frames
[
vm
->
fp
].
callee
=
callee_func
;
ant_value_t
result
=
sv_execute_frame
(
vm
,
func
,
this_val
,
super_val
,
args
,
argc
);
if
(
out_this
)
*
out_this
=
vm
->
frames
[
vm
->
fp
].
this
;
if
(
!
vm
->
suspended
)
vm
->
fp
=
saved_fp
;
return
result
;
}
#ifdef ANT_JIT
static
inline
ant_value_t
sv_try_direct_closure_jit
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_func_t
*
caller_func
,
uint8_t
*
caller_ip
,
sv_frame_t
*
caller_frame
,
sv_closure_t
*
closure
,
ant_value_t
jit_this
,
ant_value_t
*
call_args
,
int
call_argc
)
{
sv_func_t
*
callee
=
closure
->
func
;
if
(
!
callee
)
return
SV_JIT_RETRY_INTERP
;
if
(
caller_func
&&
caller_func
->
type_feedback
&&
caller_ip
)
sv_tfb_record_call_target
(
caller_func
,
(
int
)(
caller_ip
-
caller_func
->
code
),
callee
);
if
(
callee
->
jit_code
)
{
if
(
caller_frame
&&
caller_ip
)
caller_frame
->
ip
=
caller_ip
+
3
;
sv_jit_enter
(
js
);
ant_value_t
jit_result
=
((
sv_jit_func_t
)
callee
->
jit_code
)(
vm
,
jit_this
,
js_mkundef
(),
closure
->
super_val
,
call_args
,
call_argc
,
closure
);
sv_jit_leave
(
js
);
if
(
sv_is_jit_bailout
(
jit_result
))
{
sv_jit_on_bailout
(
callee
);
return
SV_JIT_RETRY_INTERP
;
}
return
jit_result
;
}
if
(
callee
->
is_generator
)
return
SV_JIT_RETRY_INTERP
;
uint32_t
cc
=
++
callee
->
call_count
;
if
(
__builtin_expect
(
cc
==
SV_TFB_ALLOC_THRESHOLD
,
0
))
sv_tfb_ensure
(
callee
);
if
(
cc
<=
SV_JIT_THRESHOLD
)
return
SV_JIT_RETRY_INTERP
;
sv_jit_func_t
jit_fn
=
sv_jit_compile
(
js
,
callee
,
closure
);
if
(
!
jit_fn
)
{
callee
->
call_count
=
0
;
callee
->
back_edge_count
=
0
;
return
SV_JIT_RETRY_INTERP
;
}
callee
->
jit_code
=
(
void
*
)
jit_fn
;
if
(
caller_frame
&&
caller_ip
)
caller_frame
->
ip
=
caller_ip
+
3
;
sv_jit_enter
(
js
);
ant_value_t
jit_result
=
jit_fn
(
vm
,
jit_this
,
js_mkundef
(),
closure
->
super_val
,
call_args
,
call_argc
,
closure
);
sv_jit_leave
(
js
);
if
(
sv_is_jit_bailout
(
jit_result
))
{
sv_jit_on_bailout
(
callee
);
return
SV_JIT_RETRY_INTERP
;
}
return
jit_result
;
}
#endif
ant_value_t
sv_execute_entry
(
sv_vm_t
*
vm
,
sv_func_t
*
func
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
)
{
return
sv_execute_entry_common
(
vm
,
func
,
NULL
,
0
,
js_mkundef
(),
js_mkundef
(),
this_val
,
args
,
argc
,
NULL
);
}
ant_value_t
sv_execute_closure_entry
(
sv_vm_t
*
vm
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
ant_value_t
super_val
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
,
ant_value_t
*
out_this
)
{
if
(
!
closure
||
!
closure
->
func
)
return
mkval
(
T_ERR
,
0
);
return
sv_execute_entry_common
(
vm
,
closure
->
func
,
closure
->
upvalues
,
closure
->
func
->
upvalue_count
,
callee_func
,
super_val
,
this_val
,
args
,
argc
,
out_this
);
}
ant_value_t
sv_execute_frame
(
sv_vm_t
*
vm
,
sv_func_t
*
func
,
ant_value_t
this
,
ant_value_t
super_val
,
ant_value_t
*
args
,
int
argc
)
{
ant_t
*
js
=
vm
->
js
;
bool
resuming
=
vm
->
suspended
&&
vm
->
suspended_resume_pending
;
uint8_t
*
ip
=
resuming
?
NULL
:
func
->
code
;
int
entry_fp
=
resuming
?
vm
->
suspended_entry_fp
:
vm
->
fp
;
ant_value_t
vm_result
=
js_mkundef
();
ant_value_t
suspended_resume_value
=
js_mkundef
();
sv_resume_kind_t
suspended_resume_kind
=
SV_RESUME_NEXT
;
js
->
vm_exec_depth
++
;
// TODO: shorthand?
sv_frame_t
*
frame
=
&
vm
->
frames
[
vm
->
fp
];
if
(
!
resuming
)
{
sv_drop_frame_runtime_state
(
js
,
frame
);
frame
->
ip
=
ip
;
frame
->
func
=
func
;
frame
->
this
=
sv_normalize_this_for_frame
(
js
,
func
,
this
);
frame
->
new_target
=
js
->
new_target
;
frame
->
super_val
=
super_val
;
frame
->
prev_sp
=
vm
->
sp
;
frame
->
handler_base
=
vm
->
handler_depth
;
frame
->
handler_top
=
vm
->
handler_depth
;
frame
->
argc
=
argc
;
frame
->
completion
.
kind
=
SV_COMPLETION_NONE
;
frame
->
completion
.
value
=
js_mkundef
();
frame
->
with_obj
=
js_mkundef
();
frame
->
arguments_obj
=
js_mkundef
();
}
else
{
func
=
frame
->
func
;
ip
=
frame
->
ip
;
suspended_resume_value
=
vm
->
suspended_resume_value
;
suspended_resume_kind
=
vm
->
suspended_resume_kind
;
vm
->
suspended
=
false
;
vm
->
suspended_resume_pending
=
false
;
vm
->
suspended_resume_is_error
=
false
;
vm
->
suspended_resume_kind
=
SV_RESUME_NEXT
;
vm
->
suspended_resume_value
=
js_mkundef
();
}
ant_value_t
*
entry_bp
=
NULL
;
ant_value_t
*
entry_lp
=
NULL
;
if
(
!
resuming
)
{
ant_value_t
stage_err
=
sv_stage_frame_args
(
vm
,
js
,
func
,
args
,
argc
,
&
entry_bp
,
&
entry_lp
);
if
(
is_err
(
stage_err
))
{
vm_result
=
stage_err
;
goto
sv_leave
;
}}
#ifdef ANT_JIT
if
(
!
resuming
&&
vm
->
jit_resume
.
active
)
{
ip
=
func
->
code
+
vm
->
jit_resume
.
ip_offset
;
frame
->
ip
=
ip
;
int64_t
rl
=
vm
->
jit_resume
.
n_locals
<
func
->
max_locals
?
vm
->
jit_resume
.
n_locals
:
func
->
max_locals
;
for
(
int64_t
i
=
0
;
i
<
rl
;
i
++
)
entry_lp
[
i
]
=
vm
->
jit_resume
.
locals
[
i
];
ant_value_t
*
old_lp
=
vm
->
jit_resume
.
locals
;
for
(
sv_upvalue_t
*
uv
=
vm
->
open_upvalues
;
uv
;
uv
=
uv
->
next
)
{
if
(
uv
->
location
>=
old_lp
&&
uv
->
location
<
old_lp
+
rl
)
{
ptrdiff_t
slot
=
uv
->
location
-
old_lp
;
uv
->
location
=
&
entry_lp
[
slot
];
}}
}
#endif
if
(
!
resuming
)
{
frame
->
bp
=
entry_bp
;
frame
->
lp
=
entry_lp
;
}
ant_value_t
*
bp
=
frame
->
bp
;
ant_value_t
*
lp
=
frame
->
lp
;
#ifdef ANT_JIT
if
(
!
resuming
&&
vm
->
jit_resume
.
active
)
{
for
(
int64_t
i
=
0
;
i
<
vm
->
jit_resume
.
vstack_sp
;
i
++
)
vm
->
stack
[
vm
->
sp
++
]
=
vm
->
jit_resume
.
vstack
[
i
];
int
resume_off
=
(
int
)
vm
->
jit_resume
.
ip_offset
;
uint8_t
*
scan
=
func
->
code
;
uint8_t
*
scan_end
=
func
->
code
+
resume_off
;
typedef
struct
{
uint8_t
*
catch_ip
;
int
saved_sp
;
}
pending_h
;
pending_h
h_stack
[
SV_TRY_MAX
];
int
enclosing_count
=
0
;
int
total_depth
=
0
;
bool
is_enclosing
[
SV_TRY_MAX
];
while
(
scan
<
scan_end
)
{
sv_op_t
scan_op
=
(
sv_op_t
)
*
scan
;
int
scan_sz
=
sv_op_size
[
scan_op
];
if
(
scan_sz
==
0
)
break
;
if
(
scan_op
==
OP_TRY_PUSH
&&
total_depth
<
SV_TRY_MAX
)
{
int32_t
off
=
sv_get_i32
(
scan
+
1
);
int
catch_off
=
(
int
)(
scan
-
func
->
code
)
+
scan_sz
+
off
;
is_enclosing
[
total_depth
]
=
(
catch_off
>
resume_off
);
if
(
is_enclosing
[
total_depth
])
{
h_stack
[
enclosing_count
].
catch_ip
=
func
->
code
+
catch_off
;
h_stack
[
enclosing_count
].
saved_sp
=
vm
->
sp
;
enclosing_count
++
;
}
total_depth
++
;
}
else
if
(
scan_op
==
OP_TRY_POP
&&
total_depth
>
0
)
{
total_depth
--
;
if
(
is_enclosing
[
total_depth
])
enclosing_count
--
;
}
scan
+=
scan_sz
;
}
for
(
int
i
=
0
;
i
<
enclosing_count
;
i
++
)
{
if
(
vm
->
handler_depth
<
SV_HANDLER_MAX
)
{
sv_handler_t
*
h
=
&
vm
->
handler_stack
[
vm
->
handler_depth
++
];
h
->
kind
=
SV_HANDLER_TRY
;
h
->
ip
=
h_stack
[
i
].
catch_ip
;
h
->
saved_sp
=
h_stack
[
i
].
saved_sp
;
frame
->
handler_top
=
vm
->
handler_depth
;
}
}
vm
->
jit_resume
.
active
=
false
;
}
#endif
static
const
void
*
dispatch
[
OP__COUNT
]
=
{
#define OP_DEF(name, size, n_pop, n_push, f) [OP_##name] = &&L_##name,
#include
"silver/opcode.h"
};
ant_value_t
sv_err
;
ant_value_t
tc_this
=
js_mkundef
();
#define VM_CHECK(expr) ({ \
frame->ip = ip; \
sv_err = (expr); \
if (is_err(sv_err)) goto sv_throw; \
})
#define DISPATCH() goto *dispatch[*ip]
#define NEXT(n) ({ ip += (n); DISPATCH(); })
#ifdef ANT_JIT
#define JIT_OSR_BACK_EDGE() do { \
if (!func->jit_compile_failed) { \
if (!func->type_feedback) sv_tfb_ensure(func); \
if (++func->back_edge_count >= SV_JIT_OSR_THRESHOLD) { \
ant_value_t osr_r = sv_jit_try_osr( \
vm, js, frame, func, \
(int)(ip - func->code)); \
if (osr_r != SV_JIT_RETRY_INTERP) { \
if (is_err(osr_r)) { sv_err = osr_r; goto sv_throw; } \
vm->sp = frame->prev_sp; \
if (vm->fp <= entry_fp) { \
vm_result = osr_r; \
goto sv_leave; \
} \
vm->fp--; \
frame = &vm->frames[vm->fp]; \
func = frame->func; \
bp = frame->bp; \
lp = frame->lp; \
ip = frame->ip; \
vm->stack[vm->sp++] = osr_r; \
DISPATCH(); \
} \
} \
} \
} while (0)
#else
#define JIT_OSR_BACK_EDGE() ((void)0)
#endif
if
(
resuming
)
{
bool
yield_star_resume
=
ip
&&
(
*
ip
==
OP_YIELD_STAR_NEXT
||
*
ip
==
OP_YIELD_STAR_THROW
||
*
ip
==
OP_YIELD_STAR_RETURN
);
if
(
suspended_resume_kind
==
SV_RESUME_THROW
&&
!
yield_star_resume
)
{
sv_err
=
js_throw
(
js
,
suspended_resume_value
);
goto
sv_throw
;
}
if
(
suspended_resume_kind
==
SV_RESUME_RETURN
&&
!
yield_star_resume
)
{
vm
->
stack
[
vm
->
sp
++
]
=
suspended_resume_value
;
goto
L_RETURN
;
}
vm
->
stack
[
vm
->
sp
++
]
=
suspended_resume_value
;
}
DISPATCH
();
L_CONST
:
{
sv_op_const
(
vm
,
func
,
ip
);
NEXT
(
5
);
}
L_CONST_I8
:
{
sv_op_const_i8
(
vm
,
ip
);
NEXT
(
2
);
}
L_CONST8
:
{
sv_op_const8
(
vm
,
func
,
ip
);
NEXT
(
2
);
}
L_UNDEF
:
{
sv_op_undef
(
vm
);
NEXT
(
1
);
}
L_NULL
:
{
sv_op_null
(
vm
);
NEXT
(
1
);
}
L_TRUE
:
{
sv_op_true
(
vm
);
NEXT
(
1
);
}
L_FALSE
:
{
sv_op_false
(
vm
);
NEXT
(
1
);
}
L_THIS
:
{
sv_op_this
(
vm
,
frame
);
NEXT
(
1
);
}
L_GLOBAL
:
{
sv_op_global
(
vm
,
js
);
NEXT
(
1
);
}
L_OBJECT
:
{
sv_op_object
(
vm
,
js
,
func
,
ip
);
NEXT
(
1
);
}
L_ARRAY
:
{
sv_op_array
(
vm
,
js
,
ip
);
NEXT
(
3
);
}
L_REGEXP
:
{
sv_op_regexp
(
vm
,
js
);
NEXT
(
1
);
}
L_CLOSURE
:
{
VM_CHECK
(
sv_op_closure
(
vm
,
js
,
frame
,
func
,
ip
));
NEXT
(
5
);
}
L_POP
:
{
sv_op_pop
(
vm
);
NEXT
(
1
);
}
L_DUP
:
{
sv_op_dup
(
vm
);
NEXT
(
1
);
}
L_DUP2
:
{
sv_op_dup2
(
vm
);
NEXT
(
1
);
}
L_SWAP
:
{
sv_op_swap
(
vm
);
NEXT
(
1
);
}
L_ROT3L
:
{
sv_op_rot3l
(
vm
);
NEXT
(
1
);
}
L_ROT3R
:
{
sv_op_rot3r
(
vm
);
NEXT
(
1
);
}
L_NIP
:
{
sv_op_nip
(
vm
);
NEXT
(
1
);
}
L_NIP2
:
{
sv_op_nip2
(
vm
);
NEXT
(
1
);
}
L_INSERT2
:
{
sv_op_insert2
(
vm
);
NEXT
(
1
);
}
L_INSERT3
:
{
sv_op_insert3
(
vm
);
NEXT
(
1
);
}
L_SWAP_UNDER
:
{
sv_op_swap_under
(
vm
);
NEXT
(
1
);
}
L_ROT4_UNDER
:
{
sv_op_rot4_under
(
vm
);
NEXT
(
1
);
}
L_GET_LOCAL
:
{
VM_CHECK
(
sv_op_get_local
(
vm
,
lp
,
js
,
frame
,
ip
));
NEXT
(
3
);
}
L_PUT_LOCAL
:
{
sv_op_put_local
(
vm
,
lp
,
frame
,
func
,
ip
);
NEXT
(
3
);
}
L_SET_LOCAL
:
{
sv_op_set_local
(
vm
,
lp
,
frame
,
func
,
ip
);
NEXT
(
3
);
}
L_GET_LOCAL8
:
{
VM_CHECK
(
sv_op_get_local8
(
vm
,
lp
,
js
,
frame
,
ip
));
NEXT
(
2
);
}
L_PUT_LOCAL8
:
{
sv_op_put_local8
(
vm
,
lp
,
frame
,
func
,
ip
);
NEXT
(
2
);
}
L_SET_LOCAL8
:
{
sv_op_set_local8
(
vm
,
lp
,
frame
,
func
,
ip
);
NEXT
(
2
);
}
L_SET_LOCAL_UNDEF
:
{
sv_op_set_local_undef
(
frame
,
lp
,
ip
);
NEXT
(
3
);
}
L_GET_LOCAL_CHK
:
{
VM_CHECK
(
sv_op_get_local_chk
(
vm
,
lp
,
js
,
frame
,
func
,
ip
));
NEXT
(
7
);
}
L_PUT_LOCAL_CHK
:
{
VM_CHECK
(
sv_op_put_local_chk
(
vm
,
lp
,
js
,
frame
,
func
,
ip
));
NEXT
(
7
);
}
L_GET_SLOT_RAW
:
{
VM_CHECK
(
sv_op_get_slot_raw
(
vm
,
js
,
frame
,
ip
));
NEXT
(
3
);
}
L_GET_ARG
:
{
VM_CHECK
(
sv_op_get_arg
(
vm
,
js
,
frame
,
ip
));
NEXT
(
3
);
}
L_PUT_ARG
:
{
sv_op_put_arg
(
vm
,
js
,
frame
,
ip
);
NEXT
(
3
);
}
L_SET_ARG
:
{
sv_op_set_arg
(
vm
,
js
,
frame
,
ip
);
NEXT
(
3
);
}
L_REST
:
{
sv_op_rest
(
vm
,
frame
,
js
,
ip
);
NEXT
(
3
);
}
L_GET_UPVAL
:
{
VM_CHECK
(
sv_op_get_upval
(
vm
,
frame
,
js
,
ip
));
NEXT
(
3
);
}
L_PUT_UPVAL
:
{
sv_op_put_upval
(
vm
,
frame
,
ip
);
NEXT
(
3
);
}
L_SET_UPVAL
:
{
sv_op_set_upval
(
vm
,
frame
,
ip
);
NEXT
(
3
);
}
L_CLOSE_UPVAL
:
{
VM_CHECK
(
sv_op_close_upval
(
vm
,
frame
,
ip
));
NEXT
(
3
);
}
L_GET_GLOBAL
:
{
VM_CHECK
(
sv_op_get_global
(
vm
,
js
,
func
,
ip
));
NEXT
(
7
);
}
L_GET_GLOBAL_UNDEF
:
{
sv_op_get_global_undef
(
vm
,
js
,
func
,
ip
);
NEXT
(
7
);
}
L_PUT_GLOBAL
:
{
VM_CHECK
(
sv_op_put_global
(
vm
,
js
,
frame
,
func
,
ip
));
NEXT
(
5
);
}
L_GET_FIELD
:
{
VM_CHECK
(
sv_op_get_field
(
vm
,
js
,
func
,
ip
));
NEXT
(
7
);
}
L_GET_FIELD2
:
{
VM_CHECK
(
sv_op_get_field2
(
vm
,
js
,
func
,
ip
));
NEXT
(
7
);
}
L_PUT_FIELD
:
{
VM_CHECK
(
sv_op_put_field
(
vm
,
js
,
func
,
ip
));
NEXT
(
7
);
}
L_GET_ELEM
:
{
VM_CHECK
(
sv_op_get_elem
(
vm
,
js
,
func
,
ip
));
NEXT
(
1
);
}
L_GET_ELEM2
:
{
VM_CHECK
(
sv_op_get_elem2
(
vm
,
js
,
func
,
ip
));
NEXT
(
1
);
}
L_PUT_ELEM
:
{
VM_CHECK
(
sv_op_put_elem
(
vm
,
js
));
NEXT
(
1
);
}
L_DEFINE_FIELD
:
{
sv_op_define_field
(
vm
,
js
,
func
,
ip
);
NEXT
(
5
);
}
L_GET_LENGTH
:
{
VM_CHECK
(
sv_op_get_length
(
vm
,
js
));
NEXT
(
1
);
}
L_GET_FIELD_OPT
:
{
VM_CHECK
(
sv_op_get_field_opt
(
vm
,
js
,
func
,
ip
));
NEXT
(
5
);
}
L_GET_ELEM_OPT
:
{
VM_CHECK
(
sv_op_get_elem_opt
(
vm
,
js
,
func
,
ip
));
NEXT
(
1
);
}
L_GET_PRIVATE
:
{
sv_op_get_private
(
vm
,
js
);
NEXT
(
1
);
}
L_PUT_PRIVATE
:
{
sv_op_put_private
(
vm
,
js
);
NEXT
(
1
);
}
L_DEF_PRIVATE
:
{
sv_op_def_private
(
vm
,
js
);
NEXT
(
1
);
}
L_GET_SUPER
:
{
sv_op_get_super
(
vm
,
js
);
NEXT
(
1
);
}
L_GET_SUPER_VAL
:
{
sv_op_get_super_val
(
vm
,
js
,
frame
);
NEXT
(
1
);
}
L_PUT_SUPER_VAL
:
{
sv_op_put_super_val
(
vm
,
js
);
NEXT
(
1
);
}
L_ADD
:
{
ant_value_t
r
=
vm
->
stack
[
vm
->
sp
-
1
],
l
=
vm
->
stack
[
vm
->
sp
-
2
];
sv_tfb_record2
(
func
,
ip
,
l
,
r
);
if
(
__builtin_expect
(
vtype
(
l
)
==
T_NUM
&&
vtype
(
r
)
==
T_NUM
,
1
))
{
vm
->
sp
--
;
vm
->
stack
[
vm
->
sp
-
1
]
=
tov
(
tod
(
l
)
+
tod
(
r
));
NEXT
(
1
);
}
VM_CHECK
(
sv_op_add
(
vm
,
js
));
NEXT
(
1
);
}
L_ADD_NUM
:
{
ant_value_t
r
=
vm
->
stack
[
--
vm
->
sp
];
ant_value_t
l
=
vm
->
stack
[
vm
->
sp
-
1
];
vm
->
stack
[
vm
->
sp
-
1
]
=
tov
(
tod
(
l
)
+
tod
(
r
));
NEXT
(
1
);
}
L_SUB
:
{
ant_value_t
r
=
vm
->
stack
[
vm
->
sp
-
1
],
l
=
vm
->
stack
[
vm
->
sp
-
2
];
sv_tfb_record2
(
func
,
ip
,
l
,
r
);
if
(
__builtin_expect
(
vtype
(
l
)
==
T_NUM
&&
vtype
(
r
)
==
T_NUM
,
1
))
{
vm
->
sp
--
;
vm
->
stack
[
vm
->
sp
-
1
]
=
tov
(
tod
(
l
)
-
tod
(
r
));
NEXT
(
1
);
}
VM_CHECK
(
sv_op_sub
(
vm
,
js
));
NEXT
(
1
);
}
L_SUB_NUM
:
{
ant_value_t
r
=
vm
->
stack
[
--
vm
->
sp
];
ant_value_t
l
=
vm
->
stack
[
vm
->
sp
-
1
];
vm
->
stack
[
vm
->
sp
-
1
]
=
tov
(
tod
(
l
)
-
tod
(
r
));
NEXT
(
1
);
}
L_MUL_NUM
:
{
ant_value_t
r
=
vm
->
stack
[
--
vm
->
sp
];
ant_value_t
l
=
vm
->
stack
[
vm
->
sp
-
1
];
vm
->
stack
[
vm
->
sp
-
1
]
=
tov
(
tod
(
l
)
*
tod
(
r
));
NEXT
(
1
);
}
L_DIV_NUM
:
{
ant_value_t
r
=
vm
->
stack
[
--
vm
->
sp
];
ant_value_t
l
=
vm
->
stack
[
vm
->
sp
-
1
];
vm
->
stack
[
vm
->
sp
-
1
]
=
tov
(
tod
(
l
)
/
tod
(
r
));
NEXT
(
1
);
}
L_MUL
:
{
sv_tfb_record2
(
func
,
ip
,
vm
->
stack
[
vm
->
sp
-2
],
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_mul
(
vm
,
js
));
NEXT
(
1
);
}
L_DIV
:
{
sv_tfb_record2
(
func
,
ip
,
vm
->
stack
[
vm
->
sp
-2
],
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_div
(
vm
,
js
));
NEXT
(
1
);
}
L_MOD
:
{
sv_tfb_record2
(
func
,
ip
,
vm
->
stack
[
vm
->
sp
-2
],
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_mod
(
vm
,
js
));
NEXT
(
1
);
}
L_NEG
:
{
sv_tfb_record1
(
func
,
ip
,
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_neg
(
vm
,
js
));
NEXT
(
1
);
}
L_ADD_LOCAL
:
{
sv_tfb_record2
(
func
,
ip
,
lp
[
sv_get_u8
(
ip
+
1
)],
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_add_local
(
vm
,
lp
,
js
,
func
,
ip
));
NEXT
(
2
);
}
L_STR_APPEND_LOCAL
:
{
VM_CHECK
(
sv_op_str_append_local
(
vm
,
js
,
frame
,
func
,
ip
));
NEXT
(
3
);
}
L_STR_ALC_SNAPSHOT
:
{
VM_CHECK
(
sv_op_str_append_local_snapshot
(
vm
,
js
,
frame
,
func
,
ip
));
NEXT
(
3
);
}
L_STR_FLUSH_LOCAL
:
{
VM_CHECK
(
sv_op_str_flush_local
(
vm
,
js
,
frame
,
ip
));
NEXT
(
3
);
}
L_EXP
:
{
VM_CHECK
(
sv_op_exp
(
vm
,
js
));
NEXT
(
1
);
}
L_UPLUS
:
{
VM_CHECK
(
sv_op_uplus
(
vm
,
js
));
NEXT
(
1
);
}
L_INC
:
{
sv_op_inc
(
vm
);
NEXT
(
1
);
}
L_DEC
:
{
sv_op_dec
(
vm
);
NEXT
(
1
);
}
L_POST_INC
:
{
sv_op_post_inc
(
vm
);
NEXT
(
1
);
}
L_POST_DEC
:
{
sv_op_post_dec
(
vm
);
NEXT
(
1
);
}
L_INC_LOCAL
:
{
VM_CHECK
(
sv_op_inc_local
(
lp
,
js
,
func
,
ip
));
NEXT
(
2
);
}
L_DEC_LOCAL
:
{
VM_CHECK
(
sv_op_dec_local
(
lp
,
js
,
func
,
ip
));
NEXT
(
2
);
}
L_EQ
:
{
sv_op_eq
(
vm
,
js
);
NEXT
(
1
);
}
L_NE
:
{
sv_op_ne
(
vm
,
js
);
NEXT
(
1
);
}
L_SEQ
:
{
sv_op_seq
(
vm
,
js
);
NEXT
(
1
);
}
L_SNE
:
{
sv_op_sne
(
vm
,
js
);
NEXT
(
1
);
}
L_LT
:
{
ant_value_t
r
=
vm
->
stack
[
vm
->
sp
-
1
],
l
=
vm
->
stack
[
vm
->
sp
-
2
];
sv_tfb_record2
(
func
,
ip
,
l
,
r
);
if
(
__builtin_expect
(
vtype
(
l
)
==
T_NUM
&&
vtype
(
r
)
==
T_NUM
,
1
))
{
vm
->
sp
--
;
vm
->
stack
[
vm
->
sp
-
1
]
=
mkval
(
T_BOOL
,
tod
(
l
)
<
tod
(
r
));
NEXT
(
1
);
}
VM_CHECK
(
sv_op_lt
(
vm
,
js
));
NEXT
(
1
);
}
L_LE
:
{
sv_tfb_record2
(
func
,
ip
,
vm
->
stack
[
vm
->
sp
-2
],
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_le
(
vm
,
js
));
NEXT
(
1
);
}
L_GT
:
{
sv_tfb_record2
(
func
,
ip
,
vm
->
stack
[
vm
->
sp
-2
],
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_gt
(
vm
,
js
));
NEXT
(
1
);
}
L_GE
:
{
sv_tfb_record2
(
func
,
ip
,
vm
->
stack
[
vm
->
sp
-2
],
vm
->
stack
[
vm
->
sp
-1
]);
VM_CHECK
(
sv_op_ge
(
vm
,
js
));
NEXT
(
1
);
}
L_INSTANCEOF
:
{
VM_CHECK
(
sv_op_instanceof
(
vm
,
js
,
func
,
ip
));
NEXT
(
3
);
}
L_IN
:
{
VM_CHECK
(
sv_op_in
(
vm
,
js
));
NEXT
(
1
);
}
L_IS_NULLISH
:
{
sv_op_is_nullish
(
vm
);
NEXT
(
1
);
}
L_IS_UNDEF_OR_NULL
:
{
sv_op_is_undef_or_null
(
vm
);
NEXT
(
1
);
}
L_BAND
:
{
VM_CHECK
(
sv_op_band
(
vm
,
js
));
NEXT
(
1
);
}
L_BOR
:
{
VM_CHECK
(
sv_op_bor
(
vm
,
js
));
NEXT
(
1
);
}
L_BXOR
:
{
VM_CHECK
(
sv_op_bxor
(
vm
,
js
));
NEXT
(
1
);
}
L_BNOT
:
{
VM_CHECK
(
sv_op_bnot
(
vm
,
js
));
NEXT
(
1
);
}
L_SHL
:
{
VM_CHECK
(
sv_op_shl
(
vm
,
js
));
NEXT
(
1
);
}
L_SHR
:
{
VM_CHECK
(
sv_op_shr
(
vm
,
js
));
NEXT
(
1
);
}
L_USHR
:
{
VM_CHECK
(
sv_op_ushr
(
vm
,
js
));
NEXT
(
1
);
}
L_NOT
:
{
sv_op_not
(
vm
,
js
);
NEXT
(
1
);
}
L_TYPEOF
:
{
sv_op_typeof
(
vm
,
js
);
NEXT
(
1
);
}
L_VOID
:
{
sv_op_void
(
vm
);
NEXT
(
1
);
}
L_DELETE
:
{
VM_CHECK
(
sv_op_delete
(
vm
,
js
));
NEXT
(
1
);
}
L_DELETE_VAR
:
{
sv_op_delete_var
(
vm
,
js
,
func
,
ip
);
NEXT
(
5
);
}
L_JMP
:
{
uint8_t
*
prev
=
ip
;
ip
=
sv_op_jmp
(
ip
);
if
(
ip
<=
prev
)
{
gc_maybe
(
js
);
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP_FALSE
:
{
uint8_t
*
prev
=
ip
;
ant_value_t
v
=
vm
->
stack
[
--
vm
->
sp
];
if
(
__builtin_expect
(
vtype
(
v
)
==
T_BOOL
,
1
))
{
ip
=
vdata
(
v
)
?
ip
+
sv_op_size
[
OP_JMP_FALSE
]
:
ip
+
sv_op_size
[
OP_JMP_FALSE
]
+
sv_get_i32
(
ip
+
1
);
}
else
{
ip
=
js_truthy
(
js
,
v
)
?
ip
+
sv_op_size
[
OP_JMP_FALSE
]
:
ip
+
sv_op_size
[
OP_JMP_FALSE
]
+
sv_get_i32
(
ip
+
1
);
}
if
(
ip
<=
prev
)
{
js
->
prop_refs_len
=
0
;
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP_TRUE
:
{
uint8_t
*
prev
=
ip
;
ant_value_t
v
=
vm
->
stack
[
--
vm
->
sp
];
if
(
__builtin_expect
(
vtype
(
v
)
==
T_BOOL
,
1
))
{
ip
=
vdata
(
v
)
?
ip
+
sv_op_size
[
OP_JMP_TRUE
]
+
sv_get_i32
(
ip
+
1
)
:
ip
+
sv_op_size
[
OP_JMP_TRUE
];
}
else
{
ip
=
js_truthy
(
js
,
v
)
?
ip
+
sv_op_size
[
OP_JMP_TRUE
]
+
sv_get_i32
(
ip
+
1
)
:
ip
+
sv_op_size
[
OP_JMP_TRUE
];
}
if
(
ip
<=
prev
)
{
js
->
prop_refs_len
=
0
;
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP_FALSE_PEEK
:
{
uint8_t
*
prev
=
ip
;
ip
=
sv_op_jmp_false_peek
(
vm
,
js
,
ip
);
if
(
ip
<=
prev
)
{
js
->
prop_refs_len
=
0
;
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP_TRUE_PEEK
:
{
uint8_t
*
prev
=
ip
;
ip
=
sv_op_jmp_true_peek
(
vm
,
js
,
ip
);
if
(
ip
<=
prev
)
{
js
->
prop_refs_len
=
0
;
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP_NOT_NULLISH
:
{
uint8_t
*
prev
=
ip
;
ip
=
sv_op_jmp_not_nullish
(
vm
,
ip
);
if
(
ip
<=
prev
)
{
js
->
prop_refs_len
=
0
;
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP8
:
{
uint8_t
*
prev
=
ip
;
ip
=
sv_op_jmp8
(
ip
);
if
(
ip
<=
prev
)
{
js
->
prop_refs_len
=
0
;
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP_FALSE8
:
{
uint8_t
*
prev
=
ip
;
ip
=
sv_op_jmp_false8
(
vm
,
js
,
ip
);
if
(
ip
<=
prev
)
{
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_JMP_TRUE8
:
{
uint8_t
*
prev
=
ip
;
ip
=
sv_op_jmp_true8
(
vm
,
js
,
ip
);
if
(
ip
<=
prev
)
{
JIT_OSR_BACK_EDGE
();
}
DISPATCH
();
}
L_CALL
:
{
uint16_t
call_argc
=
sv_get_u16
(
ip
+
1
);
ant_value_t
*
call_args
=
&
vm
->
stack
[
vm
->
sp
-
call_argc
];
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
1
];
bool
is_super_call
=
(
vtype
(
frame
->
super_val
)
!=
T_UNDEF
&&
call_func
==
frame
->
super_val
);
ant_value_t
call_this
=
is_super_call
?
frame
->
this
:
js_mkundef
();
if
(
!
is_super_call
&&
vtype
(
frame
->
new_target
)
==
T_UNDEF
&&
vtype
(
call_func
)
==
T_FUNC
)
{
sv_closure_t
*
closure
=
js_func_closure
(
call_func
);
if
(
closure
->
func
!=
NULL
)
{
if
(
closure
->
call_flags
&
(
SV_CALL_HAS_BOUND_ARGS
|
SV_CALL_HAS_SUPER
))
goto
call_fallback
;
if
(
closure
->
func
->
is_async
||
closure
->
func
->
is_generator
)
goto
call_fallback
;
#ifdef ANT_JIT
{
ant_value_t
jit_this
=
(
closure
->
func
->
is_arrow
||
vtype
(
closure
->
bound_this
)
!=
T_UNDEF
)
?
closure
->
bound_this
:
js_mkundef
();
ant_value_t
jit_result
=
sv_try_direct_closure_jit
(
vm
,
js
,
func
,
ip
,
frame
,
closure
,
jit_this
,
call_args
,
(
int
)
call_argc
);
if
(
jit_result
!=
SV_JIT_RETRY_INTERP
)
{
vm
->
sp
-=
call_argc
+
1
;
if
(
is_err
(
jit_result
))
{
sv_err
=
jit_result
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
jit_result
;
ip
=
frame
->
ip
;
DISPATCH
();
}
}
#endif
if
(
vm
->
fp
+
1
>=
vm
->
max_frames
&&
!
sv_vm_grow_frames
(
vm
))
{
sv_err
=
js_mkerr_typed
(
js
,
JS_ERR_RANGE
|
JS_ERR_NO_STACK
,
"Maximum call stack size exceeded"
);
goto
sv_throw
;
}
if
(
closure
->
func
->
is_arrow
||
vtype
(
closure
->
bound_this
)
!=
T_UNDEF
)
call_this
=
closure
->
bound_this
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
frame
->
ip
=
ip
+
3
;
vm
->
sp
-=
call_argc
+
1
;
vm
->
fp
++
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
closure
->
func
;
frame
->
func
=
func
;
frame
->
callee
=
call_func
;
frame
->
this
=
sv_normalize_this_for_frame
(
js
,
func
,
call_this
);
frame
->
new_target
=
js_mkundef
();
frame
->
super_val
=
js_mkundef
();
frame
->
prev_sp
=
vm
->
sp
;
frame
->
handler_base
=
vm
->
handler_depth
;
frame
->
handler_top
=
vm
->
handler_depth
;
frame
->
argc
=
call_argc
;
frame
->
arguments_obj
=
js_mkundef
();
ant_value_t
*
call_bp
=
NULL
;
ant_value_t
*
call_lp
=
NULL
;
ant_value_t
call_stage_err
=
sv_stage_frame_args
(
vm
,
js
,
func
,
call_args
,
(
int
)
call_argc
,
&
call_bp
,
&
call_lp
);
if
(
is_err
(
call_stage_err
))
{
vm
->
fp
--
;
vm
->
sp
+=
call_argc
+
1
;
sv_err
=
call_stage_err
;
goto
sv_throw
;
}
frame
->
bp
=
call_bp
;
frame
->
lp
=
call_lp
;
frame
->
upvalues
=
closure
->
upvalues
;
frame
->
upvalue_count
=
closure
->
func
->
upvalue_count
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
func
->
code
;
DISPATCH
();
}
}
call_fallback
:;
frame
->
ip
=
ip
;
ant_value_t
super_this_c
=
call_this
;
ant_value_t
call_result
=
sv_vm_call
(
vm
,
js
,
call_func
,
call_this
,
call_args
,
call_argc
,
is_super_call
?
&
super_this_c
:
NULL
,
is_super_call
);
sv_sync_frame_locals
(
vm
,
&
frame
,
&
func
,
&
bp
,
&
lp
);
vm
->
sp
-=
call_argc
+
1
;
if
(
is_err
(
call_result
))
{
sv_err
=
call_result
;
goto
sv_throw
;
}
if
(
is_super_call
)
frame
->
this
=
is_object_type
(
call_result
)
?
call_result
:
super_this_c
;
vm
->
stack
[
vm
->
sp
++
]
=
call_result
;
NEXT
(
3
);
}
L_CALL_METHOD
:
{
uint16_t
call_argc
=
sv_get_u16
(
ip
+
1
);
ant_value_t
*
call_args
=
&
vm
->
stack
[
vm
->
sp
-
call_argc
];
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
1
];
ant_value_t
call_this
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
2
];
bool
is_super_call
=
(
vtype
(
frame
->
super_val
)
!=
T_UNDEF
&&
call_func
==
frame
->
super_val
);
if
(
!
is_super_call
&&
vtype
(
frame
->
new_target
)
==
T_UNDEF
&&
vtype
(
call_func
)
==
T_FUNC
)
{
sv_closure_t
*
closure
=
js_func_closure
(
call_func
);
if
(
closure
->
func
!=
NULL
)
{
if
(
closure
->
call_flags
&
(
SV_CALL_HAS_BOUND_ARGS
|
SV_CALL_HAS_SUPER
))
goto
call_method_fallback
;
if
(
closure
->
func
->
is_async
||
closure
->
func
->
is_generator
)
goto
call_method_fallback
;
#ifdef ANT_JIT
{
ant_value_t
jit_this
=
(
closure
->
func
->
is_arrow
||
vtype
(
closure
->
bound_this
)
!=
T_UNDEF
)
?
closure
->
bound_this
:
call_this
;
ant_value_t
jit_result
=
sv_try_direct_closure_jit
(
vm
,
js
,
func
,
ip
,
frame
,
closure
,
jit_this
,
call_args
,
(
int
)
call_argc
);
if
(
jit_result
!=
SV_JIT_RETRY_INTERP
)
{
vm
->
sp
-=
call_argc
+
2
;
if
(
is_err
(
jit_result
))
{
sv_err
=
jit_result
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
jit_result
;
ip
=
frame
->
ip
;
DISPATCH
();
}
}
#endif
if
(
vm
->
fp
+
1
>=
vm
->
max_frames
&&
!
sv_vm_grow_frames
(
vm
))
{
sv_err
=
js_mkerr_typed
(
js
,
JS_ERR_RANGE
|
JS_ERR_NO_STACK
,
"Maximum call stack size exceeded"
);
goto
sv_throw
;
}
if
(
closure
->
func
->
is_arrow
||
vtype
(
closure
->
bound_this
)
!=
T_UNDEF
)
call_this
=
closure
->
bound_this
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
frame
->
ip
=
ip
+
3
;
vm
->
sp
-=
call_argc
+
2
;
vm
->
fp
++
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
closure
->
func
;
frame
->
func
=
func
;
frame
->
callee
=
call_func
;
frame
->
this
=
sv_normalize_this_for_frame
(
js
,
func
,
call_this
);
frame
->
new_target
=
js_mkundef
();
frame
->
super_val
=
js_mkundef
();
frame
->
prev_sp
=
vm
->
sp
;
frame
->
handler_base
=
vm
->
handler_depth
;
frame
->
handler_top
=
vm
->
handler_depth
;
frame
->
argc
=
call_argc
;
frame
->
arguments_obj
=
js_mkundef
();
ant_value_t
*
call_bp
=
NULL
;
ant_value_t
*
call_lp
=
NULL
;
ant_value_t
call_stage_err
=
sv_stage_frame_args
(
vm
,
js
,
func
,
call_args
,
(
int
)
call_argc
,
&
call_bp
,
&
call_lp
);
if
(
is_err
(
call_stage_err
))
{
vm
->
fp
--
;
vm
->
sp
+=
call_argc
+
2
;
sv_err
=
call_stage_err
;
goto
sv_throw
;
}
frame
->
bp
=
call_bp
;
frame
->
lp
=
call_lp
;
frame
->
upvalues
=
closure
->
upvalues
;
frame
->
upvalue_count
=
closure
->
func
->
upvalue_count
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
func
->
code
;
DISPATCH
();
}
}
call_method_fallback
:;
frame
->
ip
=
ip
;
if
(
is_super_call
)
js
->
new_target
=
frame
->
new_target
;
ant_value_t
super_this_cm
=
call_this
;
ant_value_t
call_result
=
sv_vm_call
(
vm
,
js
,
call_func
,
call_this
,
call_args
,
call_argc
,
is_super_call
?
&
super_this_cm
:
NULL
,
is_super_call
);
sv_sync_frame_locals
(
vm
,
&
frame
,
&
func
,
&
bp
,
&
lp
);
vm
->
sp
-=
call_argc
+
2
;
if
(
is_err
(
call_result
))
{
sv_err
=
call_result
;
goto
sv_throw
;
}
if
(
is_super_call
)
frame
->
this
=
is_object_type
(
call_result
)
?
call_result
:
super_this_cm
;
vm
->
stack
[
vm
->
sp
++
]
=
call_result
;
NEXT
(
3
);
}
L_CALL_ARRAY_INCLUDES
:
{
uint16_t
call_argc
=
sv_get_u16
(
ip
+
1
);
ant_value_t
*
call_args
=
&
vm
->
stack
[
vm
->
sp
-
call_argc
];
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
1
];
ant_value_t
call_this
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
2
];
ant_value_t
call_result
;
frame
->
ip
=
ip
;
if
(
js_is_array_includes_builtin
(
call_func
))
{
call_result
=
js_array_includes_call
(
js
,
call_this
,
call_args
,
call_argc
);
}
else
call_result
=
sv_vm_call
(
vm
,
js
,
call_func
,
call_this
,
call_args
,
call_argc
,
NULL
,
false
);
sv_sync_frame_locals
(
vm
,
&
frame
,
&
func
,
&
bp
,
&
lp
);
vm
->
sp
-=
call_argc
+
2
;
if
(
is_err
(
call_result
))
{
sv_err
=
call_result
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
call_result
;
NEXT
(
3
);
}
L_CALL_IS_PROTO
:
{
ant_value_t
call_arg
=
vm
->
stack
[
vm
->
sp
-
1
];
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
2
];
ant_value_t
call_this
=
vm
->
stack
[
vm
->
sp
-
3
];
ant_value_t
call_result
;
if
(
vtype
(
call_func
)
==
T_CFUNC
&&
js_cfunc_same_entrypoint
(
call_func
,
builtin_object_isPrototypeOf
)
)
call_result
=
sv_isproto_ic_eval
(
js
,
call_this
,
call_arg
,
func
,
ip
);
else
{
ant_value_t
call_args
[
1
]
=
{
call_arg
};
frame
->
ip
=
ip
;
call_result
=
sv_vm_call
(
vm
,
js
,
call_func
,
call_this
,
call_args
,
1
,
NULL
,
false
);
sv_sync_frame_locals
(
vm
,
&
frame
,
&
func
,
&
bp
,
&
lp
);
}
vm
->
sp
-=
3
;
if
(
is_err
(
call_result
))
{
sv_err
=
call_result
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
call_result
;
NEXT
(
3
);
}
L_RE_EXEC_TRUTHY
:
{
ant_value_t
call_arg
=
vm
->
stack
[
vm
->
sp
-
1
];
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
2
];
ant_value_t
call_this
=
vm
->
stack
[
vm
->
sp
-
3
];
ant_value_t
call_result
;
if
(
!
regexp_exec_truthy_try_fast
(
js
,
call_func
,
call_this
,
call_arg
,
&
call_result
))
{
ant_value_t
call_args
[
1
]
=
{
call_arg
};
frame
->
ip
=
ip
;
ant_value_t
raw_result
=
sv_vm_call
(
vm
,
js
,
call_func
,
call_this
,
call_args
,
1
,
NULL
,
false
);
sv_sync_frame_locals
(
vm
,
&
frame
,
&
func
,
&
bp
,
&
lp
);
if
(
is_err
(
raw_result
))
call_result
=
raw_result
;
else
call_result
=
mkval
(
T_BOOL
,
js_truthy
(
js
,
raw_result
)
?
1
:
0
);
}
vm
->
sp
-=
3
;
if
(
is_err
(
call_result
))
{
sv_err
=
call_result
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
call_result
;
NEXT
(
1
);
}
L_TAIL_CALL
:
{
uint16_t
call_argc
=
sv_get_u16
(
ip
+
1
);
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
1
];
tc_this
=
js_mkundef
();
if
(
vm
->
handler_depth
==
frame
->
handler_base
&&
vtype
(
frame
->
new_target
)
==
T_UNDEF
&&
vtype
(
call_func
)
==
T_FUNC
)
{
sv_closure_t
*
closure
=
js_func_closure
(
call_func
);
if
(
closure
->
func
!=
NULL
)
{
if
(
!
closure
->
func
->
is_async
&&
!
closure
->
func
->
is_generator
&&
!
(
closure
->
call_flags
&
(
SV_CALL_HAS_BOUND_ARGS
|
SV_CALL_HAS_SUPER
)))
{
if
(
closure
->
func
->
is_arrow
||
vtype
(
closure
->
bound_this
)
!=
T_UNDEF
)
tc_this
=
closure
->
bound_this
;
goto
tail_call_inline
;
}
}
}
ant_value_t
*
call_args
=
&
vm
->
stack
[
vm
->
sp
-
call_argc
];
frame
->
ip
=
ip
;
ant_value_t
call_result
=
sv_vm_call
(
vm
,
js
,
call_func
,
tc_this
,
call_args
,
call_argc
,
NULL
,
false
);
sv_sync_frame_locals
(
vm
,
&
frame
,
&
func
,
&
bp
,
&
lp
);
vm
->
sp
-=
call_argc
+
1
;
if
(
is_err
(
call_result
))
{
sv_err
=
call_result
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
call_result
;
goto
L_RETURN
;
}
L_TAIL_CALL_METHOD
:
{
uint16_t
call_argc
=
sv_get_u16
(
ip
+
1
);
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
1
];
tc_this
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
2
];
if
(
vm
->
handler_depth
==
frame
->
handler_base
&&
vtype
(
frame
->
new_target
)
==
T_UNDEF
&&
vtype
(
call_func
)
==
T_FUNC
)
{
sv_closure_t
*
closure
=
js_func_closure
(
call_func
);
if
(
closure
->
func
!=
NULL
)
{
if
(
!
closure
->
func
->
is_async
&&
!
closure
->
func
->
is_generator
&&
!
(
closure
->
call_flags
&
(
SV_CALL_HAS_BOUND_ARGS
|
SV_CALL_HAS_SUPER
)))
{
if
(
closure
->
func
->
is_arrow
||
vtype
(
closure
->
bound_this
)
!=
T_UNDEF
)
tc_this
=
closure
->
bound_this
;
goto
tail_call_inline
;
}
}
}
ant_value_t
*
call_args
=
&
vm
->
stack
[
vm
->
sp
-
call_argc
];
ant_value_t
call_result
=
sv_vm_call
(
vm
,
js
,
call_func
,
tc_this
,
call_args
,
call_argc
,
NULL
,
false
);
sv_sync_frame_locals
(
vm
,
&
frame
,
&
func
,
&
bp
,
&
lp
);
vm
->
sp
-=
call_argc
+
2
;
if
(
is_err
(
call_result
))
{
sv_err
=
call_result
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
call_result
;
goto
L_RETURN
;
}
tail_call_inline
:
{
uint16_t
call_argc
=
sv_get_u16
(
ip
+
1
);
ant_value_t
*
call_args
=
&
vm
->
stack
[
vm
->
sp
-
call_argc
];
ant_value_t
call_func
=
vm
->
stack
[
vm
->
sp
-
call_argc
-
1
];
sv_closure_t
*
closure
=
js_func_closure
(
call_func
);
if
(
frame
->
bp
&&
vm
->
open_upvalues
)
sv_close_upvalues_from_slot
(
vm
,
frame
->
bp
);
sv_drop_frame_runtime_state
(
js
,
frame
);
vm
->
sp
=
frame
->
prev_sp
;
int
arg_slots
=
(
(
int
)
call_argc
>
closure
->
func
->
param_count
)
?
(
int
)
call_argc
:
closure
->
func
->
param_count
;
int
need
=
arg_slots
+
closure
->
func
->
max_locals
;
ant_value_t
*
base
=
&
vm
->
stack
[
vm
->
sp
];
memmove
(
base
,
call_args
,
(
size_t
)
call_argc
*
sizeof
(
ant_value_t
));
for
(
int
i
=
(
int
)
call_argc
;
i
<
arg_slots
;
i
++
)
base
[
i
]
=
js_mkundef
();
ant_value_t
*
new_lp
=
base
+
arg_slots
;
for
(
int
i
=
0
;
i
<
closure
->
func
->
max_locals
;
i
++
)
new_lp
[
i
]
=
js_mkundef
();
vm
->
sp
=
frame
->
prev_sp
+
need
;
func
=
closure
->
func
;
frame
->
func
=
func
;
frame
->
callee
=
call_func
;
frame
->
this
=
sv_normalize_this_for_frame
(
js
,
func
,
tc_this
);
frame
->
new_target
=
js_mkundef
();
frame
->
super_val
=
js_mkundef
();
frame
->
argc
=
call_argc
;
frame
->
handler_base
=
vm
->
handler_depth
;
frame
->
handler_top
=
vm
->
handler_depth
;
frame
->
arguments_obj
=
js_mkundef
();
frame
->
bp
=
base
;
frame
->
lp
=
new_lp
;
frame
->
upvalues
=
closure
->
upvalues
;
frame
->
upvalue_count
=
closure
->
func
->
upvalue_count
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
func
->
code
;
DISPATCH
();
}
L_NEW
:
{
VM_CHECK
(
sv_op_new
(
vm
,
js
,
ip
));
NEXT
(
3
);
}
L_APPLY
:
{
VM_CHECK
(
sv_op_apply
(
vm
,
js
,
ip
));
NEXT
(
3
);
}
L_SUPER_APPLY
:
{
VM_CHECK
(
sv_op_super_apply
(
vm
,
js
,
frame
,
ip
));
NEXT
(
3
);
}
L_NEW_APPLY
:
{
VM_CHECK
(
sv_op_new_apply
(
vm
,
js
,
ip
));
NEXT
(
3
);
}
L_EVAL
:
{
VM_CHECK
(
sv_op_eval
(
vm
,
js
,
frame
,
ip
));
NEXT
(
5
);
}
// TODO: make the methods below DRY
L_RETURN
:
{
ant_value_t
r
=
vm
->
stack
[
--
vm
->
sp
];
if
(
__builtin_expect
(
vm
->
handler_depth
!=
frame
->
handler_base
,
0
))
{
uint8_t
*
finally_ip
=
sv_vm_unwind_for_return
(
vm
,
r
);
if
(
finally_ip
)
{
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
finally_ip
;
DISPATCH
();
}
vm
->
handler_depth
=
frame
->
handler_base
;
frame
->
handler_top
=
frame
->
handler_base
;
}
vm
->
sp
=
frame
->
prev_sp
;
if
(
vm
->
fp
<=
entry_fp
)
{
vm_result
=
r
;
goto
sv_leave
;
}
sv_drop_frame_runtime_state
(
js
,
frame
);
vm
->
fp
--
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
frame
->
ip
;
vm
->
stack
[
vm
->
sp
++
]
=
r
;
DISPATCH
();
}
L_RETURN_UNDEF
:
{
ant_value_t
r
=
js_mkundef
();
if
(
__builtin_expect
(
vm
->
handler_depth
!=
frame
->
handler_base
,
0
))
{
uint8_t
*
finally_ip
=
sv_vm_unwind_for_return
(
vm
,
r
);
if
(
finally_ip
)
{
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
finally_ip
;
DISPATCH
();
}
vm
->
handler_depth
=
frame
->
handler_base
;
frame
->
handler_top
=
frame
->
handler_base
;
}
vm
->
sp
=
frame
->
prev_sp
;
if
(
vm
->
fp
<=
entry_fp
)
{
vm_result
=
r
;
goto
sv_leave
;
}
sv_drop_frame_runtime_state
(
js
,
frame
);
vm
->
fp
--
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
frame
->
ip
;
vm
->
stack
[
vm
->
sp
++
]
=
r
;
DISPATCH
();
}
L_RETURN_ASYNC
:
{
ant_value_t
r
=
vm
->
stack
[
--
vm
->
sp
];
if
(
__builtin_expect
(
vm
->
handler_depth
!=
frame
->
handler_base
,
0
))
{
uint8_t
*
finally_ip
=
sv_vm_unwind_for_return
(
vm
,
r
);
if
(
finally_ip
)
{
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
finally_ip
;
DISPATCH
();
}
vm
->
handler_depth
=
frame
->
handler_base
;
frame
->
handler_top
=
frame
->
handler_base
;
}
vm
->
sp
=
frame
->
prev_sp
;
if
(
vm
->
fp
<=
entry_fp
)
{
vm_result
=
r
;
goto
sv_leave
;
}
sv_drop_frame_runtime_state
(
js
,
frame
);
vm
->
fp
--
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
frame
->
ip
;
vm
->
stack
[
vm
->
sp
++
]
=
r
;
DISPATCH
();
}
L_CHECK_CTOR
:
{
VM_CHECK
(
sv_op_check_ctor
(
vm
,
js
));
NEXT
(
1
);
}
L_CHECK_CTOR_RET
:
{
sv_op_check_ctor_ret
(
vm
,
frame
);
NEXT
(
1
);
}
L_HALT
:
{
vm_result
=
sv_op_halt
(
vm
,
frame
);
goto
sv_leave
;
}
L_THROW
:
{
sv_err
=
sv_op_throw
(
vm
);
goto
sv_throw
;
}
L_THROW_ERROR
:
{
sv_err
=
sv_op_throw_error
(
vm
,
js
,
func
,
ip
);
goto
sv_throw
;
}
L_TRY_PUSH
:
{
sv_op_try_push
(
vm
,
ip
);
NEXT
(
5
);
}
L_TRY_POP
:
{
sv_op_try_pop
(
vm
);
NEXT
(
1
);
}
L_CATCH
:
{
sv_op_catch
(
vm
,
sv_err
,
ip
);
NEXT
(
5
);
}
L_FINALLY
:
{
VM_CHECK
(
sv_op_finally
(
vm
,
js
,
ip
));
NEXT
(
5
);
}
L_NIP_CATCH
:
{
sv_op_nip_catch
(
vm
);
NEXT
(
1
);
}
L_FINALLY_RET
:
{
uint8_t
*
resume_ip
=
NULL
;
ant_value_t
completion
=
js_mkundef
();
sv_finally_ret_t
action
=
sv_op_finally_ret
(
vm
,
js
,
&
resume_ip
,
&
completion
);
if
(
action
==
SV_FINALLY_RET_ERROR
||
action
==
SV_FINALLY_RET_THROW
)
{
sv_err
=
completion
;
goto
sv_throw
;
}
if
(
action
==
SV_FINALLY_RET_RETURN
)
{
vm
->
handler_depth
=
frame
->
handler_base
;
frame
->
handler_top
=
frame
->
handler_base
;
vm
->
sp
=
frame
->
prev_sp
;
if
(
vm
->
fp
<=
entry_fp
)
{
vm_result
=
completion
;
goto
sv_leave
;
}
sv_drop_frame_runtime_state
(
js
,
frame
);
vm
->
fp
--
;
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
frame
->
ip
;
vm
->
stack
[
vm
->
sp
++
]
=
completion
;
DISPATCH
();
}
ip
=
resume_ip
;
DISPATCH
();
}
L_USING_PUSH
:
{
VM_CHECK
(
sv_op_using_push
(
vm
,
js
,
false
));
NEXT
(
1
);
}
L_USING_PUSH_ASYNC
:
{
VM_CHECK
(
sv_op_using_push
(
vm
,
js
,
true
));
NEXT
(
1
);
}
L_DISPOSE_RESOURCE
:
{
VM_CHECK
(
sv_op_dispose_resource
(
vm
,
js
,
false
));
NEXT
(
1
);
}
L_DISPOSE_RESOURCE_ASYNC
:
{
VM_CHECK
(
sv_op_dispose_resource
(
vm
,
js
,
true
));
NEXT
(
1
);
}
L_USING_DISPOSE
:
{
VM_CHECK
(
sv_op_using_dispose
(
vm
,
js
,
false
,
false
));
NEXT
(
1
);
}
L_USING_DISPOSE_ASYNC
:
{
VM_CHECK
(
sv_op_using_dispose
(
vm
,
js
,
true
,
false
));
NEXT
(
1
);
}
L_USING_DISPOSE_SUPPRESSED
:
{
VM_CHECK
(
sv_op_using_dispose
(
vm
,
js
,
false
,
true
));
NEXT
(
1
);
}
L_USING_DISPOSE_ASYNC_SUPPRESSED
:
{
VM_CHECK
(
sv_op_using_dispose
(
vm
,
js
,
true
,
true
));
NEXT
(
1
);
}
L_FOR_IN
:
{
VM_CHECK
(
sv_op_for_in
(
vm
,
js
));
NEXT
(
1
);
}
L_FOR_OF
:
{
VM_CHECK
(
sv_op_for_of
(
vm
,
js
));
NEXT
(
1
);
}
L_FOR_AWAIT_OF
:
{
VM_CHECK
(
sv_op_for_await_of
(
vm
,
js
));
NEXT
(
1
);
}
L_ITER_NEXT
:
{
VM_CHECK
(
sv_op_iter_next
(
vm
,
js
,
ip
));
NEXT
(
2
);
}
L_ITER_GET_VALUE
:
{
sv_op_iter_get_value
(
vm
,
js
);
NEXT
(
1
);
}
L_ITER_CLOSE
:
{
sv_op_iter_close
(
vm
,
js
);
NEXT
(
1
);
}
L_ITER_CALL
:
{
VM_CHECK
(
sv_op_iter_call
(
vm
,
js
,
ip
));
NEXT
(
2
);
}
L_AWAIT_ITER_NEXT
:
{
sv_await_result_t
await_result
=
sv_op_await_iter_next
(
vm
,
js
);
if
(
await_result
.
state
==
SV_AWAIT_ERROR
)
{
sv_err
=
await_result
.
value
;
goto
sv_throw
;
}
if
(
await_result
.
state
==
SV_AWAIT_SUSPENDED
)
{
if
(
await_result
.
handoff
)
vm_result
=
js_mkundef
();
goto
sv_leave
;
}
NEXT
(
1
);
}
L_DESTRUCTURE_INIT
:
{
VM_CHECK
(
sv_op_destructure_init
(
vm
,
js
));
NEXT
(
1
);
}
L_DESTRUCTURE_NEXT
:
{
VM_CHECK
(
sv_op_destructure_next
(
vm
,
js
));
NEXT
(
1
);
}
L_DESTRUCTURE_REST
:
{
VM_CHECK
(
sv_op_destructure_rest
(
vm
,
js
));
NEXT
(
1
);
}
L_DESTRUCTURE_CLOSE
:{
sv_op_destructure_close
(
vm
,
js
);
NEXT
(
1
);
}
L_AWAIT
:
{
ant_value_t
await_val
=
vm
->
stack
[
--
vm
->
sp
];
frame
->
ip
=
ip
+
1
;
vm
->
suspended_entry_fp
=
entry_fp
;
vm
->
suspended_saved_fp
=
entry_fp
-
1
;
sv_await_result_t
await_result
=
sv_await_value
(
vm
,
js
,
await_val
);
if
(
await_result
.
state
==
SV_AWAIT_SUSPENDED
&&
await_result
.
handoff
)
{
vm
->
suspended_entry_fp
=
-1
;
vm
->
suspended_saved_fp
=
-1
;
vm_result
=
js_mkundef
();
goto
sv_leave
;
}
if
(
await_result
.
state
==
SV_AWAIT_SUSPENDED
)
goto
sv_leave
;
vm
->
suspended_entry_fp
=
-1
;
vm
->
suspended_saved_fp
=
-1
;
if
(
await_result
.
state
==
SV_AWAIT_ERROR
)
{
sv_err
=
await_result
.
value
;
goto
sv_throw
;
}
vm
->
stack
[
vm
->
sp
++
]
=
await_result
.
value
;
NEXT
(
1
);
}
L_YIELD
:
{
ant_value_t
yielded
=
vm
->
stack
[
--
vm
->
sp
];
coroutine_t
*
coro
=
js
->
active_async_coro
;
if
(
!
coro
||
coro
->
type
!=
CORO_GENERATOR
)
{
sv_err
=
js_mkerr
(
js
,
"yield can only be used inside generator functions"
);
goto
sv_throw
;
}
coro
->
yield_value
=
yielded
;
coro
->
did_suspend
=
true
;
vm
->
suspended
=
true
;
vm
->
suspended_entry_fp
=
entry_fp
;
vm
->
suspended_saved_fp
=
entry_fp
-
1
;
frame
->
ip
=
ip
+
1
;
vm_result
=
yielded
;
goto
sv_leave
;
}
L_YIELD_STAR_INIT
:
{
uint16_t
base
=
sv_get_u16
(
ip
+
1
);
ant_value_t
iterable
=
vm
->
stack
[
--
vm
->
sp
];
vm
->
stack
[
vm
->
sp
++
]
=
iterable
;
VM_CHECK
(
sv_op_for_of
(
vm
,
js
));
sv_yield_star_store_state
(
vm
,
lp
,
base
);
vm
->
sp
-=
3
;
lp
[
base
+
3
]
=
js_true
;
NEXT
(
3
);
}
L_YIELD_STAR_NEXT
:
L_YIELD_STAR_THROW
:
L_YIELD_STAR_RETURN
:
{
coroutine_t
*
coro
=
js
->
active_async_coro
;
if
(
!
coro
||
coro
->
type
!=
CORO_GENERATOR
)
{
sv_err
=
js_mkerr
(
js
,
"yield can only be used inside generator functions"
);
goto
sv_throw
;
}
uint16_t
base
=
sv_get_u16
(
ip
+
1
);
ant_value_t
resume_value
=
vm
->
stack
[
--
vm
->
sp
];
ant_value_t
yielded
=
js_mkundef
();
bool
done
=
false
;
if
(
*
ip
==
OP_YIELD_STAR_THROW
||
suspended_resume_kind
==
SV_RESUME_THROW
)
{
VM_CHECK
(
sv_yield_star_throw
(
vm
,
js
,
lp
,
base
,
resume_value
,
&
yielded
,
&
done
));
}
else
if
(
*
ip
==
OP_YIELD_STAR_RETURN
||
suspended_resume_kind
==
SV_RESUME_RETURN
)
{
VM_CHECK
(
sv_yield_star_return
(
vm
,
js
,
lp
,
base
,
resume_value
,
&
yielded
,
&
done
));
if
(
done
)
{
sv_yield_star_clear_state
(
js
,
lp
,
base
);
vm
->
stack
[
vm
->
sp
++
]
=
yielded
;
goto
L_RETURN
;
}
}
else
VM_CHECK
(
sv_yield_star_next
(
vm
,
js
,
lp
,
base
,
resume_value
,
&
yielded
,
&
done
));
if
(
done
)
{
sv_yield_star_clear_state
(
js
,
lp
,
base
);
vm
->
stack
[
vm
->
sp
++
]
=
yielded
;
NEXT
(
3
);
}
coro
->
yield_value
=
yielded
;
coro
->
did_suspend
=
true
;
vm
->
suspended
=
true
;
vm
->
suspended_entry_fp
=
entry_fp
;
vm
->
suspended_saved_fp
=
entry_fp
-
1
;
frame
->
ip
=
ip
;
vm_result
=
yielded
;
goto
sv_leave
;
}
L_SPREAD
:
{
VM_CHECK
(
sv_op_spread
(
vm
,
js
));
NEXT
(
1
);
}
L_DEFINE_METHOD
:
{
sv_op_define_method
(
vm
,
js
,
func
,
ip
);
NEXT
(
6
);
}
L_DEFINE_METHOD_COMP
:
{
sv_op_define_method_comp
(
vm
,
js
,
ip
);
NEXT
(
2
);
}
L_SET_NAME
:
{
sv_op_set_name
(
vm
,
js
,
func
,
ip
);
NEXT
(
5
);
}
L_SET_NAME_COMP
:
{
sv_op_set_name_comp
(
vm
,
js
);
NEXT
(
1
);
}
L_SET_PROTO
:
{
sv_op_set_proto
(
vm
,
js
);
NEXT
(
1
);
}
L_SET_HOME_OBJ
:
{
sv_op_set_home_obj
(
vm
,
js
);
NEXT
(
1
);
}
L_APPEND
:
{
sv_op_append
(
vm
,
js
);
NEXT
(
1
);
}
L_COPY_DATA_PROPS
:
{
sv_op_copy_data_props
(
vm
,
js
,
ip
);
NEXT
(
2
);
}
L_DEFINE_CLASS
:
{
sv_op_define_class
(
vm
,
js
,
func
,
ip
);
NEXT
(
14
);
}
L_DEFINE_CLASS_COMP
:
{
sv_op_define_class_comp
(
vm
,
js
,
func
,
ip
);
NEXT
(
14
);
}
L_ADD_BRAND
:
{
sv_op_add_brand
(
vm
);
NEXT
(
1
);
}
L_TO_OBJECT
:
{
VM_CHECK
(
sv_op_to_object
(
vm
,
js
));
NEXT
(
1
);
}
L_TO_PROPKEY
:
{
sv_op_to_propkey
(
vm
,
js
);
NEXT
(
1
);
}
L_IS_UNDEF
:
{
sv_op_is_undef
(
vm
);
NEXT
(
1
);
}
L_IS_NULL
:
{
sv_op_is_null
(
vm
);
NEXT
(
1
);
}
L_IMPORT
:
{
VM_CHECK
(
sv_op_import
(
vm
,
js
));
NEXT
(
1
);
}
L_IMPORT_SYNC
:
{
VM_CHECK
(
sv_op_import_sync
(
vm
,
js
));
NEXT
(
1
);
}
L_IMPORT_DEFAULT
:
{
sv_op_import_default
(
vm
,
js
);
NEXT
(
1
);
}
L_IMPORT_NAMED
:
{
VM_CHECK
(
sv_op_import_named
(
vm
,
js
,
func
,
ip
));
NEXT
(
5
);
}
L_EXPORT
:
{
VM_CHECK
(
sv_op_export
(
vm
,
js
,
func
,
ip
));
NEXT
(
5
);
}
L_EXPORT_ALL
:
{
VM_CHECK
(
sv_op_export_all
(
vm
,
js
));
NEXT
(
1
);
}
L_ENTER_WITH
:
{
VM_CHECK
(
sv_op_enter_with
(
vm
,
js
,
frame
));
NEXT
(
1
);
}
L_EXIT_WITH
:
{
sv_op_exit_with
(
vm
,
frame
);
NEXT
(
1
);
}
L_WITH_GET_VAR
:
{
VM_CHECK
(
sv_op_with_get_var
(
vm
,
js
,
frame
,
func
,
ip
));
NEXT
(
8
);
}
L_WITH_PUT_VAR
:
{
sv_op_with_put_var
(
vm
,
js
,
frame
,
func
,
ip
);
NEXT
(
8
);
}
L_WITH_DEL_VAR
:
{
sv_op_with_del_var
(
vm
,
js
,
frame
,
func
,
ip
);
NEXT
(
5
);
}
L_SPECIAL_OBJ
:
{
sv_op_special_obj
(
vm
,
js
,
frame
,
ip
);
NEXT
(
2
);
}
L_EMPTY
:
{
vm
->
stack
[
vm
->
sp
++
]
=
T_EMPTY
;
NEXT
(
1
);
}
L_PUT_CONST
:
{
func
->
constants
[
sv_get_u32
(
ip
+
1
)]
=
vm
->
stack
[
--
vm
->
sp
];
NEXT
(
5
);
}
L_DEBUGGER
:
{
NEXT
(
1
);
}
L_NOP
:
{
NEXT
(
1
);
}
L_LABEL
:
L_LINE_NUM
:
L_COL_NUM
:
L_INVALID
:
sv_err
=
js_mkerr
(
js
,
"invalid opcode %d"
,
(
int
)
*
ip
);
goto
sv_throw
;
sv_throw
:
{
uint8_t
*
catch_ip
=
sv_vm_throw
(
vm
,
sv_err
,
entry_fp
);
if
(
catch_ip
)
{
frame
=
&
vm
->
frames
[
vm
->
fp
];
func
=
frame
->
func
;
bp
=
frame
->
bp
;
lp
=
frame
->
lp
;
ip
=
catch_ip
;
DISPATCH
();
}
if
(
!
is_err
(
sv_err
))
{
vm_result
=
js_throw
(
js
,
sv_err
);
goto
sv_leave
;
}
vm_result
=
sv_err
;
goto
sv_leave
;
}
sv_leave
:
if
(
vm
->
suspended
)
{
if
(
js
->
vm_exec_depth
>
0
)
js
->
vm_exec_depth
--
;
return
vm_result
;
}
if
(
vm
->
open_upvalues
)
{
for
(
int
f
=
vm
->
fp
;
f
>=
entry_fp
;
f
--
)
{
ant_value_t
*
drop_bp
=
vm
->
frames
[
f
].
bp
;
if
(
drop_bp
)
sv_close_upvalues_from_slot
(
vm
,
drop_bp
);
}}
for
(
int
f
=
vm
->
fp
;
f
>=
entry_fp
;
f
--
)
sv_drop_frame_runtime_state
(
js
,
&
vm
->
frames
[
f
]);
vm
->
fp
=
entry_fp
;
vm
->
sp
=
vm
->
frames
[
entry_fp
].
prev_sp
;
vm
->
handler_depth
=
vm
->
frames
[
entry_fp
].
handler_base
;
if
(
js
->
vm_exec_depth
>
0
)
js
->
vm_exec_depth
--
;
return
vm_result
;
#undef DISPATCH
#undef NEXT
#undef VM_CHECK
// TODO: use entry_bp/frame->bp
// and sv_op_size values
(
void
)
bp
;
(
void
)
sv_op_size
;
}
ant_value_t
sv_resume_suspended
(
sv_vm_t
*
vm
)
{
if
(
!
vm
||
!
vm
->
suspended
||
!
vm
->
suspended_resume_pending
||
vm
->
fp
<
0
)
return
mkval
(
T_ERR
,
0
);
// crash-resistance for missing frames
if
(
vm
->
suspended_entry_fp
<
0
||
vm
->
suspended_entry_fp
>
vm
->
fp
)
{
vm
->
suspended
=
false
;
vm
->
suspended_resume_pending
=
false
;
vm
->
suspended_resume_is_error
=
false
;
vm
->
suspended_resume_kind
=
SV_RESUME_NEXT
;
vm
->
suspended_resume_value
=
js_mkundef
();
vm
->
suspended_entry_fp
=
-1
;
vm
->
suspended_saved_fp
=
-1
;
return
vm
->
js
?
js_mkerr
(
vm
->
js
,
"invalid suspended entry frame"
)
:
mkval
(
T_ERR
,
0
);
}
int
saved_fp
=
vm
->
suspended_saved_fp
;
sv_frame_t
*
frame
=
&
vm
->
frames
[
vm
->
fp
];
if
(
!
vm
->
js
||
!
frame
->
func
||
!
frame
->
ip
)
{
vm
->
suspended
=
false
;
vm
->
suspended_resume_pending
=
false
;
vm
->
suspended_resume_is_error
=
false
;
vm
->
suspended_resume_kind
=
SV_RESUME_NEXT
;
vm
->
suspended_resume_value
=
js_mkundef
();
vm
->
suspended_entry_fp
=
-1
;
vm
->
suspended_saved_fp
=
-1
;
return
vm
->
js
?
js_mkerr
(
vm
->
js
,
"invalid suspended frame state"
)
:
mkval
(
T_ERR
,
0
);
}
ant_value_t
result
=
sv_execute_frame
(
vm
,
frame
->
func
,
frame
->
this
,
frame
->
super_val
,
NULL
,
frame
->
argc
);
if
(
!
vm
->
suspended
)
{
vm
->
fp
=
saved_fp
;
vm
->
suspended_entry_fp
=
-1
;
vm
->
suspended_saved_fp
=
-1
;
}
return
result
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 8:28 AM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
543161
Default Alt Text
engine.c (69 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment