Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4442835
engine.h
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
engine.h
View Options
#ifndef SILVER_ENGINE_H
#define SILVER_ENGINE_H
#include
"silver/vm.h"
#include
"internal.h"
#include
"runtime.h"
#include
"errors.h"
#include
"gc/objects.h"
#include
"modules/timer.h"
#include
<stdbool.h>
#include
<stdint.h>
#include
<stdlib.h>
#include
<string.h>
#include
<stdio.h>
typedef
enum
{
#define OP_DEF(name, size, n_pop, n_push, f) OP_##name,
#include
"silver/opcode.h"
OP__COUNT
}
sv_op_t
;
static
const
uint8_t
sv_op_size
[
OP__COUNT
]
=
{
#define OP_DEF(name, size, n_pop, n_push, f) [OP_##name] = (size),
#include
"silver/opcode.h"
};
typedef
struct
{
const
char
*
str
;
uint32_t
len
;
}
sv_atom_t
;
typedef
struct
{
uint16_t
index
;
bool
is_local
;
bool
is_const
;
}
sv_upval_desc_t
;
typedef
struct
{
uint32_t
bc_offset
;
uint32_t
line
;
uint32_t
col
;
uint32_t
src_off
;
uint32_t
src_end
;
}
sv_srcpos_t
;
typedef
enum
{
SV_TI_UNKNOWN
=
0
,
SV_TI_NUM
,
SV_TI_STR
,
SV_TI_ARR
,
SV_TI_OBJ
,
SV_TI_BOOL
,
SV_TI_NULL
,
SV_TI_UNDEF
,
}
sv_local_type_t
;
typedef
struct
{
uint8_t
type
;
}
sv_type_info_t
;
typedef
struct
{
ant_shape_t
*
cached_shape
;
ant_object_t
*
cached_holder
;
uint32_t
cached_index
;
uint32_t
epoch
;
uintptr_t
cached_aux
;
ant_shape_t
*
add_from_shape
;
ant_shape_t
*
add_to_shape
;
uint32_t
add_slot
;
uint32_t
add_epoch
;
bool
cached_is_own
;
}
sv_ic_entry_t
;
typedef
struct
{
uint32_t
bc_off
;
ant_shape_t
*
shared_shape
;
}
sv_obj_site_cache_t
;
#define SV_GF_IC_AUX_WARMUP_MASK ((uintptr_t)0xFFu)
#define SV_GF_IC_AUX_MISS_MASK ((uintptr_t)0xFF00u)
#define SV_GF_IC_AUX_ACTIVE_BIT ((uintptr_t)0x10000u)
#define SV_GF_IC_AUX_MISS_SHIFT 8u
#define SV_GF_IC_WARMUP_ENABLE 16u
#define SV_GF_IC_MISS_DISABLE 4u
static
inline
uint8_t
sv_gf_ic_warmup
(
uintptr_t
aux
)
{
return
(
uint8_t
)(
aux
&
SV_GF_IC_AUX_WARMUP_MASK
);
}
static
inline
uint8_t
sv_gf_ic_miss_streak
(
uintptr_t
aux
)
{
return
(
uint8_t
)((
aux
&
SV_GF_IC_AUX_MISS_MASK
)
>>
SV_GF_IC_AUX_MISS_SHIFT
);
}
static
inline
bool
sv_gf_ic_active
(
uintptr_t
aux
)
{
return
(
aux
&
SV_GF_IC_AUX_ACTIVE_BIT
)
!=
0
;
}
static
inline
uintptr_t
sv_gf_ic_pack_aux
(
uint8_t
warmup
,
uint8_t
miss_streak
,
bool
active
)
{
uintptr_t
aux
=
((
uintptr_t
)
warmup
&
SV_GF_IC_AUX_WARMUP_MASK
)
|
((
uintptr_t
)
miss_streak
<<
SV_GF_IC_AUX_MISS_SHIFT
);
if
(
active
)
aux
|=
SV_GF_IC_AUX_ACTIVE_BIT
;
return
aux
;
}
bool
sv_lookup_srcpos
(
sv_func_t
*
func
,
int
bc_offset
,
uint32_t
*
line
,
uint32_t
*
col
);
bool
sv_lookup_srcspan
(
sv_func_t
*
func
,
int
bc_offset
,
uint32_t
*
src_off
,
uint32_t
*
src_end
);
#ifdef ANT_JIT
typedef
struct
{
uint16_t
bc_off
;
uint8_t
miss_count
;
uint8_t
disabled
;
sv_func_t
*
target
;
}
sv_call_target_fb_t
;
#endif
struct
sv_func
{
uint8_t
*
code
;
int
code_len
;
ant_value_t
*
constants
;
int
const_count
;
struct
sv_func
**
child_funcs
;
int
child_func_count
;
uint32_t
*
gc_const_slots
;
int
gc_const_slot_count
;
sv_atom_t
*
atoms
;
int
atom_count
;
sv_ic_entry_t
*
ic_slots
;
uint16_t
ic_count
;
sv_obj_site_cache_t
*
obj_sites
;
uint16_t
obj_site_count
;
sv_upval_desc_t
*
upval_descs
;
int
max_locals
;
int
max_stack
;
sv_type_info_t
*
local_types
;
int
local_type_count
;
int
param_count
;
int
upvalue_count
;
bool
is_strict
;
bool
is_arrow
;
bool
is_async
;
bool
has_await
;
bool
is_generator
;
bool
is_method
;
bool
is_static
;
bool
is_tla
;
uint64_t
gc_epoch
;
const
char
*
name
;
const
char
*
filename
;
sv_srcpos_t
*
srcpos
;
int
srcpos_count
;
int
source_line
;
const
char
*
source
;
int
source_len
;
int
source_start
;
int
source_end
;
#ifdef ANT_JIT
void
*
jit_code
;
uint32_t
call_count
;
uint32_t
back_edge_count
;
bool
jit_compile_failed
;
bool
jit_compiling
;
uint32_t
tfb_version
;
uint32_t
jit_compiled_tfb_ver
;
uint8_t
*
type_feedback
;
uint8_t
*
local_type_feedback
;
uint64_t
ctor_prop_samples
;
uint64_t
ctor_prop_hist
[
17
];
uint8_t
ctor_inobj_limit
;
uint8_t
ctor_inobj_frozen
;
sv_call_target_fb_t
*
call_target_fb
;
uint8_t
call_target_fb_count
;
#endif
};
typedef
enum
{
SV_COMPLETION_NONE
=
0
,
SV_COMPLETION_THROW
=
1
,
SV_COMPLETION_RETURN
=
2
,
}
sv_completion_kind_t
;
typedef
struct
{
sv_completion_kind_t
kind
;
ant_value_t
value
;
}
sv_completion_t
;
typedef
enum
{
SV_RESUME_NEXT
=
0
,
SV_RESUME_THROW
=
1
,
SV_RESUME_RETURN
=
2
,
}
sv_resume_kind_t
;
typedef
struct
sv_upvalue
sv_upvalue_t
;
typedef
struct
sv_frame
{
uint8_t
*
ip
;
ant_value_t
*
bp
;
ant_value_t
*
lp
;
sv_func_t
*
func
;
ant_value_t
callee
;
ant_value_t
this
;
ant_value_t
new_target
;
ant_value_t
super_val
;
int
prev_sp
;
int
handler_base
;
int
handler_top
;
int
argc
;
sv_completion_t
completion
;
sv_upvalue_t
**
upvalues
;
int
upvalue_count
;
ant_value_t
with_obj
;
ant_value_t
arguments_obj
;
}
sv_frame_t
;
typedef
enum
{
SV_HANDLER_TRY
=
1
,
SV_HANDLER_FINALLY
=
2
,
}
sv_handler_kind_t
;
typedef
struct
{
uint8_t
*
ip
;
int
saved_sp
;
uint8_t
kind
;
}
sv_handler_t
;
struct
sv_upvalue
{
ant_value_t
*
location
;
ant_value_t
closed
;
struct
sv_upvalue
*
next
;
uint64_t
gc_epoch
;
};
static
inline
sv_upvalue_t
*
js_upvalue_alloc
(
void
)
{
return
(
sv_upvalue_t
*
)
fixed_arena_alloc
(
&
rt
->
js
->
upvalue_arena
);
}
bool
sv_slot_has_open_upvalue
(
sv_vm_t
*
vm
,
ant_value_t
*
slot
);
#define SV_CALL_HAS_BOUND_ARGS (1u << 0)
#define SV_CALL_HAS_SUPER (1u << 1)
#define SV_CALL_IS_ARROW (1u << 2)
#define SV_CALL_IS_DEFAULT_CTOR (1u << 3)
#define SV_CALL_BORROWED_UPVALS (1u << 4)
typedef
struct
sv_closure
{
uint32_t
call_flags
;
int
bound_argc
;
sv_func_t
*
func
;
sv_upvalue_t
**
upvalues
;
ant_value_t
bound_this
;
ant_value_t
*
bound_argv
;
ant_value_t
bound_args
;
ant_value_t
super_val
;
ant_value_t
func_obj
;
uint64_t
gc_epoch
;
}
sv_closure_t
;
static
inline
sv_closure_t
*
js_closure_alloc
(
ant_t
*
js
)
{
sv_closure_t
*
c
=
(
sv_closure_t
*
)
fixed_arena_alloc
(
&
js
->
closure_arena
);
if
(
c
)
c
->
gc_epoch
=
gc_get_epoch
();
return
c
;
}
static
inline
sv_closure_t
*
js_func_closure
(
ant_value_t
func
)
{
return
(
sv_closure_t
*
)(
uintptr_t
)
vdata
(
func
);
}
static
inline
ant_value_t
js_func_obj
(
ant_value_t
func
)
{
return
js_func_closure
(
func
)
->
func_obj
;
}
static
inline
ant_value_t
js_as_obj
(
ant_value_t
v
)
{
uint8_t
t
=
vtype
(
v
);
if
(
t
==
T_OBJ
)
return
v
;
if
(
t
==
T_FUNC
)
return
js_func_obj
(
v
);
return
mkval
(
T_OBJ
,
vdata
(
v
));
}
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
);
#ifdef ANT_JIT
typedef
struct
{
bool
active
;
int
bc_offset
;
ant_value_t
*
locals
;
int
n_locals
;
ant_value_t
*
lp
;
}
sv_jit_osr_t
;
#endif
#define SV_TRY_MAX 64
#define SV_TDZ T_EMPTY
#define SV_HANDLER_MAX (SV_TRY_MAX * 2)
#define SV_FRAMES_HARD_MAX 65536
#define SV_STACK_HARD_MAX 524288
struct
sv_vm
{
ant_t
*
js
;
ant_value_t
*
stack
;
int
sp
;
int
stack_size
;
sv_frame_t
*
frames
;
int
fp
;
int
max_frames
;
sv_handler_t
handler_stack
[
SV_HANDLER_MAX
];
sv_upvalue_t
*
open_upvalues
;
int
handler_depth
;
// TODO: move to nested struct
bool
suspended
;
bool
suspended_resume_pending
;
bool
suspended_resume_is_error
;
sv_resume_kind_t
suspended_resume_kind
;
int
suspended_entry_fp
;
int
suspended_saved_fp
;
ant_value_t
suspended_resume_value
;
#ifdef ANT_JIT
struct
{
bool
active
;
int64_t
ip_offset
;
ant_value_t
*
locals
;
int64_t
n_locals
;
ant_value_t
*
vstack
;
int64_t
vstack_sp
;
}
jit_resume
;
sv_jit_osr_t
jit_osr
;
#endif
};
static
inline
uint8_t
sv_get_u8
(
const
uint8_t
*
ip
)
{
return
ip
[
0
];
}
static
inline
int8_t
sv_get_i8
(
const
uint8_t
*
ip
)
{
return
(
int8_t
)
ip
[
0
];
}
static
inline
uint16_t
sv_get_u16
(
const
uint8_t
*
ip
)
{
uint16_t
v
;
memcpy
(
&
v
,
ip
,
2
);
return
v
;
}
static
inline
int16_t
sv_get_i16
(
const
uint8_t
*
ip
)
{
int16_t
v
;
memcpy
(
&
v
,
ip
,
2
);
return
v
;
}
static
inline
uint32_t
sv_get_u32
(
const
uint8_t
*
ip
)
{
uint32_t
v
;
memcpy
(
&
v
,
ip
,
4
);
return
v
;
}
static
inline
int32_t
sv_get_i32
(
const
uint8_t
*
ip
)
{
int32_t
v
;
memcpy
(
&
v
,
ip
,
4
);
return
v
;
}
static
inline
const
char
*
sv_atom_cstr
(
sv_atom_t
*
a
,
char
*
buf
,
size_t
bufsz
)
{
size_t
n
=
a
->
len
<
bufsz
-
1
?
a
->
len
:
bufsz
-
1
;
memcpy
(
buf
,
a
->
str
,
n
);
buf
[
n
]
=
'\0'
;
return
buf
;
}
static
inline
bool
sv_frame_is_strict
(
const
sv_frame_t
*
frame
)
{
return
frame
&&
frame
->
func
&&
frame
->
func
->
is_strict
;
}
static
inline
bool
sv_slot_in_range
(
const
ant_value_t
*
base
,
size_t
count
,
const
ant_value_t
*
slot
)
{
if
(
!
base
||
!
slot
||
count
==
0
)
return
false
;
uintptr_t
lo
=
(
uintptr_t
)
base
;
uintptr_t
hi
=
lo
+
count
*
sizeof
(
*
base
);
uintptr_t
addr
=
(
uintptr_t
)
slot
;
return
addr
>=
lo
&&
addr
<
hi
;
}
static
inline
bool
sv_slot_in_vm_stack
(
const
sv_vm_t
*
vm
,
const
ant_value_t
*
slot
)
{
return
vm
&&
sv_slot_in_range
(
vm
->
stack
,
(
size_t
)
vm
->
stack_size
,
slot
);
}
static
inline
bool
sv_is_nullish_this
(
ant_value_t
v
)
{
return
vtype
(
v
)
==
T_UNDEF
||
vtype
(
v
)
==
T_NULL
||
(
vtype
(
v
)
==
T_OBJ
&&
vdata
(
v
)
==
0
);
}
static
inline
ant_value_t
sv_normalize_this_for_frame
(
ant_t
*
js
,
sv_func_t
*
func
,
ant_value_t
this_val
)
{
if
(
!
func
||
func
->
is_arrow
)
return
this_val
;
if
(
func
->
is_strict
)
return
sv_is_nullish_this
(
this_val
)
?
js_mkundef
()
:
this_val
;
return
sv_is_nullish_this
(
this_val
)
?
js
->
global
:
this_val
;
}
static
inline
bool
sv_vm_is_strict
(
const
sv_vm_t
*
vm
)
{
if
(
vm
&&
vm
->
fp
>=
0
)
{
const
sv_frame_t
*
f
=
&
vm
->
frames
[
vm
->
fp
];
return
f
->
func
&&
f
->
func
->
is_strict
;
}
return
false
;
}
static
inline
sv_vm_t
*
sv_vm_get_active
(
ant_t
*
js
)
{
if
(
!
js
)
return
NULL
;
if
(
js
->
active_async_coro
)
{
if
(
js
->
active_async_coro
->
sv_vm
)
return
js
->
active_async_coro
->
sv_vm
;
if
(
js
->
active_async_coro
->
owner_vm
)
return
js
->
active_async_coro
->
owner_vm
;
}
return
js
->
vm
;
}
static
inline
bool
sv_is_strict_context
(
ant_t
*
js
)
{
return
sv_vm_is_strict
(
sv_vm_get_active
(
js
));
}
static
inline
ant_value_t
sv_vm_get_new_target
(
const
sv_vm_t
*
vm
,
ant_t
*
js
)
{
if
(
vm
&&
vm
->
fp
>=
0
)
return
vm
->
frames
[
vm
->
fp
].
new_target
;
return
js
->
new_target
;
}
static
inline
ant_value_t
sv_vm_get_super_val
(
const
sv_vm_t
*
vm
)
{
if
(
vm
&&
vm
->
fp
>=
0
)
return
vm
->
frames
[
vm
->
fp
].
super_val
;
return
js_mkundef
();
}
static
inline
int
sv_frame_arg_slots
(
const
sv_frame_t
*
frame
)
{
if
(
!
frame
||
!
frame
->
func
)
return
0
;
return
frame
->
argc
>
frame
->
func
->
param_count
?
frame
->
argc
:
frame
->
func
->
param_count
;
}
static
inline
ant_value_t
sv_frame_get_arg_value
(
const
sv_frame_t
*
frame
,
uint16_t
idx
)
{
int
arg_slots
=
sv_frame_arg_slots
(
frame
);
if
(
!
frame
||
!
frame
->
bp
||
(
int
)
idx
>=
arg_slots
)
return
js_mkundef
();
return
frame
->
bp
[
idx
];
}
static
inline
void
sv_frame_set_arg_value
(
ant_t
*
js
,
sv_frame_t
*
frame
,
uint16_t
idx
,
ant_value_t
val
)
{
int
arg_slots
=
sv_frame_arg_slots
(
frame
);
if
(
!
frame
||
!
frame
->
bp
||
(
int
)
idx
>=
arg_slots
)
return
;
frame
->
bp
[
idx
]
=
val
;
if
(
vtype
(
frame
->
arguments_obj
)
!=
T_UNDEF
)
js_arguments_sync_slot
(
js
,
frame
->
arguments_obj
,
idx
,
val
);
}
static
inline
ant_value_t
*
sv_frame_slot_ptr
(
sv_frame_t
*
frame
,
uint16_t
slot_idx
)
{
if
(
!
frame
||
!
frame
->
func
)
return
NULL
;
int
param_count
=
frame
->
func
->
param_count
;
if
((
int
)
slot_idx
<
param_count
)
{
int
arg_slots
=
sv_frame_arg_slots
(
frame
);
if
((
int
)
slot_idx
>=
arg_slots
||
!
frame
->
bp
)
return
NULL
;
return
&
frame
->
bp
[
slot_idx
];
}
if
(
!
frame
->
lp
)
return
NULL
;
return
&
frame
->
lp
[
slot_idx
-
param_count
];
}
static
inline
uint16_t
sv_frame_total_slots
(
const
sv_frame_t
*
frame
)
{
if
(
!
frame
||
!
frame
->
func
)
return
0
;
int
total
=
frame
->
func
->
param_count
+
frame
->
func
->
max_locals
;
return
total
>
0
?
(
uint16_t
)
total
:
0
;
}
static
inline
void
sv_vm_maybe_checkpoint_microtasks
(
ant_t
*
js
)
{
if
(
!
js
||
js
->
microtasks_draining
||
js
->
vm_exec_depth
!=
0
)
return
;
js_maybe_drain_microtasks
(
js
);
}
ant_value_t
sv_string_builder_read_value
(
ant_t
*
js
,
ant_value_t
value
);
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
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
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
);
typedef
struct
{
ant_value_t
this_val
;
ant_value_t
super_val
;
ant_value_t
*
args
;
int
argc
;
ant_value_t
*
alloc
;
}
sv_call_ctx_t
;
typedef
enum
{
SV_CALL_MODE_NORMAL
=
0
,
SV_CALL_MODE_EXPLICIT_THIS
,
SV_CALL_MODE_CONSTRUCT
,
}
sv_call_mode_t
;
typedef
enum
{
SV_CALL_EXEC_NATIVE
=
0
,
SV_CALL_EXEC_PROXY_APPLY
,
SV_CALL_EXEC_PROXY_CONSTRUCT
,
SV_CALL_EXEC_DEFAULT_CTOR
,
SV_CALL_EXEC_CLOSURE
,
}
sv_call_exec_kind_t
;
typedef
struct
{
sv_call_exec_kind_t
kind
;
ant_value_t
func
;
sv_closure_t
*
closure
;
sv_call_ctx_t
ctx
;
}
sv_call_plan_t
;
static
inline
ant_value_t
*
sv_prepend_bound_args
(
sv_closure_t
*
closure
,
ant_value_t
*
args
,
int
argc
,
int
*
out_total
)
{
int
total
=
closure
->
bound_argc
+
argc
;
ant_value_t
*
combined
=
malloc
(
sizeof
(
ant_value_t
)
*
(
size_t
)
total
);
if
(
!
combined
)
{
*
out_total
=
argc
;
return
NULL
;
}
memcpy
(
combined
,
closure
->
bound_argv
,
sizeof
(
ant_value_t
)
*
(
size_t
)
closure
->
bound_argc
);
memcpy
(
combined
+
closure
->
bound_argc
,
args
,
sizeof
(
ant_value_t
)
*
(
size_t
)
argc
);
*
out_total
=
total
;
return
combined
;
}
static
inline
bool
sv_call_mode_is_construct
(
sv_call_mode_t
mode
)
{
return
mode
==
SV_CALL_MODE_CONSTRUCT
;
}
static
inline
ant_value_t
sv_call_normalize_this
(
ant_t
*
js
,
ant_value_t
this_val
,
sv_call_mode_t
mode
)
{
if
(
mode
==
SV_CALL_MODE_NORMAL
&&
sv_is_nullish_this
(
this_val
))
return
js
->
global
;
return
this_val
;
}
static
inline
ant_value_t
sv_call_resolve_bound
(
ant_t
*
js
,
sv_closure_t
*
closure
,
sv_call_ctx_t
*
ctx
,
sv_call_mode_t
mode
)
{
uint32_t
flags
=
closure
->
call_flags
;
if
(
flags
&
SV_CALL_IS_ARROW
)
ctx
->
this_val
=
closure
->
bound_this
;
else
if
(
!
sv_call_mode_is_construct
(
mode
)
&&
vtype
(
closure
->
bound_this
)
!=
T_UNDEF
)
ctx
->
this_val
=
closure
->
bound_this
;
if
((
flags
&
SV_CALL_HAS_BOUND_ARGS
)
&&
closure
->
bound_argc
>
0
)
{
int
total
;
ant_value_t
*
combined
=
sv_prepend_bound_args
(
closure
,
ctx
->
args
,
ctx
->
argc
,
&
total
);
if
(
!
combined
)
return
js_mkerr
(
js
,
"out of memory"
);
ctx
->
args
=
combined
;
ctx
->
argc
=
total
;
ctx
->
alloc
=
combined
;
}
if
(
flags
&
SV_CALL_HAS_SUPER
)
ctx
->
super_val
=
closure
->
super_val
;
return
js_mkundef
();
}
static
inline
void
sv_call_cleanup
(
ant_t
*
js
,
sv_call_ctx_t
*
ctx
)
{
if
(
ctx
->
alloc
)
{
free
(
ctx
->
alloc
);
ctx
->
alloc
=
NULL
;
}
}
static
inline
ant_value_t
sv_call_default_ctor
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
sv_call_ctx_t
*
ctx
,
ant_value_t
*
out_this
);
static
inline
ant_value_t
sv_call_resolve_closure
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
sv_call_ctx_t
*
ctx
,
ant_value_t
*
out_this
);
static
inline
ant_value_t
sv_prepare_call
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
func
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
,
ant_value_t
*
out_this
,
sv_call_mode_t
mode
,
sv_call_plan_t
*
plan
)
{
bool
is_construct_call
=
sv_call_mode_is_construct
(
mode
);
plan
->
kind
=
SV_CALL_EXEC_NATIVE
;
plan
->
func
=
func
;
plan
->
closure
=
NULL
;
plan
->
ctx
=
(
sv_call_ctx_t
){
.
this_val
=
this_val
,
.
super_val
=
js_mkundef
(),
.
args
=
args
,
.
argc
=
argc
,
.
alloc
=
NULL
,
};
if
(
!
is_construct_call
)
js
->
new_target
=
js_mkundef
();
if
(
out_this
)
*
out_this
=
this_val
;
if
(
is_construct_call
&&
vtype
(
func
)
==
T_OBJ
&&
is_proxy
(
func
))
{
plan
->
kind
=
SV_CALL_EXEC_PROXY_CONSTRUCT
;
return
js_mkundef
();
}
if
(
is_construct_call
&&
!
js_is_constructor
(
func
))
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"not a constructor"
);
if
(
!
is_construct_call
&&
vtype
(
func
)
==
T_OBJ
&&
is_proxy
(
func
))
{
plan
->
kind
=
SV_CALL_EXEC_PROXY_APPLY
;
return
js_mkundef
();
}
if
(
vtype
(
func
)
==
T_CFUNC
)
{
plan
->
ctx
.
this_val
=
sv_call_normalize_this
(
js
,
this_val
,
mode
);
if
(
out_this
)
*
out_this
=
plan
->
ctx
.
this_val
;
return
js_mkundef
();
}
if
(
vtype
(
func
)
!=
T_FUNC
)
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"%s is not a function"
,
typestr
(
vtype
(
func
)));
sv_closure_t
*
closure
=
js_func_closure
(
func
);
plan
->
closure
=
closure
;
ant_value_t
err
=
sv_call_resolve_bound
(
js
,
closure
,
&
plan
->
ctx
,
mode
);
if
(
is_err
(
err
))
return
err
;
if
(
is_construct_call
)
plan
->
ctx
.
this_val
=
this_val
;
if
(
out_this
)
*
out_this
=
plan
->
ctx
.
this_val
;
if
(
closure
->
call_flags
&
SV_CALL_IS_DEFAULT_CTOR
)
{
plan
->
kind
=
SV_CALL_EXEC_DEFAULT_CTOR
;
return
js_mkundef
();
}
if
(
closure
->
func
!=
NULL
)
{
plan
->
kind
=
SV_CALL_EXEC_CLOSURE
;
return
js_mkundef
();
}
return
js_mkundef
();
}
static
inline
ant_value_t
sv_execute_call_plan
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_call_plan_t
*
plan
,
ant_value_t
*
out_this
)
{
switch
(
plan
->
kind
)
{
case
SV_CALL_EXEC_PROXY_APPLY
:
return
js_proxy_apply
(
js
,
plan
->
func
,
plan
->
ctx
.
this_val
,
plan
->
ctx
.
args
,
plan
->
ctx
.
argc
);
case
SV_CALL_EXEC_PROXY_CONSTRUCT
:
return
js_proxy_construct
(
js
,
plan
->
func
,
plan
->
ctx
.
args
,
plan
->
ctx
.
argc
,
sv_vm_get_new_target
(
vm
,
js
)
);
case
SV_CALL_EXEC_DEFAULT_CTOR
:
return
sv_call_default_ctor
(
vm
,
js
,
plan
->
closure
,
&
plan
->
ctx
,
out_this
);
case
SV_CALL_EXEC_CLOSURE
:
return
sv_call_resolve_closure
(
vm
,
js
,
plan
->
closure
,
plan
->
func
,
&
plan
->
ctx
,
out_this
);
case
SV_CALL_EXEC_NATIVE
:
{
ant_value_t
result
=
sv_call_native
(
js
,
plan
->
func
,
plan
->
ctx
.
this_val
,
plan
->
ctx
.
args
,
plan
->
ctx
.
argc
);
sv_call_cleanup
(
js
,
&
plan
->
ctx
);
return
result
;
}}
return
js_mkerr
(
js
,
"invalid call plan"
);
}
static
inline
bool
sv_check_c_stack_overflow
(
ant_t
*
js
)
{
volatile
char
marker
;
if
(
js
->
cstk
.
limit
==
0
||
js
->
cstk
.
base
==
NULL
)
return
false
;
uintptr_t
base
=
(
uintptr_t
)
js
->
cstk
.
base
;
uintptr_t
curr
=
(
uintptr_t
)
&
marker
;
size_t
used
=
(
base
>
curr
)
?
(
base
-
curr
)
:
(
curr
-
base
);
return
used
>
js
->
cstk
.
limit
;
}
static
inline
ant_value_t
sv_vm_call
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
func
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
,
ant_value_t
*
out_this
,
bool
is_construct_call
)
{
if
(
sv_check_c_stack_overflow
(
js
))
return
js_mkerr_typed
(
js
,
JS_ERR_RANGE
|
JS_ERR_NO_STACK
,
"Maximum call stack size exceeded"
);
sv_call_mode_t
mode
=
is_construct_call
?
SV_CALL_MODE_CONSTRUCT
:
SV_CALL_MODE_NORMAL
;
sv_call_plan_t
plan
;
ant_value_t
err
=
sv_prepare_call
(
vm
,
js
,
func
,
this_val
,
args
,
argc
,
out_this
,
mode
,
&
plan
);
if
(
is_err
(
err
))
return
err
;
ant_value_t
result
=
sv_execute_call_plan
(
vm
,
js
,
&
plan
,
out_this
);
sv_vm_maybe_checkpoint_microtasks
(
js
);
return
result
;
}
static
inline
ant_value_t
sv_vm_call_explicit_this
(
sv_vm_t
*
vm
,
ant_t
*
js
,
ant_value_t
func
,
ant_value_t
this_val
,
ant_value_t
*
args
,
int
argc
)
{
if
(
sv_check_c_stack_overflow
(
js
))
return
js_mkerr_typed
(
js
,
JS_ERR_RANGE
|
JS_ERR_NO_STACK
,
"Maximum call stack size exceeded"
);
sv_call_plan_t
plan
;
ant_value_t
err
=
sv_prepare_call
(
vm
,
js
,
func
,
this_val
,
args
,
argc
,
NULL
,
SV_CALL_MODE_EXPLICIT_THIS
,
&
plan
);
if
(
is_err
(
err
))
return
err
;
ant_value_t
result
=
sv_execute_call_plan
(
vm
,
js
,
&
plan
,
NULL
);
sv_vm_maybe_checkpoint_microtasks
(
js
);
return
result
;
}
static
inline
ant_value_t
sv_call_default_ctor
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
sv_call_ctx_t
*
ctx
,
ant_value_t
*
out_this
)
{
if
(
vtype
(
js
->
new_target
)
==
T_UNDEF
)
{
sv_call_cleanup
(
js
,
ctx
);
return
js_mkerr_typed
(
js
,
JS_ERR_TYPE
,
"Class constructor cannot be invoked without 'new'"
);}
ant_value_t
super_ctor
=
closure
->
super_val
;
uint8_t
st
=
vtype
(
super_ctor
);
if
(
st
==
T_FUNC
||
st
==
T_CFUNC
)
{
ant_value_t
super_this
=
ctx
->
this_val
;
ant_value_t
result
=
sv_vm_call
(
vm
,
js
,
super_ctor
,
ctx
->
this_val
,
ctx
->
args
,
ctx
->
argc
,
&
super_this
,
true
);
if
(
out_this
)
*
out_this
=
super_this
;
sv_call_cleanup
(
js
,
ctx
);
return
result
;
}
sv_call_cleanup
(
js
,
ctx
);
return
js_mkundef
();
}
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
);
ant_value_t
sv_call_generator_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
);
static
inline
ant_value_t
sv_call_async_closure
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
sv_call_ctx_t
*
ctx
)
{
ant_value_t
result
=
sv_call_async_closure_dispatch
(
vm
,
js
,
closure
,
callee_func
,
ctx
->
super_val
,
ctx
->
this_val
,
ctx
->
args
,
ctx
->
argc
);
sv_call_cleanup
(
js
,
ctx
);
return
result
;
}
static
inline
ant_value_t
sv_call_generator_closure
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
sv_call_ctx_t
*
ctx
)
{
ant_value_t
result
=
sv_call_generator_closure_dispatch
(
vm
,
js
,
closure
,
callee_func
,
ctx
->
super_val
,
ctx
->
this_val
,
ctx
->
args
,
ctx
->
argc
);
sv_call_cleanup
(
js
,
ctx
);
return
result
;
}
static
inline
ant_value_t
sv_call_closure
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
sv_call_ctx_t
*
ctx
,
ant_value_t
*
out_this
)
{
ant_value_t
result
=
sv_execute_closure_entry
(
vm
,
closure
,
callee_func
,
ctx
->
super_val
,
ctx
->
this_val
,
ctx
->
args
,
ctx
->
argc
,
out_this
);
sv_call_cleanup
(
js
,
ctx
);
return
result
;
}
#ifdef ANT_JIT
#define SV_TFB_NUM (1 << 0)
#define SV_TFB_STR (1 << 1)
#define SV_TFB_BOOL (1 << 2)
#define SV_TFB_OTHER (1 << 3)
#define SV_TFB_CTOR_PROP_BINS 17
#define SV_TFB_CTOR_PROP_OVERFLOW_FROM (SV_TFB_CTOR_PROP_BINS - 1)
#define SV_TFB_INOBJ_SLACK_ALLOCATIONS 32
#define SV_TFB_INOBJ_P90_NUMERATOR 9
#define SV_TFB_INOBJ_P90_DENOMINATOR 10
#define SV_JIT_THRESHOLD 100
#define SV_JIT_RECOMPILE_DELAY 50
#define SV_TFB_ALLOC_THRESHOLD 2
#define SV_CALL_FB_MAX_SLOTS 32
#define SV_CALL_FB_MISS_DISABLE 4
#define SV_JIT_RETRY_INTERP mkval(T_ERR, 1)
#define SV_JIT_MAGIC 0xBA110ULL
#define SV_JIT_BAILOUT \
(NANBOX_PREFIX \
| ((ant_value_t)T_SENTINEL << NANBOX_TYPE_SHIFT) \
| SV_JIT_MAGIC)
static
inline
bool
sv_is_jit_bailout
(
ant_value_t
v
)
{
return
v
==
SV_JIT_BAILOUT
;
}
static
inline
void
sv_jit_enter
(
ant_t
*
js
)
{
if
(
js
)
js
->
jit_active_depth
++
;
}
static
inline
void
sv_jit_leave
(
ant_t
*
js
)
{
if
(
js
&&
js
->
jit_active_depth
>
0
)
js
->
jit_active_depth
--
;
}
static
inline
void
sv_jit_on_bailout
(
sv_func_t
*
fn
)
{
fn
->
jit_code
=
NULL
;
fn
->
back_edge_count
=
0
;
fn
->
call_count
=
SV_JIT_THRESHOLD
-
SV_JIT_RECOMPILE_DELAY
;
}
typedef
ant_value_t
(
*
sv_jit_func_t
)(
sv_vm_t
*
,
ant_value_t
,
ant_value_t
,
ant_value_t
,
ant_value_t
*
,
int
,
sv_closure_t
*
);
ant_value_t
sv_jit_try_compile_and_call
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
sv_call_ctx_t
*
ctx
,
ant_value_t
*
out_this
);
static
inline
uint8_t
sv_tfb_classify
(
ant_value_t
v
)
{
if
(
vtype
(
v
)
==
T_NUM
)
return
SV_TFB_NUM
;
if
(
vtype
(
v
)
==
T_STR
)
return
SV_TFB_STR
;
if
(
vtype
(
v
)
==
T_BOOL
)
return
SV_TFB_BOOL
;
return
SV_TFB_OTHER
;
}
static
inline
void
sv_tfb_record2
(
sv_func_t
*
func
,
uint8_t
*
ip
,
ant_value_t
l
,
ant_value_t
r
)
{
if
(
func
->
type_feedback
)
{
int
off
=
(
int
)(
ip
-
func
->
code
);
uint8_t
old
=
func
->
type_feedback
[
off
];
uint8_t
neu
=
old
|
sv_tfb_classify
(
l
)
|
sv_tfb_classify
(
r
);
if
(
neu
!=
old
)
{
func
->
type_feedback
[
off
]
=
neu
;
func
->
tfb_version
++
;
}
}}
static
inline
void
sv_tfb_record1
(
sv_func_t
*
func
,
uint8_t
*
ip
,
ant_value_t
v
)
{
if
(
func
->
type_feedback
)
{
int
off
=
(
int
)(
ip
-
func
->
code
);
uint8_t
old
=
func
->
type_feedback
[
off
];
uint8_t
neu
=
old
|
sv_tfb_classify
(
v
);
if
(
neu
!=
old
)
{
func
->
type_feedback
[
off
]
=
neu
;
func
->
tfb_version
++
;
}
}}
static
inline
void
sv_tfb_ensure
(
sv_func_t
*
fn
)
{
if
(
!
fn
->
type_feedback
&&
fn
->
code_len
>
0
)
fn
->
type_feedback
=
calloc
((
size_t
)
fn
->
code_len
,
1
);
if
(
!
fn
->
local_type_feedback
&&
fn
->
max_locals
>
0
)
fn
->
local_type_feedback
=
calloc
((
size_t
)
fn
->
max_locals
,
1
);
}
static
inline
void
sv_tfb_record_call_target
(
sv_func_t
*
func
,
int
bc_off
,
sv_func_t
*
callee
)
{
if
(
!
callee
)
return
;
sv_call_target_fb_t
*
fb
=
func
->
call_target_fb
;
int
count
=
func
->
call_target_fb_count
;
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
if
(
fb
[
i
].
bc_off
!=
(
uint16_t
)
bc_off
)
continue
;
if
(
fb
[
i
].
disabled
)
return
;
if
(
fb
[
i
].
target
==
callee
)
return
;
if
(
fb
[
i
].
target
==
NULL
)
{
fb
[
i
].
target
=
callee
;
return
;
}
fb
[
i
].
miss_count
++
;
if
(
fb
[
i
].
miss_count
>=
SV_CALL_FB_MISS_DISABLE
)
{
fb
[
i
].
disabled
=
1
;
fb
[
i
].
target
=
NULL
;
}
else
{
fb
[
i
].
target
=
callee
;
}
func
->
tfb_version
++
;
return
;
}
if
(
count
>=
SV_CALL_FB_MAX_SLOTS
)
return
;
if
(
!
fb
)
{
fb
=
calloc
(
SV_CALL_FB_MAX_SLOTS
,
sizeof
(
sv_call_target_fb_t
));
if
(
!
fb
)
return
;
func
->
call_target_fb
=
fb
;
}
fb
[
count
].
bc_off
=
(
uint16_t
)
bc_off
;
fb
[
count
].
target
=
callee
;
fb
[
count
].
miss_count
=
0
;
fb
[
count
].
disabled
=
0
;
func
->
call_target_fb_count
=
(
uint8_t
)(
count
+
1
);
}
static
inline
sv_func_t
*
sv_tfb_get_call_target
(
sv_func_t
*
func
,
int
bc_off
)
{
sv_call_target_fb_t
*
fb
=
func
->
call_target_fb
;
int
count
=
func
->
call_target_fb_count
;
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
if
(
fb
[
i
].
bc_off
==
(
uint16_t
)
bc_off
&&
!
fb
[
i
].
disabled
)
return
fb
[
i
].
target
;
}
return
NULL
;
}
static
inline
void
sv_tfb_record_local
(
sv_func_t
*
func
,
int
idx
,
ant_value_t
v
)
{
if
(
func
->
local_type_feedback
&&
idx
>=
0
&&
idx
<
func
->
max_locals
)
{
uint8_t
old
=
func
->
local_type_feedback
[
idx
];
uint8_t
neu
=
old
|
sv_tfb_classify
(
v
);
if
(
neu
!=
old
)
{
func
->
local_type_feedback
[
idx
]
=
neu
;
func
->
tfb_version
++
;
}
}
}
static
inline
uint8_t
sv_tfb_clamp_inobj_limit
(
uint32_t
limit
)
{
return
(
limit
>
ANT_INOBJ_MAX_SLOTS
)
?
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
:
(
uint8_t
)
limit
;
}
static
inline
uint8_t
sv_tfb_infer_inobj_limit
(
const
sv_func_t
*
func
,
uint64_t
samples
)
{
if
(
!
func
||
samples
==
0
)
return
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
;
uint64_t
target
=
(
(
samples
*
SV_TFB_INOBJ_P90_NUMERATOR
)
+
(
SV_TFB_INOBJ_P90_DENOMINATOR
-
1
)
)
/
SV_TFB_INOBJ_P90_DENOMINATOR
;
if
(
target
==
0
)
target
=
1
;
uint64_t
seen
=
0
;
for
(
uint32_t
i
=
0
;
i
<
SV_TFB_CTOR_PROP_BINS
;
i
++
)
{
seen
+=
func
->
ctor_prop_hist
[
i
];
if
(
seen
<
target
)
continue
;
if
(
i
>=
SV_TFB_CTOR_PROP_OVERFLOW_FROM
)
return
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
;
return
sv_tfb_clamp_inobj_limit
(
i
);
}
return
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
;
}
static
inline
void
sv_tfb_record_ctor_prop_count
(
ant_value_t
ctor_func
,
ant_value_t
instance
)
{
if
(
vtype
(
ctor_func
)
!=
T_FUNC
)
return
;
if
(
!
is_object_type
(
instance
))
return
;
sv_closure_t
*
closure
=
js_func_closure
(
ctor_func
);
if
(
!
closure
||
!
closure
->
func
)
return
;
ant_object_t
*
obj
=
js_obj_ptr
(
js_as_obj
(
instance
));
if
(
!
obj
)
return
;
sv_func_t
*
func
=
closure
->
func
;
uint32_t
count
=
obj
->
prop_count
;
uint32_t
bin
=
(
count
<
SV_TFB_CTOR_PROP_OVERFLOW_FROM
)
?
count
:
SV_TFB_CTOR_PROP_OVERFLOW_FROM
;
func
->
ctor_prop_hist
[
bin
]
++
;
uint64_t
samples
=
++
func
->
ctor_prop_samples
;
if
(
!
func
->
ctor_inobj_frozen
&&
samples
>=
SV_TFB_INOBJ_SLACK_ALLOCATIONS
)
{
func
->
ctor_inobj_limit
=
sv_tfb_infer_inobj_limit
(
func
,
samples
);
func
->
ctor_inobj_frozen
=
1
;
}
}
static
inline
uint8_t
sv_tfb_ctor_inobj_limit
(
ant_value_t
ctor_func
)
{
if
(
vtype
(
ctor_func
)
!=
T_FUNC
)
return
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
;
sv_closure_t
*
closure
=
js_func_closure
(
ctor_func
);
if
(
!
closure
||
!
closure
->
func
)
return
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
;
sv_func_t
*
func
=
closure
->
func
;
if
(
!
func
->
ctor_inobj_frozen
)
return
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
;
return
sv_tfb_clamp_inobj_limit
(
func
->
ctor_inobj_limit
);
}
static
inline
bool
sv_tfb_ctor_inobj_limit_frozen
(
ant_value_t
ctor_func
)
{
if
(
vtype
(
ctor_func
)
!=
T_FUNC
)
return
false
;
sv_closure_t
*
closure
=
js_func_closure
(
ctor_func
);
if
(
!
closure
||
!
closure
->
func
)
return
false
;
return
closure
->
func
->
ctor_inobj_frozen
!=
0
;
}
static
inline
uint32_t
sv_tfb_ctor_inobj_slack_remaining
(
ant_value_t
ctor_func
)
{
if
(
vtype
(
ctor_func
)
!=
T_FUNC
)
return
SV_TFB_INOBJ_SLACK_ALLOCATIONS
;
sv_closure_t
*
closure
=
js_func_closure
(
ctor_func
);
if
(
!
closure
||
!
closure
->
func
)
return
SV_TFB_INOBJ_SLACK_ALLOCATIONS
;
sv_func_t
*
func
=
closure
->
func
;
if
(
func
->
ctor_inobj_frozen
||
func
->
ctor_prop_samples
>=
SV_TFB_INOBJ_SLACK_ALLOCATIONS
)
return
0
;
return
(
uint32_t
)(
SV_TFB_INOBJ_SLACK_ALLOCATIONS
-
func
->
ctor_prop_samples
);
}
#endif
#ifndef ANT_JIT
static
inline
void
sv_tfb_record_ctor_prop_count
(
ant_value_t
ctor_func
,
ant_value_t
instance
)
{
(
void
)
ctor_func
;
(
void
)
instance
;
}
static
inline
uint8_t
sv_tfb_ctor_inobj_limit
(
ant_value_t
ctor_func
)
{
(
void
)
ctor_func
;
return
(
uint8_t
)
ANT_INOBJ_MAX_SLOTS
;
}
static
inline
bool
sv_tfb_ctor_inobj_limit_frozen
(
ant_value_t
ctor_func
)
{
(
void
)
ctor_func
;
return
false
;
}
static
inline
uint32_t
sv_tfb_ctor_inobj_slack_remaining
(
ant_value_t
ctor_func
)
{
(
void
)
ctor_func
;
return
0
;
}
#endif
static
inline
ant_value_t
sv_call_resolve_closure
(
sv_vm_t
*
vm
,
ant_t
*
js
,
sv_closure_t
*
closure
,
ant_value_t
callee_func
,
sv_call_ctx_t
*
ctx
,
ant_value_t
*
out_this
)
{
if
(
closure
->
func
->
is_generator
)
return
sv_call_generator_closure
(
vm
,
js
,
closure
,
callee_func
,
ctx
);
if
(
closure
->
func
->
is_async
)
return
sv_call_async_closure
(
vm
,
js
,
closure
,
callee_func
,
ctx
);
#ifdef ANT_JIT
if
(
!
closure
->
func
->
is_generator
)
{
sv_func_t
*
fn
=
closure
->
func
;
if
(
fn
->
jit_code
)
{
sv_jit_enter
(
js
);
ant_value_t
result
=
((
sv_jit_func_t
)
fn
->
jit_code
)(
vm
,
ctx
->
this_val
,
js
->
new_target
,
ctx
->
super_val
,
ctx
->
args
,
ctx
->
argc
,
closure
);
sv_jit_leave
(
js
);
if
(
sv_is_jit_bailout
(
result
))
{
sv_jit_on_bailout
(
fn
);
}
else
{
sv_call_cleanup
(
js
,
ctx
);
return
result
;
}
}
{
uint32_t
cc
=
++
fn
->
call_count
;
if
(
__builtin_expect
(
cc
==
SV_TFB_ALLOC_THRESHOLD
,
0
))
sv_tfb_ensure
(
fn
);
if
(
cc
>
SV_JIT_THRESHOLD
)
{
ant_value_t
result
=
sv_jit_try_compile_and_call
(
vm
,
js
,
closure
,
callee_func
,
ctx
,
out_this
);
if
(
result
!=
SV_JIT_RETRY_INTERP
)
return
result
;
}
}
}
#endif
return
sv_call_closure
(
vm
,
js
,
closure
,
callee_func
,
ctx
,
out_this
);
}
#endif
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, May 2, 9:43 AM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
542368
Default Alt Text
engine.h (30 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment