Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2916110
compiler.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
125 KB
Referenced Files
None
Subscribers
None
compiler.c
View Options
#include
"silver/ast.h"
#include
"silver/engine.h"
#include
"silver/compiler.h"
#include
"silver/directives.h"
#include
"internal.h"
#include
"tokens.h"
#include
"runtime.h"
#include
"ops/coercion.h"
#include
<stdlib.h>
#include
<string.h>
#include
<stdio.h>
typedef
struct
{
const
char
*
name
;
uint32_t
name_len
;
int
depth
;
bool
is_const
;
bool
captured
;
bool
is_tdz
;
}
sv_local_t
;
typedef
struct
{
int
*
offsets
;
int
count
;
int
cap
;
}
sv_patch_list_t
;
typedef
struct
{
int
loop_start
;
sv_patch_list_t
breaks
;
sv_patch_list_t
continues
;
int
scope_depth
;
const
char
*
label
;
uint32_t
label_len
;
}
sv_loop_t
;
typedef
struct
{
const
char
*
name
;
uint32_t
len
;
}
sv_deferred_export_t
;
typedef
struct
sv_compiler
{
ant_t
*
js
;
const
char
*
source
;
jsoff_t
source_len
;
uint8_t
*
code
;
int
code_len
;
int
code_cap
;
jsval_t
*
constants
;
int
const_count
;
int
const_cap
;
sv_atom_t
*
atoms
;
int
atom_count
;
int
atom_cap
;
sv_local_t
*
locals
;
int
local_count
;
int
local_cap
;
int
max_local_count
;
int
scope_depth
;
int
param_locals
;
sv_upval_desc_t
*
upval_descs
;
int
upvalue_count
;
int
upvalue_cap
;
sv_loop_t
*
loops
;
int
loop_count
;
int
loop_cap
;
struct
sv_compiler
*
enclosing
;
sv_ast_t
**
field_inits
;
int
field_init_count
;
int
*
computed_key_locals
;
int
param_count
;
bool
is_arrow
;
bool
is_strict
;
sv_compile_mode_t
mode
;
bool
is_tla
;
int
try_depth
;
int
with_depth
;
int
strict_args_local
;
int
new_target_local
;
int
super_local
;
const
char
*
pending_label
;
uint32_t
pending_label_len
;
const
char
*
inferred_name
;
uint32_t
inferred_name_len
;
sv_srcpos_t
*
srcpos
;
int
srcpos_count
;
int
srcpos_cap
;
uint32_t
last_srcpos_off
;
uint32_t
last_srcpos_end
;
sv_deferred_export_t
*
deferred_exports
;
int
deferred_export_count
;
int
deferred_export_cap
;
}
sv_compiler_t
;
static
sv_func_t
*
compile_function_body
(
sv_compiler_t
*
enclosing
,
sv_ast_t
*
node
,
sv_compile_mode_t
mode
);
static
void
compile_expr
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_stmt
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_stmts
(
sv_compiler_t
*
c
,
sv_ast_list_t
*
list
);
static
void
compile_binary
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_unary
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_update
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_assign
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_lhs_set
(
sv_compiler_t
*
c
,
sv_ast_t
*
target
,
bool
keep
);
static
void
compile_ternary
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_tail_return_expr
(
sv_compiler_t
*
c
,
sv_ast_t
*
expr
);
static
void
compile_typeof
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_delete
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_template
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_call
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_new
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_member
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_optional
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_optional_get
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_array
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_object
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_func_expr
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_array_destructure
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
bool
keep
);
static
void
compile_object_destructure
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
bool
keep
);
static
void
compile_var_decl
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_import_decl
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_export_decl
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_destructure_binding
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
sv_var_kind_t
kind
);
static
void
compile_if
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_while
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_do_while
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_for
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_for_in
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_for_of
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_break
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_continue
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_try
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_switch
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_label
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
void
compile_class
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
);
static
const
char
*
pin_source_text
(
const
char
*
source
,
jsoff_t
source_len
)
{
if
(
!
source
||
source_len
<=
0
)
return
source
;
const
char
*
pinned
=
code_arena_alloc
(
source
,
(
size_t
)
source_len
);
return
pinned
?
pinned
:
source
;
}
static
void
emit
(
sv_compiler_t
*
c
,
uint8_t
byte
)
{
if
(
c
->
code_len
>=
c
->
code_cap
)
{
c
->
code_cap
=
c
->
code_cap
?
c
->
code_cap
*
2
:
256
;
c
->
code
=
realloc
(
c
->
code
,
(
size_t
)
c
->
code_cap
);
}
c
->
code
[
c
->
code_len
++
]
=
byte
;
}
static
void
emit_u16
(
sv_compiler_t
*
c
,
uint16_t
val
)
{
emit
(
c
,
(
uint8_t
)(
val
&
0xFF
));
emit
(
c
,
(
uint8_t
)(
val
>>
8
));
}
static
void
emit_u32
(
sv_compiler_t
*
c
,
uint32_t
val
)
{
emit
(
c
,
(
uint8_t
)(
val
&
0xFF
));
emit
(
c
,
(
uint8_t
)((
val
>>
8
)
&
0xFF
));
emit
(
c
,
(
uint8_t
)((
val
>>
16
)
&
0xFF
));
emit
(
c
,
(
uint8_t
)((
val
>>
24
)
&
0xFF
));
}
static
void
emit_i32
(
sv_compiler_t
*
c
,
int32_t
val
)
{
uint32_t
u
;
memcpy
(
&
u
,
&
val
,
4
);
emit_u32
(
c
,
u
);
}
static
void
emit_op
(
sv_compiler_t
*
c
,
sv_op_t
op
)
{
emit
(
c
,
(
uint8_t
)
op
);
}
static
void
emit_srcpos
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
!
node
)
return
;
const
char
*
code
=
c
->
source
;
jsoff_t
clen
=
c
->
source_len
;
if
(
!
code
||
clen
<=
0
)
return
;
uint32_t
off
=
node
->
src_off
;
if
(
off
>
clen
)
off
=
(
uint32_t
)
clen
;
uint32_t
end
=
node
->
src_end
;
if
(
end
>
clen
)
end
=
(
uint32_t
)
clen
;
if
(
end
<
off
)
end
=
off
;
if
(
end
==
off
&&
off
<
(
uint32_t
)
clen
)
end
=
off
+
1
;
if
(
end
==
off
)
return
;
uint32_t
line
=
1
,
col
=
1
;
for
(
uint32_t
i
=
0
;
i
<
off
;
i
++
)
{
if
(
code
[
i
]
==
'\n'
)
{
line
++
;
col
=
1
;
}
else
col
++
;
}
if
(
c
->
srcpos_count
>
0
&&
c
->
last_srcpos_off
==
off
&&
c
->
last_srcpos_end
==
end
)
return
;
if
(
c
->
srcpos_count
>=
c
->
srcpos_cap
)
{
c
->
srcpos_cap
=
c
->
srcpos_cap
?
c
->
srcpos_cap
*
2
:
32
;
c
->
srcpos
=
realloc
(
c
->
srcpos
,
(
size_t
)
c
->
srcpos_cap
*
sizeof
(
sv_srcpos_t
));
}
c
->
srcpos
[
c
->
srcpos_count
++
]
=
(
sv_srcpos_t
){
.
bc_offset
=
(
uint32_t
)
c
->
code_len
,
.
line
=
line
,
.
col
=
col
,
.
src_off
=
off
,
.
src_end
=
end
,
};
c
->
last_srcpos_off
=
off
;
c
->
last_srcpos_end
=
end
;
}
static
void
patch_u32
(
sv_compiler_t
*
c
,
int
offset
,
uint32_t
val
)
{
c
->
code
[
offset
]
=
(
uint8_t
)(
val
&
0xFF
);
c
->
code
[
offset
+
1
]
=
(
uint8_t
)((
val
>>
8
)
&
0xFF
);
c
->
code
[
offset
+
2
]
=
(
uint8_t
)((
val
>>
16
)
&
0xFF
);
c
->
code
[
offset
+
3
]
=
(
uint8_t
)((
val
>>
24
)
&
0xFF
);
}
static
int
add_constant
(
sv_compiler_t
*
c
,
jsval_t
val
)
{
if
(
c
->
const_count
>=
c
->
const_cap
)
{
c
->
const_cap
=
c
->
const_cap
?
c
->
const_cap
*
2
:
16
;
c
->
constants
=
realloc
(
c
->
constants
,
(
size_t
)
c
->
const_cap
*
sizeof
(
jsval_t
));
}
c
->
constants
[
c
->
const_count
]
=
val
;
return
c
->
const_count
++
;
}
static
void
emit_constant
(
sv_compiler_t
*
c
,
jsval_t
val
)
{
int
idx
=
add_constant
(
c
,
val
);
if
(
idx
<=
255
)
{
emit_op
(
c
,
OP_CONST8
);
emit
(
c
,
(
uint8_t
)
idx
);
}
else
{
emit_op
(
c
,
OP_CONST
);
emit_u32
(
c
,
(
uint32_t
)
idx
);
}
}
static
void
emit_number
(
sv_compiler_t
*
c
,
double
num
)
{
if
(
num
>=
-128
&&
num
<=
127
&&
num
==
(
int
)
num
&&
num
!=
-0.0
)
{
emit_op
(
c
,
OP_CONST_I8
);
emit
(
c
,
(
uint8_t
)(
int8_t
)
num
);
}
else
emit_constant
(
c
,
tov
(
num
));
}
static
inline
bool
is_quoted_ident_key
(
const
sv_ast_t
*
node
)
{
if
(
!
node
||
node
->
type
!=
N_IDENT
||
!
node
->
str
||
node
->
len
<
2
)
return
false
;
char
open
=
node
->
str
[
0
];
char
close
=
node
->
str
[
node
->
len
-
1
];
return
((
open
==
'\''
&&
close
==
'\''
)
||
(
open
==
'"'
&&
close
==
'"'
));
}
static
inline
bool
is_invalid_cooked_string
(
const
sv_ast_t
*
node
)
{
return
node
&&
(
node
->
flags
&
FN_INVALID_COOKED
);
}
static
inline
jsval_t
ast_string_const
(
sv_compiler_t
*
c
,
const
sv_ast_t
*
node
)
{
if
(
!
node
||
!
node
->
str
)
return
js_mkstr
(
c
->
js
,
""
,
0
);
return
js_mkstr
(
c
->
js
,
node
->
str
,
node
->
len
);
}
static
inline
void
compile_static_property_key
(
sv_compiler_t
*
c
,
sv_ast_t
*
key
)
{
if
(
!
key
)
{
emit_op
(
c
,
OP_UNDEF
);
return
;
}
if
(
key
->
type
==
N_STRING
)
{
emit_constant
(
c
,
ast_string_const
(
c
,
key
));
return
;
}
if
(
key
->
type
==
N_NUMBER
)
{
char
buf
[
32
];
int
n
=
snprintf
(
buf
,
sizeof
(
buf
),
"%g"
,
key
->
num
);
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
buf
,
(
size_t
)
n
));
return
;
}
if
(
key
->
type
==
N_IDENT
)
{
if
(
is_quoted_ident_key
(
key
))
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
key
->
str
+
1
,
key
->
len
-
2
));
else
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
key
->
str
,
key
->
len
));
return
;
}
compile_expr
(
c
,
key
);
}
static
int
add_atom
(
sv_compiler_t
*
c
,
const
char
*
str
,
uint32_t
len
)
{
for
(
int
i
=
0
;
i
<
c
->
atom_count
;
i
++
)
{
if
(
c
->
atoms
[
i
].
len
==
len
&&
memcmp
(
c
->
atoms
[
i
].
str
,
str
,
len
)
==
0
)
return
i
;
}
if
(
c
->
atom_count
>=
c
->
atom_cap
)
{
c
->
atom_cap
=
c
->
atom_cap
?
c
->
atom_cap
*
2
:
16
;
c
->
atoms
=
realloc
(
c
->
atoms
,
(
size_t
)
c
->
atom_cap
*
sizeof
(
sv_atom_t
));
}
char
*
copy
=
code_arena_bump
(
len
);
memcpy
(
copy
,
str
,
len
);
c
->
atoms
[
c
->
atom_count
]
=
(
sv_atom_t
){
.
str
=
copy
,
.
len
=
len
};
return
c
->
atom_count
++
;
}
static
void
emit_atom_op
(
sv_compiler_t
*
c
,
sv_op_t
op
,
const
char
*
str
,
uint32_t
len
)
{
int
idx
=
add_atom
(
c
,
str
,
len
);
emit_op
(
c
,
op
);
emit_u32
(
c
,
(
uint32_t
)
idx
);
}
static
inline
void
emit_set_function_name
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
if
(
!
name
)
{
name
=
""
;
len
=
0
;
}
emit_atom_op
(
c
,
OP_SET_NAME
,
name
,
len
);
}
static
void
emit_const_assign_error
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
static
const
char
prefix
[]
=
"Assignment to constant variable '"
;
static
const
char
suffix
[]
=
"'"
;
uint32_t
mlen
=
(
uint32_t
)(
sizeof
(
prefix
)
-
1
+
len
+
sizeof
(
suffix
)
-
1
);
char
*
buf
=
code_arena_bump
(
mlen
);
memcpy
(
buf
,
prefix
,
sizeof
(
prefix
)
-
1
);
memcpy
(
buf
+
sizeof
(
prefix
)
-
1
,
name
,
len
);
memcpy
(
buf
+
sizeof
(
prefix
)
-
1
+
len
,
suffix
,
sizeof
(
suffix
)
-
1
);
int
atom
=
add_atom
(
c
,
buf
,
mlen
);
emit_op
(
c
,
OP_THROW_ERROR
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
emit
(
c
,
(
uint8_t
)
JS_ERR_TYPE
);
}
static
inline
bool
is_ident_str
(
const
char
*
name
,
uint32_t
len
,
const
char
*
lit
,
uint32_t
lit_len
)
{
return
len
==
lit_len
&&
memcmp
(
name
,
lit
,
lit_len
)
==
0
;
}
static
inline
bool
is_strict_restricted_ident
(
const
char
*
name
,
uint32_t
len
)
{
return
is_ident_str
(
name
,
len
,
"eval"
,
4
)
||
is_ident_str
(
name
,
len
,
"arguments"
,
9
);
}
static
inline
bool
is_repl_top_level
(
const
sv_compiler_t
*
c
)
{
return
c
->
mode
==
SV_COMPILE_REPL
&&
c
->
scope_depth
==
0
&&
c
->
enclosing
&&
!
c
->
enclosing
->
enclosing
&&
!
c
->
is_strict
;
}
static
inline
bool
has_completion_value
(
const
sv_compiler_t
*
c
)
{
return
c
&&
(
c
->
mode
==
SV_COMPILE_EVAL
||
c
->
mode
==
SV_COMPILE_REPL
);
}
static
inline
bool
has_implicit_arguments_obj
(
const
sv_compiler_t
*
c
)
{
return
c
&&
!
c
->
is_arrow
&&
c
->
enclosing
;
}
static
int
resolve_local
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
for
(
int
i
=
c
->
local_count
-
1
;
i
>=
0
;
i
--
)
{
sv_local_t
*
loc
=
&
c
->
locals
[
i
];
if
(
loc
->
name_len
==
len
&&
memcmp
(
loc
->
name
,
name
,
len
)
==
0
)
return
i
;
}
return
-1
;
}
static
int
add_local
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
,
bool
is_const
,
int
depth
)
{
if
(
c
->
local_count
>=
c
->
local_cap
)
{
c
->
local_cap
=
c
->
local_cap
?
c
->
local_cap
*
2
:
16
;
c
->
locals
=
realloc
(
c
->
locals
,
(
size_t
)
c
->
local_cap
*
sizeof
(
sv_local_t
));
}
int
idx
=
c
->
local_count
++
;
if
(
c
->
local_count
>
c
->
max_local_count
)
c
->
max_local_count
=
c
->
local_count
;
c
->
locals
[
idx
]
=
(
sv_local_t
){
.
name
=
name
,
.
name_len
=
len
,
.
depth
=
depth
,
.
is_const
=
is_const
,
.
captured
=
false
,
};
return
idx
;
}
static
int
ensure_local_at_depth
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
,
bool
is_const
,
int
depth
)
{
int
local
=
resolve_local
(
c
,
name
,
len
);
if
(
local
!=
-1
&&
c
->
locals
[
local
].
depth
==
depth
)
return
local
;
return
add_local
(
c
,
name
,
len
,
is_const
,
depth
);
}
static
int
local_to_frame_slot
(
sv_compiler_t
*
c
,
int
local_idx
)
{
if
(
c
->
locals
[
local_idx
].
depth
==
-1
)
return
local_idx
;
return
c
->
param_count
+
(
local_idx
-
c
->
param_locals
);
}
static
int
resolve_local_slot
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
int
local
=
resolve_local
(
c
,
name
,
len
);
if
(
local
==
-1
||
c
->
locals
[
local
].
depth
==
-1
)
return
-1
;
int
slot
=
local
-
c
->
param_locals
;
return
(
slot
>=
0
&&
slot
<=
255
)
?
slot
:
-1
;
}
static
int
add_upvalue
(
sv_compiler_t
*
c
,
uint16_t
index
,
bool
is_local
,
bool
is_const
)
{
for
(
int
i
=
0
;
i
<
c
->
upvalue_count
;
i
++
)
{
if
(
c
->
upval_descs
[
i
].
index
==
index
&&
c
->
upval_descs
[
i
].
is_local
==
is_local
)
return
i
;
}
if
(
c
->
upvalue_count
>=
c
->
upvalue_cap
)
{
c
->
upvalue_cap
=
c
->
upvalue_cap
?
c
->
upvalue_cap
*
2
:
8
;
c
->
upval_descs
=
realloc
(
c
->
upval_descs
,
(
size_t
)
c
->
upvalue_cap
*
sizeof
(
sv_upval_desc_t
));
}
int
idx
=
c
->
upvalue_count
++
;
c
->
upval_descs
[
idx
]
=
(
sv_upval_desc_t
){
.
index
=
index
,
.
is_local
=
is_local
,
.
is_const
=
is_const
,
};
return
idx
;
}
static
int
resolve_super_upvalue
(
sv_compiler_t
*
c
)
{
if
(
!
c
->
enclosing
)
return
-1
;
sv_compiler_t
*
enc
=
c
->
enclosing
;
if
(
!
enc
->
is_arrow
)
{
if
(
enc
->
super_local
<
0
)
return
-1
;
enc
->
locals
[
enc
->
super_local
].
captured
=
true
;
uint16_t
slot
=
(
uint16_t
)
local_to_frame_slot
(
enc
,
enc
->
super_local
);
return
add_upvalue
(
c
,
slot
,
true
,
false
);
}
int
upvalue
=
resolve_super_upvalue
(
enc
);
if
(
upvalue
==
-1
)
return
-1
;
return
add_upvalue
(
c
,
(
uint16_t
)
upvalue
,
false
,
false
);
}
static
int
resolve_arguments_upvalue
(
sv_compiler_t
*
c
)
{
if
(
!
c
->
enclosing
)
return
-1
;
sv_compiler_t
*
enc
=
c
->
enclosing
;
if
(
!
enc
->
is_arrow
)
{
if
(
enc
->
strict_args_local
<
0
)
return
-1
;
enc
->
locals
[
enc
->
strict_args_local
].
captured
=
true
;
uint16_t
slot
=
(
uint16_t
)
local_to_frame_slot
(
enc
,
enc
->
strict_args_local
);
return
add_upvalue
(
c
,
slot
,
true
,
false
);
}
int
upvalue
=
resolve_arguments_upvalue
(
enc
);
if
(
upvalue
==
-1
)
return
-1
;
return
add_upvalue
(
c
,
(
uint16_t
)
upvalue
,
false
,
false
);
}
static
int
resolve_upvalue
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
if
(
!
c
->
enclosing
)
return
-1
;
int
local
=
resolve_local
(
c
->
enclosing
,
name
,
len
);
if
(
local
!=
-1
)
{
c
->
enclosing
->
locals
[
local
].
captured
=
true
;
uint16_t
slot
=
(
uint16_t
)
local_to_frame_slot
(
c
->
enclosing
,
local
);
return
add_upvalue
(
c
,
slot
,
true
,
c
->
enclosing
->
locals
[
local
].
is_const
);
}
int
upvalue
=
resolve_upvalue
(
c
->
enclosing
,
name
,
len
);
if
(
upvalue
!=
-1
)
{
bool
uv_const
=
c
->
enclosing
->
upval_descs
[
upvalue
].
is_const
;
return
add_upvalue
(
c
,
(
uint16_t
)
upvalue
,
false
,
uv_const
);
}
return
-1
;
}
static
int
emit_jump
(
sv_compiler_t
*
c
,
sv_op_t
op
)
{
emit_op
(
c
,
op
);
int
offset
=
c
->
code_len
;
emit_i32
(
c
,
0
);
return
offset
;
}
static
void
patch_jump
(
sv_compiler_t
*
c
,
int
offset
)
{
int32_t
delta
=
(
int32_t
)(
c
->
code_len
-
offset
-
4
);
patch_u32
(
c
,
offset
,
(
uint32_t
)
delta
);
}
static
void
emit_loop
(
sv_compiler_t
*
c
,
int
loop_start
)
{
emit_op
(
c
,
OP_JMP
);
int32_t
delta
=
(
int32_t
)(
loop_start
-
c
->
code_len
-
4
);
emit_i32
(
c
,
delta
);
}
static
void
patch_list_add
(
sv_patch_list_t
*
pl
,
int
offset
)
{
if
(
pl
->
count
>=
pl
->
cap
)
{
pl
->
cap
=
pl
->
cap
?
pl
->
cap
*
2
:
4
;
pl
->
offsets
=
realloc
(
pl
->
offsets
,
(
size_t
)
pl
->
cap
*
sizeof
(
int
));
}
pl
->
offsets
[
pl
->
count
++
]
=
offset
;
}
static
void
patch_list_resolve
(
sv_compiler_t
*
c
,
sv_patch_list_t
*
pl
)
{
for
(
int
i
=
0
;
i
<
pl
->
count
;
i
++
)
patch_jump
(
c
,
pl
->
offsets
[
i
]);
free
(
pl
->
offsets
);
*
pl
=
(
sv_patch_list_t
){
0
};
}
static
void
push_loop
(
sv_compiler_t
*
c
,
int
loop_start
,
const
char
*
label
,
uint32_t
label_len
)
{
if
(
c
->
loop_count
>=
c
->
loop_cap
)
{
c
->
loop_cap
=
c
->
loop_cap
?
c
->
loop_cap
*
2
:
4
;
c
->
loops
=
realloc
(
c
->
loops
,
(
size_t
)
c
->
loop_cap
*
sizeof
(
sv_loop_t
));
}
if
(
!
label
&&
c
->
pending_label
)
{
label
=
c
->
pending_label
;
label_len
=
c
->
pending_label_len
;
c
->
pending_label
=
NULL
;
c
->
pending_label_len
=
0
;
}
c
->
loops
[
c
->
loop_count
++
]
=
(
sv_loop_t
){
.
loop_start
=
loop_start
,
.
scope_depth
=
c
->
scope_depth
,
.
label
=
label
,
.
label_len
=
label_len
,
};
}
static
void
pop_loop
(
sv_compiler_t
*
c
)
{
sv_loop_t
*
loop
=
&
c
->
loops
[
--
c
->
loop_count
];
patch_list_resolve
(
c
,
&
loop
->
breaks
);
free
(
loop
->
continues
.
offsets
);
}
static
void
begin_scope
(
sv_compiler_t
*
c
)
{
c
->
scope_depth
++
;
}
static
void
end_scope
(
sv_compiler_t
*
c
)
{
while
(
c
->
local_count
>
0
&&
c
->
locals
[
c
->
local_count
-
1
].
depth
>=
c
->
scope_depth
)
{
sv_local_t
*
loc
=
&
c
->
locals
[
c
->
local_count
-
1
];
if
(
loc
->
captured
)
{
int
frame_slot
=
local_to_frame_slot
(
c
,
c
->
local_count
-
1
);
emit_op
(
c
,
OP_CLOSE_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
frame_slot
);
}
c
->
local_count
--
;
}
c
->
scope_depth
--
;
}
static
void
emit_close_upvals
(
sv_compiler_t
*
c
)
{
for
(
int
i
=
0
;
i
<
c
->
local_count
;
i
++
)
{
if
(
c
->
locals
[
i
].
captured
)
{
int
frame_slot
=
local_to_frame_slot
(
c
,
i
);
emit_op
(
c
,
OP_CLOSE_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
frame_slot
);
return
;
}}
}
static
void
emit_with_get
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
,
uint8_t
fb_kind
,
uint16_t
fb_idx
)
{
int
atom
=
add_atom
(
c
,
name
,
len
);
emit_op
(
c
,
OP_WITH_GET_VAR
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
emit
(
c
,
fb_kind
);
emit_u16
(
c
,
fb_idx
);
}
static
void
emit_with_put
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
,
uint8_t
fb_kind
,
uint16_t
fb_idx
)
{
int
atom
=
add_atom
(
c
,
name
,
len
);
emit_op
(
c
,
OP_WITH_PUT_VAR
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
emit
(
c
,
fb_kind
);
emit_u16
(
c
,
fb_idx
);
}
static
void
emit_get_local
(
sv_compiler_t
*
c
,
int
local_idx
);
static
void
emit_put_local
(
sv_compiler_t
*
c
,
int
local_idx
);
static
void
emit_get_var
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
int
local
=
resolve_local
(
c
,
name
,
len
);
if
(
local
!=
-1
)
{
if
(
c
->
with_depth
>
0
)
{
uint8_t
kind
=
c
->
locals
[
local
].
depth
==
-1
?
WITH_FB_ARG
:
WITH_FB_LOCAL
;
uint16_t
idx
=
kind
==
WITH_FB_ARG
?
(
uint16_t
)
local
:
(
uint16_t
)(
local
-
c
->
param_locals
);
emit_with_get
(
c
,
name
,
len
,
kind
,
idx
);
return
;
}
if
(
c
->
locals
[
local
].
depth
==
-1
)
{
emit_op
(
c
,
OP_GET_ARG
);
emit_u16
(
c
,
(
uint16_t
)
local
);
}
else
{
int
slot
=
local
-
c
->
param_locals
;
if
(
c
->
locals
[
local
].
is_tdz
)
{
int
ai
=
add_atom
(
c
,
name
,
len
);
emit_op
(
c
,
OP_GET_LOCAL_CHK
);
emit_u16
(
c
,
(
uint16_t
)
slot
);
emit_u32
(
c
,
(
uint32_t
)
ai
);
}
else
if
(
slot
<=
255
)
{
emit_op
(
c
,
OP_GET_LOCAL8
);
emit
(
c
,
(
uint8_t
)
slot
);
}
else
{
emit_op
(
c
,
OP_GET_LOCAL
);
emit_u16
(
c
,
(
uint16_t
)
slot
);
}
}
return
;
}
int
upval
=
resolve_upvalue
(
c
,
name
,
len
);
if
(
upval
!=
-1
)
{
if
(
c
->
with_depth
>
0
)
{
emit_with_get
(
c
,
name
,
len
,
WITH_FB_UPVAL
,
(
uint16_t
)
upval
);
return
;
}
emit_op
(
c
,
OP_GET_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
upval
);
return
;
}
if
(
is_ident_str
(
name
,
len
,
"arguments"
,
9
))
{
if
(
has_implicit_arguments_obj
(
c
))
{
if
(
c
->
strict_args_local
>=
0
)
{
emit_get_local
(
c
,
c
->
strict_args_local
);
}
else
{
emit_op
(
c
,
OP_SPECIAL_OBJ
);
emit
(
c
,
0
);
}
return
;
}
if
(
c
->
is_arrow
)
{
int
args_upval
=
resolve_arguments_upvalue
(
c
);
if
(
args_upval
!=
-1
)
{
emit_op
(
c
,
OP_GET_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
args_upval
);
return
;
}
}
}
if
(
c
->
is_arrow
&&
is_ident_str
(
name
,
len
,
"super"
,
5
))
{
int
super_upval
=
resolve_super_upvalue
(
c
);
if
(
super_upval
!=
-1
)
{
emit_op
(
c
,
OP_GET_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
super_upval
);
return
;
}
}
if
(
c
->
with_depth
>
0
)
emit_with_get
(
c
,
name
,
len
,
WITH_FB_GLOBAL
,
0
);
else
emit_atom_op
(
c
,
OP_GET_GLOBAL
,
name
,
len
);
}
static
void
emit_set_var
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
,
bool
keep
)
{
int
local
=
resolve_local
(
c
,
name
,
len
);
if
(
local
!=
-1
)
{
if
(
c
->
locals
[
local
].
is_const
)
{
emit_const_assign_error
(
c
,
name
,
len
);
return
;
}
if
(
c
->
with_depth
>
0
)
{
uint8_t
kind
=
c
->
locals
[
local
].
depth
==
-1
?
WITH_FB_ARG
:
WITH_FB_LOCAL
;
uint16_t
idx
=
kind
==
WITH_FB_ARG
?
(
uint16_t
)
local
:
(
uint16_t
)(
local
-
c
->
param_locals
);
if
(
keep
)
emit_op
(
c
,
OP_DUP
);
emit_with_put
(
c
,
name
,
len
,
kind
,
idx
);
return
;
}
if
(
c
->
locals
[
local
].
depth
==
-1
)
{
emit_op
(
c
,
keep
?
OP_SET_ARG
:
OP_PUT_ARG
);
emit_u16
(
c
,
(
uint16_t
)
local
);
}
else
{
int
slot
=
local
-
c
->
param_locals
;
sv_op_t
op
=
keep
?
(
slot
<=
255
?
OP_SET_LOCAL8
:
OP_SET_LOCAL
)
:
(
slot
<=
255
?
OP_PUT_LOCAL8
:
OP_PUT_LOCAL
);
emit_op
(
c
,
op
);
if
(
slot
<=
255
)
emit
(
c
,
(
uint8_t
)
slot
);
else
emit_u16
(
c
,
(
uint16_t
)
slot
);
}
return
;
}
int
upval
=
resolve_upvalue
(
c
,
name
,
len
);
if
(
upval
!=
-1
)
{
if
(
c
->
upval_descs
[
upval
].
is_const
)
{
emit_const_assign_error
(
c
,
name
,
len
);
return
;
}
if
(
c
->
with_depth
>
0
)
{
if
(
keep
)
emit_op
(
c
,
OP_DUP
);
emit_with_put
(
c
,
name
,
len
,
WITH_FB_UPVAL
,
(
uint16_t
)
upval
);
return
;
}
emit_op
(
c
,
keep
?
OP_SET_UPVAL
:
OP_PUT_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
upval
);
return
;
}
if
(
c
->
with_depth
>
0
)
{
if
(
keep
)
emit_op
(
c
,
OP_DUP
);
emit_with_put
(
c
,
name
,
len
,
WITH_FB_GLOBAL
,
0
);
}
else
{
if
(
keep
)
{
emit_op
(
c
,
OP_DUP
);
emit_atom_op
(
c
,
OP_PUT_GLOBAL
,
name
,
len
);
}
else
emit_atom_op
(
c
,
OP_PUT_GLOBAL
,
name
,
len
);
}
}
static
void
emit_put_local
(
sv_compiler_t
*
c
,
int
local_idx
)
{
int
slot
=
local_idx
-
c
->
param_locals
;
if
(
slot
<=
255
)
{
emit_op
(
c
,
OP_PUT_LOCAL8
);
emit
(
c
,
(
uint8_t
)
slot
);
}
else
{
emit_op
(
c
,
OP_PUT_LOCAL
);
emit_u16
(
c
,
(
uint16_t
)
slot
);
}
}
static
void
emit_get_local
(
sv_compiler_t
*
c
,
int
local_idx
)
{
int
slot
=
local_idx
-
c
->
param_locals
;
if
(
slot
<=
255
)
{
emit_op
(
c
,
OP_GET_LOCAL8
);
emit
(
c
,
(
uint8_t
)
slot
);
}
else
{
emit_op
(
c
,
OP_GET_LOCAL
);
emit_u16
(
c
,
(
uint16_t
)
slot
);
}
}
static
inline
bool
is_ident_name
(
sv_ast_t
*
node
,
const
char
*
name
)
{
size_t
n
=
strlen
(
name
);
return
node
&&
node
->
type
==
N_IDENT
&&
node
->
len
==
(
uint32_t
)
n
&&
memcmp
(
node
->
str
,
name
,
n
)
==
0
;
}
static
void
hoist_var_pattern
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
)
{
if
(
!
pat
)
return
;
switch
(
pat
->
type
)
{
case
N_IDENT
:
if
(
resolve_local
(
c
,
pat
->
str
,
pat
->
len
)
==
-1
)
add_local
(
c
,
pat
->
str
,
pat
->
len
,
false
,
0
);
break
;
case
N_ARRAY
:
case
N_ARRAY_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
hoist_var_pattern
(
c
,
pat
->
args
.
items
[
i
]);
break
;
case
N_OBJECT
:
case
N_OBJECT_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
{
sv_ast_t
*
prop
=
pat
->
args
.
items
[
i
];
if
(
!
prop
)
continue
;
if
(
prop
->
type
==
N_PROPERTY
)
hoist_var_pattern
(
c
,
prop
->
right
);
else
if
(
prop
->
type
==
N_REST
||
prop
->
type
==
N_SPREAD
)
hoist_var_pattern
(
c
,
prop
->
right
);
}
break
;
case
N_ASSIGN_PAT
:
hoist_var_pattern
(
c
,
pat
->
left
);
break
;
case
N_ASSIGN
:
hoist_var_pattern
(
c
,
pat
->
left
);
break
;
case
N_REST
:
case
N_SPREAD
:
hoist_var_pattern
(
c
,
pat
->
right
);
break
;
default
:
break
;
}
}
static
void
hoist_var_decls
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
!
node
)
return
;
switch
(
node
->
type
)
{
case
N_VAR
:
if
(
node
->
var_kind
==
VAR_VAR
)
{
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
decl
=
node
->
args
.
items
[
i
];
if
(
decl
->
type
==
N_VARDECL
&&
decl
->
left
)
hoist_var_pattern
(
c
,
decl
->
left
);
}
}
break
;
case
N_BLOCK
:
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
hoist_var_decls
(
c
,
node
->
args
.
items
[
i
]);
break
;
case
N_IF
:
hoist_var_decls
(
c
,
node
->
left
);
hoist_var_decls
(
c
,
node
->
right
);
break
;
case
N_WHILE
:
case
N_DO_WHILE
:
case
N_LABEL
:
hoist_var_decls
(
c
,
node
->
body
);
break
;
case
N_FOR
:
hoist_var_decls
(
c
,
node
->
init
);
hoist_var_decls
(
c
,
node
->
body
);
break
;
case
N_FOR_IN
:
case
N_FOR_OF
:
case
N_FOR_AWAIT_OF
:
hoist_var_decls
(
c
,
node
->
left
);
hoist_var_decls
(
c
,
node
->
body
);
break
;
case
N_SWITCH
:
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
cas
=
node
->
args
.
items
[
i
];
for
(
int
j
=
0
;
j
<
cas
->
args
.
count
;
j
++
)
hoist_var_decls
(
c
,
cas
->
args
.
items
[
j
]);
}
break
;
case
N_TRY
:
hoist_var_decls
(
c
,
node
->
body
);
hoist_var_decls
(
c
,
node
->
catch_body
);
hoist_var_decls
(
c
,
node
->
finally_body
);
break
;
case
N_EXPORT
:
hoist_var_decls
(
c
,
node
->
left
);
break
;
default
:
break
;
}
}
static
void
hoist_lexical_pattern
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
bool
is_const
)
{
if
(
!
pat
)
return
;
switch
(
pat
->
type
)
{
case
N_IDENT
:
ensure_local_at_depth
(
c
,
pat
->
str
,
pat
->
len
,
is_const
,
c
->
scope_depth
);
break
;
case
N_ASSIGN_PAT
:
hoist_lexical_pattern
(
c
,
pat
->
left
,
is_const
);
break
;
case
N_REST
:
case
N_SPREAD
:
hoist_lexical_pattern
(
c
,
pat
->
right
,
is_const
);
break
;
case
N_ARRAY
:
case
N_ARRAY_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
hoist_lexical_pattern
(
c
,
pat
->
args
.
items
[
i
],
is_const
);
break
;
case
N_OBJECT
:
case
N_OBJECT_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
{
sv_ast_t
*
prop
=
pat
->
args
.
items
[
i
];
if
(
!
prop
)
continue
;
if
(
prop
->
type
==
N_PROPERTY
)
hoist_lexical_pattern
(
c
,
prop
->
right
,
is_const
);
}
break
;
default
:
break
;
}
}
static
void
annex_b_collect_funcs
(
sv_ast_t
*
node
,
sv_ast_list_t
*
out
)
{
if
(
!
node
)
return
;
if
(
node
->
type
==
N_FUNC
&&
node
->
str
&&
!
(
node
->
flags
&
FN_ARROW
))
{
sv_ast_list_push
(
out
,
node
);
return
;
}
if
(
node
->
type
==
N_IF
)
{
annex_b_collect_funcs
(
node
->
left
,
out
);
annex_b_collect_funcs
(
node
->
right
,
out
);
}
else
if
(
node
->
type
==
N_LABEL
)
annex_b_collect_funcs
(
node
->
body
,
out
);
}
static
void
hoist_lexical_decls
(
sv_compiler_t
*
c
,
sv_ast_list_t
*
stmts
)
{
for
(
int
i
=
0
;
i
<
stmts
->
count
;
i
++
)
{
sv_ast_t
*
node
=
stmts
->
items
[
i
];
if
(
!
node
)
continue
;
sv_ast_t
*
decl_node
=
(
node
->
type
==
N_EXPORT
)
?
node
->
left
:
node
;
if
(
!
decl_node
)
continue
;
if
(
decl_node
->
type
==
N_VAR
&&
decl_node
->
var_kind
!=
VAR_VAR
)
{
bool
is_const
=
(
decl_node
->
var_kind
==
VAR_CONST
);
int
lb
=
c
->
local_count
;
for
(
int
j
=
0
;
j
<
decl_node
->
args
.
count
;
j
++
)
{
sv_ast_t
*
decl
=
decl_node
->
args
.
items
[
j
];
if
(
!
decl
||
decl
->
type
!=
N_VARDECL
||
!
decl
->
left
)
continue
;
hoist_lexical_pattern
(
c
,
decl
->
left
,
is_const
);
}
for
(
int
j
=
lb
;
j
<
c
->
local_count
;
j
++
)
{
c
->
locals
[
j
].
is_tdz
=
true
;
int
slot
=
j
-
c
->
param_locals
;
emit_op
(
c
,
OP_SET_LOCAL_UNDEF
);
emit_u16
(
c
,
(
uint16_t
)
slot
);
}
}
else
if
(
decl_node
->
type
==
N_IMPORT_DECL
)
{
for
(
int
j
=
0
;
j
<
decl_node
->
args
.
count
;
j
++
)
{
sv_ast_t
*
spec
=
decl_node
->
args
.
items
[
j
];
if
(
!
spec
||
spec
->
type
!=
N_IMPORT_SPEC
||
!
spec
->
right
||
spec
->
right
->
type
!=
N_IDENT
)
continue
;
ensure_local_at_depth
(
c
,
spec
->
right
->
str
,
spec
->
right
->
len
,
true
,
c
->
scope_depth
);
}
}
else
if
(
decl_node
->
type
==
N_CLASS
&&
decl_node
->
str
)
{
int
lb
=
c
->
local_count
;
ensure_local_at_depth
(
c
,
decl_node
->
str
,
decl_node
->
len
,
false
,
c
->
scope_depth
);
if
(
c
->
local_count
>
lb
)
{
c
->
locals
[
c
->
local_count
-
1
].
is_tdz
=
true
;
int
slot
=
(
c
->
local_count
-
1
)
-
c
->
param_locals
;
emit_op
(
c
,
OP_SET_LOCAL_UNDEF
);
emit_u16
(
c
,
(
uint16_t
)
slot
);
}
}
else
if
(
decl_node
->
type
==
N_FUNC
&&
decl_node
->
str
&&
!
(
decl_node
->
flags
&
FN_ARROW
))
{
ensure_local_at_depth
(
c
,
decl_node
->
str
,
decl_node
->
len
,
false
,
c
->
scope_depth
);
}
if
(
!
c
->
is_strict
&&
(
decl_node
->
type
==
N_IF
||
decl_node
->
type
==
N_LABEL
))
{
sv_ast_list_t
funcs
=
{
0
};
annex_b_collect_funcs
(
decl_node
,
&
funcs
);
for
(
int
j
=
0
;
j
<
funcs
.
count
;
j
++
)
{
sv_ast_t
*
fn
=
funcs
.
items
[
j
];
if
(
resolve_local
(
c
,
fn
->
str
,
fn
->
len
)
==
-1
)
add_local
(
c
,
fn
->
str
,
fn
->
len
,
false
,
c
->
scope_depth
);
}
}
}
}
static
void
hoist_one_func
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
sv_func_t
*
fn
=
compile_function_body
(
c
,
node
,
SV_COMPILE_SCRIPT
);
if
(
!
fn
)
return
;
int
idx
=
add_constant
(
c
,
mkval
(
T_CFUNC
,
(
uintptr_t
)
fn
));
emit_op
(
c
,
OP_CLOSURE
);
emit_u32
(
c
,
(
uint32_t
)
idx
);
emit_set_function_name
(
c
,
node
->
str
,
node
->
len
);
if
(
is_repl_top_level
(
c
))
{
emit_atom_op
(
c
,
OP_PUT_GLOBAL
,
node
->
str
,
node
->
len
);
}
else
{
int
local
=
resolve_local
(
c
,
node
->
str
,
node
->
len
);
emit_put_local
(
c
,
local
);
}
}
static
void
hoist_func_decls
(
sv_compiler_t
*
c
,
sv_ast_list_t
*
stmts
)
{
for
(
int
i
=
0
;
i
<
stmts
->
count
;
i
++
)
{
sv_ast_t
*
node
=
stmts
->
items
[
i
];
if
(
node
&&
node
->
type
==
N_EXPORT
&&
node
->
left
)
node
=
node
->
left
;
if
(
!
node
)
continue
;
if
(
node
->
type
==
N_FUNC
&&
node
->
str
&&
!
(
node
->
flags
&
FN_ARROW
))
{
hoist_one_func
(
c
,
node
);
}
if
(
!
c
->
is_strict
&&
(
node
->
type
==
N_IF
||
node
->
type
==
N_LABEL
))
{
sv_ast_list_t
funcs
=
{
0
};
annex_b_collect_funcs
(
node
,
&
funcs
);
for
(
int
j
=
0
;
j
<
funcs
.
count
;
j
++
)
hoist_one_func
(
c
,
funcs
.
items
[
j
]);
}
}
}
static
void
compile_expr
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
!
node
)
{
emit_op
(
c
,
OP_UNDEF
);
return
;
}
emit_srcpos
(
c
,
node
);
switch
(
node
->
type
)
{
case
N_NUMBER
:
emit_number
(
c
,
node
->
num
);
break
;
case
N_STRING
:
{
emit_constant
(
c
,
ast_string_const
(
c
,
node
));
break
;
}
case
N_BIGINT
:
{
bool
neg
=
false
;
const
char
*
digits
=
node
->
str
;
uint32_t
dlen
=
node
->
len
;
if
(
dlen
>
0
&&
digits
[
0
]
==
'-'
)
{
neg
=
true
;
digits
++
;
dlen
--
;
}
if
(
dlen
>
0
&&
digits
[
dlen
-
1
]
==
'n'
)
dlen
--
;
jsval_t
bi
=
js_mkbigint
(
c
->
js
,
digits
,
dlen
,
neg
);
emit_constant
(
c
,
bi
);
break
;
}
case
N_BOOL
:
emit_op
(
c
,
node
->
num
!=
0.0
?
OP_TRUE
:
OP_FALSE
);
break
;
case
N_NULL
:
emit_op
(
c
,
OP_NULL
);
break
;
case
N_UNDEF
:
emit_op
(
c
,
OP_UNDEF
);
break
;
case
N_THIS
:
emit_op
(
c
,
OP_THIS
);
break
;
case
N_GLOBAL_THIS
:
emit_op
(
c
,
OP_GLOBAL
);
break
;
case
N_NEW_TARGET
:
{
static
const
char
nt_name
[]
=
"
\x01
new.target"
;
int
local
=
resolve_local
(
c
,
nt_name
,
sizeof
(
nt_name
)
-
1
);
if
(
local
>=
0
)
{
emit_get_local
(
c
,
local
);
}
else
{
int
upval
=
resolve_upvalue
(
c
,
nt_name
,
sizeof
(
nt_name
)
-
1
);
if
(
upval
>=
0
)
{
emit_op
(
c
,
OP_GET_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
upval
);
}
else
{
emit_op
(
c
,
OP_UNDEF
);
}
}
break
;
}
case
N_IDENT
:
emit_get_var
(
c
,
node
->
str
,
node
->
len
);
break
;
case
N_BINARY
:
compile_binary
(
c
,
node
);
break
;
case
N_UNARY
:
compile_unary
(
c
,
node
);
break
;
case
N_UPDATE
:
compile_update
(
c
,
node
);
break
;
case
N_ASSIGN
:
compile_assign
(
c
,
node
);
break
;
case
N_TERNARY
:
compile_ternary
(
c
,
node
);
break
;
case
N_CALL
:
compile_call
(
c
,
node
);
break
;
case
N_NEW
:
compile_new
(
c
,
node
);
break
;
case
N_MEMBER
:
compile_member
(
c
,
node
);
break
;
case
N_OPTIONAL
:
compile_optional
(
c
,
node
);
break
;
case
N_ARRAY
:
compile_array
(
c
,
node
);
break
;
case
N_OBJECT
:
compile_object
(
c
,
node
);
break
;
case
N_FUNC
:
compile_func_expr
(
c
,
node
);
break
;
case
N_CLASS
:
compile_class
(
c
,
node
);
break
;
case
N_SEQUENCE
:
compile_expr
(
c
,
node
->
left
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
node
->
right
);
break
;
case
N_TYPEOF
:
compile_typeof
(
c
,
node
);
break
;
case
N_VOID
:
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_VOID
);
break
;
case
N_DELETE
:
compile_delete
(
c
,
node
);
break
;
case
N_SPREAD
:
compile_expr
(
c
,
node
->
right
);
break
;
case
N_TEMPLATE
:
compile_template
(
c
,
node
);
break
;
case
N_AWAIT
:
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_AWAIT
);
if
(
c
->
enclosing
&&
!
c
->
enclosing
->
enclosing
)
c
->
is_tla
=
true
;
break
;
case
N_YIELD
:
if
(
node
->
right
)
compile_expr
(
c
,
node
->
right
);
else
emit_op
(
c
,
OP_UNDEF
);
emit_op
(
c
,
node
->
flags
?
OP_YIELD_STAR
:
OP_YIELD
);
break
;
case
N_TAGGED_TEMPLATE
:
{
compile_expr
(
c
,
node
->
left
);
sv_ast_t
*
tpl
=
node
->
right
;
int
n
=
tpl
->
args
.
count
;
int
n_strings
=
0
,
n_exprs
=
0
;
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
if
(
tpl
->
args
.
items
[
i
]
->
type
==
N_STRING
)
n_strings
++
;
else
n_exprs
++
;
}
int
cache_idx
=
add_constant
(
c
,
js_mkundef
());
emit_op
(
c
,
OP_CONST
);
emit_u32
(
c
,
(
uint32_t
)
cache_idx
);
int
skip_jump
=
emit_jump
(
c
,
OP_JMP_TRUE_PEEK
);
emit_op
(
c
,
OP_POP
);
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
sv_ast_t
*
item
=
tpl
->
args
.
items
[
i
];
if
(
item
->
type
!=
N_STRING
)
continue
;
if
(
is_invalid_cooked_string
(
item
))
emit_op
(
c
,
OP_UNDEF
);
else
emit_constant
(
c
,
ast_string_const
(
c
,
item
));
}
emit_op
(
c
,
OP_ARRAY
);
emit_u16
(
c
,
(
uint16_t
)
n_strings
);
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
sv_ast_t
*
item
=
tpl
->
args
.
items
[
i
];
if
(
item
->
type
!=
N_STRING
)
continue
;
const
char
*
raw
=
item
->
aux
?
item
->
aux
:
item
->
str
;
uint32_t
raw_len
=
item
->
aux
?
item
->
aux_len
:
item
->
len
;
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
raw
?
raw
:
""
,
raw_len
));
}
emit_op
(
c
,
OP_ARRAY
);
emit_u16
(
c
,
(
uint16_t
)
n_strings
);
emit_atom_op
(
c
,
OP_GET_GLOBAL
,
"Object"
,
6
);
emit_atom_op
(
c
,
OP_GET_FIELD2
,
"freeze"
,
6
);
emit_op
(
c
,
OP_ROT3L
);
emit_op
(
c
,
OP_CALL_METHOD
);
emit_u16
(
c
,
1
);
emit_atom_op
(
c
,
OP_DEFINE_FIELD
,
"raw"
,
3
);
emit_atom_op
(
c
,
OP_GET_GLOBAL
,
"Object"
,
6
);
emit_atom_op
(
c
,
OP_GET_FIELD2
,
"freeze"
,
6
);
emit_op
(
c
,
OP_ROT3L
);
emit_op
(
c
,
OP_CALL_METHOD
);
emit_u16
(
c
,
1
);
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_PUT_CONST
);
emit_u32
(
c
,
(
uint32_t
)
cache_idx
);
patch_jump
(
c
,
skip_jump
);
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
sv_ast_t
*
item
=
tpl
->
args
.
items
[
i
];
if
(
item
->
type
==
N_STRING
)
continue
;
compile_expr
(
c
,
item
);
}
emit_op
(
c
,
OP_CALL
);
emit_u16
(
c
,
(
uint16_t
)(
1
+
n_exprs
));
break
;
}
case
N_IMPORT
:
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_IMPORT
);
break
;
case
N_REGEXP
:
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
node
->
str
?
node
->
str
:
""
,
node
->
len
));
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
node
->
aux
?
node
->
aux
:
""
,
node
->
aux_len
));
emit_op
(
c
,
OP_REGEXP
);
break
;
default
:
emit_op
(
c
,
OP_UNDEF
);
break
;
}
}
static
void
compile_binary
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
uint8_t
op
=
node
->
op
;
if
(
op
==
TOK_LAND
)
{
compile_expr
(
c
,
node
->
left
);
int
jump
=
emit_jump
(
c
,
OP_JMP_FALSE_PEEK
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
node
->
right
);
patch_jump
(
c
,
jump
);
return
;
}
if
(
op
==
TOK_LOR
)
{
compile_expr
(
c
,
node
->
left
);
int
jump
=
emit_jump
(
c
,
OP_JMP_TRUE_PEEK
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
node
->
right
);
patch_jump
(
c
,
jump
);
return
;
}
if
(
op
==
TOK_NULLISH
)
{
compile_expr
(
c
,
node
->
left
);
int
jump
=
emit_jump
(
c
,
OP_JMP_NOT_NULLISH
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
node
->
right
);
patch_jump
(
c
,
jump
);
return
;
}
if
(
op
==
TOK_IN
&&
node
->
left
->
type
==
N_IDENT
&&
node
->
left
->
len
>
0
&&
node
->
left
->
str
[
0
]
==
'#'
)
{
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
node
->
left
->
str
,
node
->
left
->
len
));
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_IN
);
return
;
}
compile_expr
(
c
,
node
->
left
);
compile_expr
(
c
,
node
->
right
);
switch
(
op
)
{
case
TOK_PLUS
:
emit_op
(
c
,
OP_ADD
);
break
;
case
TOK_MINUS
:
emit_op
(
c
,
OP_SUB
);
break
;
case
TOK_MUL
:
emit_op
(
c
,
OP_MUL
);
break
;
case
TOK_DIV
:
emit_op
(
c
,
OP_DIV
);
break
;
case
TOK_REM
:
emit_op
(
c
,
OP_MOD
);
break
;
case
TOK_EXP
:
emit_op
(
c
,
OP_EXP
);
break
;
case
TOK_LT
:
emit_op
(
c
,
OP_LT
);
break
;
case
TOK_LE
:
emit_op
(
c
,
OP_LE
);
break
;
case
TOK_GT
:
emit_op
(
c
,
OP_GT
);
break
;
case
TOK_GE
:
emit_op
(
c
,
OP_GE
);
break
;
case
TOK_EQ
:
emit_op
(
c
,
OP_EQ
);
break
;
case
TOK_NE
:
emit_op
(
c
,
OP_NE
);
break
;
case
TOK_SEQ
:
emit_op
(
c
,
OP_SEQ
);
break
;
case
TOK_SNE
:
emit_op
(
c
,
OP_SNE
);
break
;
case
TOK_AND
:
emit_op
(
c
,
OP_BAND
);
break
;
case
TOK_OR
:
emit_op
(
c
,
OP_BOR
);
break
;
case
TOK_XOR
:
emit_op
(
c
,
OP_BXOR
);
break
;
case
TOK_SHL
:
emit_op
(
c
,
OP_SHL
);
break
;
case
TOK_SHR
:
emit_op
(
c
,
OP_SHR
);
break
;
case
TOK_ZSHR
:
emit_op
(
c
,
OP_USHR
);
break
;
case
TOK_INSTANCEOF
:
emit_op
(
c
,
OP_INSTANCEOF
);
break
;
case
TOK_IN
:
emit_op
(
c
,
OP_IN
);
break
;
default
:
emit_op
(
c
,
OP_UNDEF
);
break
;
}
}
static
void
compile_unary
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
compile_expr
(
c
,
node
->
right
);
switch
(
node
->
op
)
{
case
TOK_NOT
:
emit_op
(
c
,
OP_NOT
);
break
;
case
TOK_TILDA
:
emit_op
(
c
,
OP_BNOT
);
break
;
case
TOK_UPLUS
:
emit_op
(
c
,
OP_UPLUS
);
break
;
case
TOK_UMINUS
:
emit_op
(
c
,
OP_NEG
);
break
;
default
:
break
;
}
}
static
void
compile_update
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
bool
prefix
=
(
node
->
flags
&
1
);
bool
is_inc
=
(
node
->
op
==
TOK_POSTINC
);
sv_ast_t
*
target
=
node
->
right
;
if
(
target
->
type
==
N_IDENT
)
{
if
(
prefix
)
{
emit_get_var
(
c
,
target
->
str
,
target
->
len
);
emit_op
(
c
,
is_inc
?
OP_INC
:
OP_DEC
);
emit_set_var
(
c
,
target
->
str
,
target
->
len
,
true
);
}
else
{
emit_get_var
(
c
,
target
->
str
,
target
->
len
);
emit_op
(
c
,
is_inc
?
OP_POST_INC
:
OP_POST_DEC
);
emit_set_var
(
c
,
target
->
str
,
target
->
len
,
false
);
}
}
else
if
(
target
->
type
==
N_MEMBER
&&
!
(
target
->
flags
&
1
))
{
compile_expr
(
c
,
target
->
left
);
emit_op
(
c
,
OP_DUP
);
int
atom
=
add_atom
(
c
,
target
->
right
->
str
,
target
->
right
->
len
);
emit_op
(
c
,
OP_GET_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
if
(
prefix
)
{
emit_op
(
c
,
is_inc
?
OP_INC
:
OP_DEC
);
emit_op
(
c
,
OP_INSERT2
);
emit_op
(
c
,
OP_PUT_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
}
else
{
emit_op
(
c
,
is_inc
?
OP_POST_INC
:
OP_POST_DEC
);
emit_op
(
c
,
OP_SWAP_UNDER
);
emit_op
(
c
,
OP_PUT_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
}
}
else
if
(
target
->
type
==
N_MEMBER
&&
(
target
->
flags
&
1
))
{
compile_expr
(
c
,
target
->
left
);
compile_expr
(
c
,
target
->
right
);
emit_op
(
c
,
OP_DUP2
);
emit_op
(
c
,
OP_GET_ELEM
);
if
(
prefix
)
{
emit_op
(
c
,
is_inc
?
OP_INC
:
OP_DEC
);
emit_op
(
c
,
OP_INSERT3
);
emit_op
(
c
,
OP_PUT_ELEM
);
}
else
{
emit_op
(
c
,
is_inc
?
OP_POST_INC
:
OP_POST_DEC
);
emit_op
(
c
,
OP_ROT4_UNDER
);
emit_op
(
c
,
OP_PUT_ELEM
);
}
}
else
{
emit_op
(
c
,
OP_UNDEF
);
}
}
static
void
compile_assign
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
sv_ast_t
*
target
=
node
->
left
;
uint8_t
op
=
node
->
op
;
if
(
op
==
TOK_ASSIGN
)
{
if
(
target
->
type
==
N_MEMBER
&&
!
(
target
->
flags
&
1
))
{
int
atom
=
add_atom
(
c
,
target
->
right
->
str
,
target
->
right
->
len
);
compile_expr
(
c
,
target
->
left
);
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_INSERT2
);
emit_op
(
c
,
OP_PUT_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
return
;
}
if
(
target
->
type
==
N_MEMBER
&&
(
target
->
flags
&
1
))
{
compile_expr
(
c
,
target
->
left
);
compile_expr
(
c
,
target
->
right
);
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_INSERT3
);
emit_op
(
c
,
OP_PUT_ELEM
);
return
;
}
compile_expr
(
c
,
node
->
right
);
compile_lhs_set
(
c
,
target
,
true
);
return
;
}
if
(
target
->
type
==
N_IDENT
)
{
if
(
op
==
TOK_PLUS_ASSIGN
)
{
int
slot
=
resolve_local_slot
(
c
,
target
->
str
,
target
->
len
);
if
(
slot
>=
0
&&
!
c
->
locals
[
slot
+
c
->
param_locals
].
is_const
)
{
sv_ast_t
*
rhs
=
node
->
right
;
bool
rhs_pure
=
rhs
&&
(
rhs
->
type
==
N_NUMBER
||
rhs
->
type
==
N_STRING
||
rhs
->
type
==
N_BOOL
||
rhs
->
type
==
N_NULL
||
rhs
->
type
==
N_UNDEF
||
rhs
->
type
==
N_IDENT
);
if
(
rhs_pure
)
{
compile_expr
(
c
,
rhs
);
emit_op
(
c
,
OP_ADD_LOCAL
);
emit
(
c
,
(
uint8_t
)
slot
);
emit_get_local
(
c
,
c
->
param_locals
+
slot
);
return
;
}
}
}
if
(
op
==
TOK_LOR_ASSIGN
||
op
==
TOK_LAND_ASSIGN
||
op
==
TOK_NULLISH_ASSIGN
)
{
emit_get_var
(
c
,
target
->
str
,
target
->
len
);
int
skip
=
emit_jump
(
c
,
op
==
TOK_LOR_ASSIGN
?
OP_JMP_TRUE_PEEK
:
op
==
TOK_LAND_ASSIGN
?
OP_JMP_FALSE_PEEK
:
OP_JMP_NOT_NULLISH
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
node
->
right
);
emit_set_var
(
c
,
target
->
str
,
target
->
len
,
true
);
patch_jump
(
c
,
skip
);
return
;
}
emit_get_var
(
c
,
target
->
str
,
target
->
len
);
compile_expr
(
c
,
node
->
right
);
switch
(
op
)
{
case
TOK_PLUS_ASSIGN
:
emit_op
(
c
,
OP_ADD
);
break
;
case
TOK_MINUS_ASSIGN
:
emit_op
(
c
,
OP_SUB
);
break
;
case
TOK_MUL_ASSIGN
:
emit_op
(
c
,
OP_MUL
);
break
;
case
TOK_DIV_ASSIGN
:
emit_op
(
c
,
OP_DIV
);
break
;
case
TOK_REM_ASSIGN
:
emit_op
(
c
,
OP_MOD
);
break
;
case
TOK_SHL_ASSIGN
:
emit_op
(
c
,
OP_SHL
);
break
;
case
TOK_SHR_ASSIGN
:
emit_op
(
c
,
OP_SHR
);
break
;
case
TOK_ZSHR_ASSIGN
:
emit_op
(
c
,
OP_USHR
);
break
;
case
TOK_AND_ASSIGN
:
emit_op
(
c
,
OP_BAND
);
break
;
case
TOK_XOR_ASSIGN
:
emit_op
(
c
,
OP_BXOR
);
break
;
case
TOK_OR_ASSIGN
:
emit_op
(
c
,
OP_BOR
);
break
;
case
TOK_EXP_ASSIGN
:
emit_op
(
c
,
OP_EXP
);
break
;
default
:
break
;
}
emit_set_var
(
c
,
target
->
str
,
target
->
len
,
true
);
}
else
if
(
target
->
type
==
N_MEMBER
&&
!
(
target
->
flags
&
1
))
{
int
atom
=
add_atom
(
c
,
target
->
right
->
str
,
target
->
right
->
len
);
if
(
op
==
TOK_LOR_ASSIGN
||
op
==
TOK_LAND_ASSIGN
||
op
==
TOK_NULLISH_ASSIGN
)
{
compile_expr
(
c
,
target
->
left
);
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_GET_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
int
skip
=
emit_jump
(
c
,
op
==
TOK_LOR_ASSIGN
?
OP_JMP_TRUE_PEEK
:
op
==
TOK_LAND_ASSIGN
?
OP_JMP_FALSE_PEEK
:
OP_JMP_NOT_NULLISH
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_INSERT2
);
emit_op
(
c
,
OP_PUT_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
int
end
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
skip
);
emit_op
(
c
,
OP_NIP
);
patch_jump
(
c
,
end
);
return
;
}
compile_expr
(
c
,
target
->
left
);
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_GET_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
compile_expr
(
c
,
node
->
right
);
switch
(
op
)
{
case
TOK_PLUS_ASSIGN
:
emit_op
(
c
,
OP_ADD
);
break
;
case
TOK_MINUS_ASSIGN
:
emit_op
(
c
,
OP_SUB
);
break
;
case
TOK_MUL_ASSIGN
:
emit_op
(
c
,
OP_MUL
);
break
;
case
TOK_DIV_ASSIGN
:
emit_op
(
c
,
OP_DIV
);
break
;
case
TOK_REM_ASSIGN
:
emit_op
(
c
,
OP_MOD
);
break
;
case
TOK_SHL_ASSIGN
:
emit_op
(
c
,
OP_SHL
);
break
;
case
TOK_SHR_ASSIGN
:
emit_op
(
c
,
OP_SHR
);
break
;
case
TOK_ZSHR_ASSIGN
:
emit_op
(
c
,
OP_USHR
);
break
;
case
TOK_AND_ASSIGN
:
emit_op
(
c
,
OP_BAND
);
break
;
case
TOK_XOR_ASSIGN
:
emit_op
(
c
,
OP_BXOR
);
break
;
case
TOK_OR_ASSIGN
:
emit_op
(
c
,
OP_BOR
);
break
;
case
TOK_EXP_ASSIGN
:
emit_op
(
c
,
OP_EXP
);
break
;
default
:
break
;
}
emit_op
(
c
,
OP_INSERT2
);
emit_op
(
c
,
OP_PUT_FIELD
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
}
else
if
(
target
->
type
==
N_MEMBER
&&
(
target
->
flags
&
1
))
{
if
(
op
==
TOK_LOR_ASSIGN
||
op
==
TOK_LAND_ASSIGN
||
op
==
TOK_NULLISH_ASSIGN
)
{
compile_expr
(
c
,
target
->
left
);
compile_expr
(
c
,
target
->
right
);
emit_op
(
c
,
OP_DUP2
);
emit_op
(
c
,
OP_GET_ELEM
);
int
skip
=
emit_jump
(
c
,
op
==
TOK_LOR_ASSIGN
?
OP_JMP_TRUE_PEEK
:
op
==
TOK_LAND_ASSIGN
?
OP_JMP_FALSE_PEEK
:
OP_JMP_NOT_NULLISH
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_INSERT3
);
emit_op
(
c
,
OP_PUT_ELEM
);
int
end
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
skip
);
emit_op
(
c
,
OP_NIP2
);
patch_jump
(
c
,
end
);
return
;
}
compile_expr
(
c
,
target
->
left
);
compile_expr
(
c
,
target
->
right
);
emit_op
(
c
,
OP_DUP2
);
emit_op
(
c
,
OP_GET_ELEM
);
compile_expr
(
c
,
node
->
right
);
switch
(
op
)
{
case
TOK_PLUS_ASSIGN
:
emit_op
(
c
,
OP_ADD
);
break
;
case
TOK_MINUS_ASSIGN
:
emit_op
(
c
,
OP_SUB
);
break
;
case
TOK_MUL_ASSIGN
:
emit_op
(
c
,
OP_MUL
);
break
;
case
TOK_DIV_ASSIGN
:
emit_op
(
c
,
OP_DIV
);
break
;
case
TOK_REM_ASSIGN
:
emit_op
(
c
,
OP_MOD
);
break
;
case
TOK_SHL_ASSIGN
:
emit_op
(
c
,
OP_SHL
);
break
;
case
TOK_SHR_ASSIGN
:
emit_op
(
c
,
OP_SHR
);
break
;
case
TOK_ZSHR_ASSIGN
:
emit_op
(
c
,
OP_USHR
);
break
;
case
TOK_AND_ASSIGN
:
emit_op
(
c
,
OP_BAND
);
break
;
case
TOK_XOR_ASSIGN
:
emit_op
(
c
,
OP_BXOR
);
break
;
case
TOK_OR_ASSIGN
:
emit_op
(
c
,
OP_BOR
);
break
;
case
TOK_EXP_ASSIGN
:
emit_op
(
c
,
OP_EXP
);
break
;
default
:
break
;
}
emit_op
(
c
,
OP_INSERT3
);
emit_op
(
c
,
OP_PUT_ELEM
);
}
else
{
compile_expr
(
c
,
node
->
right
);
}
}
static
void
compile_lhs_set
(
sv_compiler_t
*
c
,
sv_ast_t
*
target
,
bool
keep
)
{
if
(
target
->
type
==
N_IDENT
)
{
emit_set_var
(
c
,
target
->
str
,
target
->
len
,
keep
);
}
else
if
(
target
->
type
==
N_MEMBER
&&
!
(
target
->
flags
&
1
))
{
if
(
keep
)
emit_op
(
c
,
OP_DUP
);
compile_expr
(
c
,
target
->
left
);
emit_op
(
c
,
OP_SWAP
);
emit_atom_op
(
c
,
OP_PUT_FIELD
,
target
->
right
->
str
,
target
->
right
->
len
);
}
else
if
(
target
->
type
==
N_MEMBER
&&
(
target
->
flags
&
1
))
{
if
(
keep
)
emit_op
(
c
,
OP_DUP
);
compile_expr
(
c
,
target
->
left
);
compile_expr
(
c
,
target
->
right
);
emit_op
(
c
,
OP_ROT3L
);
emit_op
(
c
,
OP_PUT_ELEM
);
}
else
if
(
target
->
type
==
N_ARRAY_PAT
||
target
->
type
==
N_ARRAY
)
{
compile_array_destructure
(
c
,
target
,
keep
);
}
else
if
(
target
->
type
==
N_OBJECT_PAT
||
target
->
type
==
N_OBJECT
)
{
compile_object_destructure
(
c
,
target
,
keep
);
}
}
static
void
compile_ternary
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
compile_expr
(
c
,
node
->
cond
);
int
else_jump
=
emit_jump
(
c
,
OP_JMP_FALSE
);
compile_expr
(
c
,
node
->
left
);
int
end_jump
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
else_jump
);
compile_expr
(
c
,
node
->
right
);
patch_jump
(
c
,
end_jump
);
}
static
void
compile_typeof
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
sv_ast_t
*
arg
=
node
->
right
;
if
(
arg
->
type
==
N_IDENT
)
{
int
local
=
resolve_local
(
c
,
arg
->
str
,
arg
->
len
);
if
(
local
!=
-1
)
{
emit_get_var
(
c
,
arg
->
str
,
arg
->
len
);
}
else
{
int
upval
=
resolve_upvalue
(
c
,
arg
->
str
,
arg
->
len
);
if
(
upval
!=
-1
)
{
emit_op
(
c
,
OP_GET_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
upval
);
}
else
if
(
has_implicit_arguments_obj
(
c
)
&&
is_ident_str
(
arg
->
str
,
arg
->
len
,
"arguments"
,
9
)
)
{
if
(
c
->
strict_args_local
>=
0
)
{
emit_get_local
(
c
,
c
->
strict_args_local
);
}
else
{
emit_op
(
c
,
OP_SPECIAL_OBJ
);
emit
(
c
,
0
);
}
}
else
emit_atom_op
(
c
,
OP_GET_GLOBAL_UNDEF
,
arg
->
str
,
arg
->
len
);
}
}
else
compile_expr
(
c
,
arg
);
emit_op
(
c
,
OP_TYPEOF
);
}
static
void
compile_delete
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
sv_ast_t
*
arg
=
node
->
right
;
if
(
arg
->
type
==
N_MEMBER
&&
!
(
arg
->
flags
&
1
))
{
compile_expr
(
c
,
arg
->
left
);
jsval_t
key
=
js_mkstr
(
c
->
js
,
arg
->
right
->
str
,
arg
->
right
->
len
);
emit_constant
(
c
,
key
);
emit_op
(
c
,
OP_DELETE
);
}
else
if
(
arg
->
type
==
N_MEMBER
&&
(
arg
->
flags
&
1
))
{
compile_expr
(
c
,
arg
->
left
);
compile_expr
(
c
,
arg
->
right
);
emit_op
(
c
,
OP_DELETE
);
}
else
if
(
arg
->
type
==
N_IDENT
)
{
emit_atom_op
(
c
,
OP_DELETE_VAR
,
arg
->
str
,
arg
->
len
);
}
else
{
compile_expr
(
c
,
arg
);
emit_op
(
c
,
OP_POP
);
emit_op
(
c
,
OP_TRUE
);
}
}
static
void
compile_template
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
int
n
=
node
->
args
.
count
;
if
(
n
==
0
)
{
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
""
,
0
));
return
;
}
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
sv_ast_t
*
item
=
node
->
args
.
items
[
i
];
if
(
item
->
type
==
N_STRING
&&
is_invalid_cooked_string
(
item
))
{
static
const
char
msg
[]
=
"Invalid or unexpected token"
;
int
atom
=
add_atom
(
c
,
msg
,
sizeof
(
msg
)
-
1
);
emit_op
(
c
,
OP_THROW_ERROR
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
emit
(
c
,
(
uint8_t
)
JS_ERR_SYNTAX
);
return
;
}
}
compile_expr
(
c
,
node
->
args
.
items
[
0
]);
if
(
node
->
args
.
items
[
0
]
->
type
!=
N_STRING
)
emit_op
(
c
,
OP_TO_PROPKEY
);
for
(
int
i
=
1
;
i
<
n
;
i
++
)
{
compile_expr
(
c
,
node
->
args
.
items
[
i
]);
if
(
node
->
args
.
items
[
i
]
->
type
!=
N_STRING
)
emit_op
(
c
,
OP_TO_PROPKEY
);
emit_op
(
c
,
OP_ADD
);
}
}
static
bool
call_has_spread_arg
(
const
sv_ast_t
*
node
)
{
if
(
!
node
)
return
false
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
arg
=
node
->
args
.
items
[
i
];
if
(
arg
&&
arg
->
type
==
N_SPREAD
)
return
true
;
}
return
false
;
}
static
void
compile_push_arg_to_array
(
sv_compiler_t
*
c
,
sv_ast_t
*
arg
)
{
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_DUP
);
emit_atom_op
(
c
,
OP_GET_FIELD
,
"push"
,
4
);
compile_expr
(
c
,
arg
);
emit_op
(
c
,
OP_CALL_METHOD
);
emit_u16
(
c
,
1
);
emit_op
(
c
,
OP_POP
);
}
static
void
compile_concat_spread_to_array
(
sv_compiler_t
*
c
,
sv_ast_t
*
spread_arg
)
{
compile_expr
(
c
,
spread_arg
->
right
);
emit_op
(
c
,
OP_SPREAD
);
}
static
void
compile_call_args_array
(
sv_compiler_t
*
c
,
sv_ast_t
*
call_node
)
{
emit_op
(
c
,
OP_ARRAY
);
emit_u16
(
c
,
0
);
for
(
int
i
=
0
;
i
<
call_node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
arg
=
call_node
->
args
.
items
[
i
];
if
(
arg
&&
arg
->
type
==
N_SPREAD
)
compile_concat_spread_to_array
(
c
,
arg
);
else
compile_push_arg_to_array
(
c
,
arg
);
}
}
typedef
enum
{
SV_CALL_DIRECT
=
0
,
SV_CALL_METHOD
=
1
,
}
sv_call_kind_t
;
static
void
compile_receiver_property_get
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
emit_op
(
c
,
OP_DUP
);
if
(
node
->
flags
&
1
)
{
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_GET_ELEM
);
}
else
{
emit_srcpos
(
c
,
node
->
right
);
emit_atom_op
(
c
,
OP_GET_FIELD
,
node
->
right
->
str
,
node
->
right
->
len
);
}
}
static
void
compile_call_emit_invoke
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
,
sv_call_kind_t
kind
,
bool
has_spread
)
{
int
argc
=
node
->
args
.
count
;
if
(
has_spread
)
{
if
(
kind
==
SV_CALL_METHOD
)
emit_op
(
c
,
OP_SWAP
);
else
emit_op
(
c
,
OP_GLOBAL
);
compile_call_args_array
(
c
,
node
);
emit_op
(
c
,
OP_APPLY
);
emit_u16
(
c
,
1
);
return
;
}
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
compile_expr
(
c
,
node
->
args
.
items
[
i
]);
emit_op
(
c
,
kind
==
SV_CALL_METHOD
?
OP_CALL_METHOD
:
OP_CALL
);
emit_u16
(
c
,
(
uint16_t
)
argc
);
}
static
sv_call_kind_t
compile_call_setup_non_optional
(
sv_compiler_t
*
c
,
sv_ast_t
*
callee
)
{
if
(
is_ident_name
(
callee
,
"super"
))
{
emit_op
(
c
,
OP_THIS
);
emit_get_var
(
c
,
"super"
,
5
);
return
SV_CALL_METHOD
;
}
if
(
callee
->
type
==
N_MEMBER
&&
is_ident_name
(
callee
->
left
,
"super"
))
{
emit_op
(
c
,
OP_THIS
);
emit_op
(
c
,
OP_THIS
);
emit_get_var
(
c
,
"super"
,
5
);
if
(
callee
->
flags
&
1
)
compile_expr
(
c
,
callee
->
right
);
else
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
callee
->
right
->
str
,
callee
->
right
->
len
));
emit_op
(
c
,
OP_GET_SUPER_VAL
);
return
SV_CALL_METHOD
;
}
if
(
callee
->
type
==
N_MEMBER
)
{
compile_expr
(
c
,
callee
->
left
);
compile_receiver_property_get
(
c
,
callee
);
return
SV_CALL_METHOD
;
}
compile_expr
(
c
,
callee
);
return
SV_CALL_DIRECT
;
}
static
void
compile_optional_call_after_setup
(
sv_compiler_t
*
c
,
sv_ast_t
*
call_node
,
sv_call_kind_t
kind
,
bool
has_spread
)
{
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_IS_UNDEF_OR_NULL
);
int
j_do_call
=
emit_jump
(
c
,
OP_JMP_FALSE
);
emit_op
(
c
,
OP_POP
);
if
(
kind
==
SV_CALL_METHOD
)
emit_op
(
c
,
OP_POP
);
emit_op
(
c
,
OP_UNDEF
);
int
j_end
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
j_do_call
);
compile_call_emit_invoke
(
c
,
call_node
,
kind
,
has_spread
);
patch_jump
(
c
,
j_end
);
}
static
void
compile_call_optional
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
,
sv_ast_t
*
opt_callee
,
bool
has_spread
)
{
if
(
opt_callee
->
right
)
{
compile_expr
(
c
,
opt_callee
->
left
);
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_IS_UNDEF_OR_NULL
);
int
j_have_obj
=
emit_jump
(
c
,
OP_JMP_FALSE
);
emit_op
(
c
,
OP_POP
);
emit_op
(
c
,
OP_UNDEF
);
int
j_end
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
j_have_obj
);
compile_receiver_property_get
(
c
,
opt_callee
);
compile_call_emit_invoke
(
c
,
node
,
SV_CALL_METHOD
,
has_spread
);
patch_jump
(
c
,
j_end
);
return
;
}
sv_ast_t
*
target
=
opt_callee
->
left
;
if
(
target
&&
target
->
type
==
N_OPTIONAL
&&
target
->
right
)
{
compile_expr
(
c
,
target
->
left
);
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_IS_UNDEF_OR_NULL
);
int
j_have_obj
=
emit_jump
(
c
,
OP_JMP_FALSE
);
emit_op
(
c
,
OP_POP
);
emit_op
(
c
,
OP_UNDEF
);
int
j_end
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
j_have_obj
);
compile_receiver_property_get
(
c
,
target
);
compile_optional_call_after_setup
(
c
,
node
,
SV_CALL_METHOD
,
has_spread
);
patch_jump
(
c
,
j_end
);
return
;
}
sv_call_kind_t
kind
=
compile_call_setup_non_optional
(
c
,
target
);
compile_optional_call_after_setup
(
c
,
node
,
kind
,
has_spread
);
}
static
void
compile_call
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
sv_ast_t
*
callee
=
node
->
left
;
bool
has_spread
=
call_has_spread_arg
(
node
);
if
(
callee
->
type
==
N_OPTIONAL
)
{
compile_call_optional
(
c
,
node
,
callee
,
has_spread
);
return
;
}
if
(
!
has_spread
&&
is_ident_name
(
callee
,
"eval"
))
{
if
(
node
->
args
.
count
>
0
)
compile_expr
(
c
,
node
->
args
.
items
[
0
]);
else
emit_op
(
c
,
OP_UNDEF
);
for
(
int
i
=
1
;
i
<
node
->
args
.
count
;
i
++
)
{
compile_expr
(
c
,
node
->
args
.
items
[
i
]);
emit_op
(
c
,
OP_POP
);
}
emit_op
(
c
,
OP_EVAL
);
emit_u32
(
c
,
0
);
return
;
}
sv_call_kind_t
kind
=
compile_call_setup_non_optional
(
c
,
callee
);
compile_call_emit_invoke
(
c
,
node
,
kind
,
has_spread
);
}
static
void
compile_new
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
compile_expr
(
c
,
node
->
left
);
emit_op
(
c
,
OP_DUP
);
int
argc
=
node
->
args
.
count
;
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
compile_expr
(
c
,
node
->
args
.
items
[
i
]);
emit_op
(
c
,
OP_NEW
);
emit_u16
(
c
,
(
uint16_t
)
argc
);
}
static
bool
sv_node_has_optional_base
(
sv_ast_t
*
n
)
{
while
(
n
)
{
if
(
n
->
type
==
N_OPTIONAL
)
return
true
;
if
(
n
->
type
==
N_MEMBER
||
n
->
type
==
N_CALL
)
n
=
n
->
left
;
else
break
;
}
return
false
;
}
static
void
compile_member
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
is_ident_name
(
node
->
left
,
"super"
))
{
emit_op
(
c
,
OP_THIS
);
emit_get_var
(
c
,
"super"
,
5
);
if
(
node
->
flags
&
1
)
compile_expr
(
c
,
node
->
right
);
else
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
node
->
right
->
str
,
node
->
right
->
len
));
emit_op
(
c
,
OP_GET_SUPER_VAL
);
return
;
}
compile_expr
(
c
,
node
->
left
);
int
ok_jump
=
-1
,
end_jump
=
-1
;
if
(
sv_node_has_optional_base
(
node
->
left
))
{
ok_jump
=
emit_jump
(
c
,
OP_JMP_NOT_NULLISH
);
end_jump
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
ok_jump
);
}
if
(
node
->
flags
&
1
)
{
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_GET_ELEM
);
}
else
{
if
(
node
->
right
->
len
==
6
&&
memcmp
(
node
->
right
->
str
,
"length"
,
6
)
==
0
)
emit_op
(
c
,
OP_GET_LENGTH
);
else
{
emit_srcpos
(
c
,
node
->
right
);
emit_atom_op
(
c
,
OP_GET_FIELD
,
node
->
right
->
str
,
node
->
right
->
len
);
}
}
if
(
end_jump
>=
0
)
patch_jump
(
c
,
end_jump
);
}
static
void
compile_optional_get
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
node
->
flags
&
1
)
{
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_GET_ELEM_OPT
);
}
else
{
emit_srcpos
(
c
,
node
->
right
);
emit_atom_op
(
c
,
OP_GET_FIELD_OPT
,
node
->
right
->
str
,
node
->
right
->
len
);
}
}
static
void
compile_optional
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
compile_expr
(
c
,
node
->
left
);
compile_optional_get
(
c
,
node
);
}
static
void
compile_array
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
int
count
=
node
->
args
.
count
;
bool
has_spread
=
false
;
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
sv_ast_t
*
elem
=
node
->
args
.
items
[
i
];
if
(
elem
&&
elem
->
type
==
N_SPREAD
)
{
has_spread
=
true
;
break
;
}
}
if
(
!
has_spread
)
{
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
sv_ast_t
*
elem
=
node
->
args
.
items
[
i
];
if
(
elem
&&
elem
->
type
==
N_EMPTY
)
emit_op
(
c
,
OP_EMPTY
);
else
compile_expr
(
c
,
elem
);
}
emit_op
(
c
,
OP_ARRAY
);
emit_u16
(
c
,
(
uint16_t
)
count
);
return
;
}
emit_op
(
c
,
OP_ARRAY
);
emit_u16
(
c
,
0
);
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
sv_ast_t
*
elem
=
node
->
args
.
items
[
i
];
if
(
elem
&&
elem
->
type
==
N_SPREAD
)
compile_concat_spread_to_array
(
c
,
elem
);
else
compile_push_arg_to_array
(
c
,
elem
);
}
}
static
void
compile_object
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
emit_op
(
c
,
OP_OBJECT
);
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
prop
=
node
->
args
.
items
[
i
];
if
(
prop
->
type
==
N_SPREAD
)
{
compile_expr
(
c
,
prop
->
right
);
emit_op
(
c
,
OP_COPY_DATA_PROPS
);
emit
(
c
,
0
);
emit_op
(
c
,
OP_POP
);
continue
;
}
if
(
prop
->
type
!=
N_PROPERTY
)
continue
;
if
(
prop
->
flags
&
FN_GETTER
||
prop
->
flags
&
FN_SETTER
)
{
compile_expr
(
c
,
prop
->
right
);
uint8_t
flags
=
0
;
if
(
prop
->
flags
&
FN_GETTER
)
flags
|=
1
;
if
(
prop
->
flags
&
FN_SETTER
)
flags
|=
2
;
if
(
prop
->
flags
&
FN_COMPUTED
)
compile_expr
(
c
,
prop
->
left
);
else
compile_static_property_key
(
c
,
prop
->
left
);
emit_op
(
c
,
OP_SWAP
);
emit_op
(
c
,
OP_DEFINE_METHOD_COMP
);
emit
(
c
,
flags
);
}
else
if
(
prop
->
flags
&
FN_COMPUTED
)
{
compile_expr
(
c
,
prop
->
left
);
compile_expr
(
c
,
prop
->
right
);
emit_op
(
c
,
OP_DEFINE_METHOD_COMP
);
emit
(
c
,
0
);
}
else
{
if
(
prop
->
right
&&
(
prop
->
right
->
type
==
N_FUNC
||
prop
->
right
->
type
==
N_CLASS
)
&&
(
!
prop
->
right
->
str
||
prop
->
right
->
len
==
0
)
&&
prop
->
left
&&
prop
->
left
->
type
==
N_IDENT
&&
!
is_quoted_ident_key
(
prop
->
left
))
{
c
->
inferred_name
=
prop
->
left
->
str
;
c
->
inferred_name_len
=
prop
->
left
->
len
;
}
compile_expr
(
c
,
prop
->
right
);
if
((
prop
->
flags
&
FN_COLON
)
&&
prop
->
left
->
type
==
N_IDENT
&&
!
is_quoted_ident_key
(
prop
->
left
)
&&
is_ident_str
(
prop
->
left
->
str
,
prop
->
left
->
len
,
"__proto__"
,
9
))
{
emit_op
(
c
,
OP_SET_PROTO
);
continue
;
}
if
(
prop
->
left
->
type
==
N_IDENT
&&
!
is_quoted_ident_key
(
prop
->
left
))
{
emit_atom_op
(
c
,
OP_DEFINE_FIELD
,
prop
->
left
->
str
,
prop
->
left
->
len
);
}
else
if
(
is_quoted_ident_key
(
prop
->
left
))
{
emit_atom_op
(
c
,
OP_DEFINE_FIELD
,
prop
->
left
->
str
+
1
,
prop
->
left
->
len
-
2
);
}
else
if
(
prop
->
left
->
type
==
N_STRING
)
{
emit_atom_op
(
c
,
OP_DEFINE_FIELD
,
prop
->
left
->
str
?
prop
->
left
->
str
:
""
,
prop
->
left
->
len
);
}
else
if
(
prop
->
left
->
type
==
N_NUMBER
)
{
char
buf
[
32
];
int
n
=
snprintf
(
buf
,
sizeof
(
buf
),
"%g"
,
prop
->
left
->
num
);
emit_atom_op
(
c
,
OP_DEFINE_FIELD
,
buf
,
(
uint32_t
)
n
);
}
else
emit_atom_op
(
c
,
OP_DEFINE_FIELD
,
prop
->
left
->
str
,
prop
->
left
->
len
);
}
}
}
static
void
compile_func_expr
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
bool
has_name
=
node
->
str
&&
node
->
len
>
0
&&
!
(
node
->
flags
&
FN_ARROW
);
int
name_local
=
-1
;
if
(
has_name
)
{
begin_scope
(
c
);
name_local
=
add_local
(
c
,
node
->
str
,
node
->
len
,
true
,
c
->
scope_depth
);
emit_op
(
c
,
OP_UNDEF
);
emit_put_local
(
c
,
name_local
);
}
sv_func_t
*
fn
=
compile_function_body
(
c
,
node
,
SV_COMPILE_SCRIPT
);
if
(
!
fn
)
{
emit_op
(
c
,
OP_UNDEF
);
if
(
has_name
)
end_scope
(
c
);
return
;
}
int
idx
=
add_constant
(
c
,
mkval
(
T_CFUNC
,
(
uintptr_t
)
fn
));
emit_op
(
c
,
OP_CLOSURE
);
emit_u32
(
c
,
(
uint32_t
)
idx
);
if
(
node
->
str
&&
node
->
len
>
0
)
{
emit_set_function_name
(
c
,
node
->
str
,
node
->
len
);
}
else
if
(
c
->
inferred_name
)
{
emit_set_function_name
(
c
,
c
->
inferred_name
,
c
->
inferred_name_len
);
c
->
inferred_name
=
NULL
;
c
->
inferred_name_len
=
0
;
}
else
{
emit_set_function_name
(
c
,
NULL
,
0
);
}
if
(
has_name
)
{
emit_op
(
c
,
OP_DUP
);
emit_put_local
(
c
,
name_local
);
end_scope
(
c
);
}
}
typedef
enum
{
DESTRUCTURE_ASSIGN
=
0
,
DESTRUCTURE_BIND
=
1
,
}
sv_destructure_mode_t
;
static
void
emit_build_object_rest
(
sv_compiler_t
*
c
)
{
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_OBJECT
);
emit_op
(
c
,
OP_SWAP
);
emit_op
(
c
,
OP_COPY_DATA_PROPS
);
emit
(
c
,
0
);
emit_op
(
c
,
OP_POP
);
}
static
void
emit_delete_rest_key
(
sv_compiler_t
*
c
,
sv_ast_t
*
key
)
{
emit_op
(
c
,
OP_DUP
);
if
(
key
->
type
==
N_IDENT
)
{
emit_constant
(
c
,
js_mkstr
(
c
->
js
,
key
->
str
,
key
->
len
));
}
else
if
(
key
->
type
==
N_STRING
)
{
emit_constant
(
c
,
ast_string_const
(
c
,
key
));
}
else
if
(
key
->
type
==
N_NUMBER
)
{
emit_number
(
c
,
key
->
num
);
}
else
{
compile_expr
(
c
,
key
);
}
emit_op
(
c
,
OP_DELETE
);
emit_op
(
c
,
OP_POP
);
}
static
bool
is_destructure_pattern_node
(
sv_ast_t
*
node
)
{
return
node
&&
(
node
->
type
==
N_ARRAY_PAT
||
node
->
type
==
N_ARRAY
||
node
->
type
==
N_OBJECT_PAT
||
node
->
type
==
N_OBJECT
);
}
static
void
compile_destructure_pattern
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
bool
keep
,
bool
consume_source
,
sv_destructure_mode_t
mode
,
sv_var_kind_t
kind
);
static
void
compile_destructure_store
(
sv_compiler_t
*
c
,
sv_ast_t
*
target
,
sv_destructure_mode_t
mode
,
sv_var_kind_t
kind
)
{
if
(
!
target
)
return
;
if
(
mode
==
DESTRUCTURE_ASSIGN
)
{
compile_lhs_set
(
c
,
target
,
false
);
return
;
}
if
(
target
->
type
==
N_IDENT
)
{
bool
is_const
=
(
kind
==
VAR_CONST
);
int
idx
=
ensure_local_at_depth
(
c
,
target
->
str
,
target
->
len
,
is_const
,
c
->
scope_depth
);
emit_put_local
(
c
,
idx
);
return
;
}
if
(
is_destructure_pattern_node
(
target
))
{
compile_destructure_pattern
(
c
,
target
,
false
,
true
,
mode
,
kind
);
return
;
}
compile_lhs_set
(
c
,
target
,
false
);
}
static
void
compile_destructure_pattern
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
bool
keep
,
bool
consume_source
,
sv_destructure_mode_t
mode
,
sv_var_kind_t
kind
)
{
if
(
!
pat
)
return
;
if
(
keep
)
emit_op
(
c
,
OP_DUP
);
if
(
pat
->
type
==
N_ARRAY_PAT
||
pat
->
type
==
N_ARRAY
)
{
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
{
sv_ast_t
*
elem
=
pat
->
args
.
items
[
i
];
if
(
!
elem
||
elem
->
type
==
N_EMPTY
)
continue
;
if
(
elem
->
type
==
N_REST
||
elem
->
type
==
N_SPREAD
)
{
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_DUP
);
emit_atom_op
(
c
,
OP_GET_FIELD
,
"slice"
,
5
);
emit_number
(
c
,
(
double
)
i
);
emit_op
(
c
,
OP_CALL_METHOD
);
emit_u16
(
c
,
1
);
compile_destructure_store
(
c
,
elem
->
right
,
mode
,
kind
);
continue
;
}
sv_ast_t
*
target
=
elem
;
sv_ast_t
*
default_val
=
NULL
;
if
(
elem
->
type
==
N_ASSIGN_PAT
||
(
elem
->
type
==
N_ASSIGN
&&
elem
->
op
==
TOK_ASSIGN
))
{
target
=
elem
->
left
;
default_val
=
elem
->
right
;
}
emit_op
(
c
,
OP_DUP
);
emit_number
(
c
,
(
double
)
i
);
emit_op
(
c
,
OP_GET_ELEM
);
if
(
default_val
)
{
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_IS_UNDEF
);
int
skip
=
emit_jump
(
c
,
OP_JMP_FALSE
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
default_val
);
patch_jump
(
c
,
skip
);
}
compile_destructure_store
(
c
,
target
,
mode
,
kind
);
}
}
else
if
(
pat
->
type
==
N_OBJECT_PAT
||
pat
->
type
==
N_OBJECT
)
{
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
{
sv_ast_t
*
prop
=
pat
->
args
.
items
[
i
];
if
(
!
prop
)
continue
;
if
(
prop
->
type
==
N_REST
||
prop
->
type
==
N_SPREAD
)
{
emit_build_object_rest
(
c
);
for
(
int
j
=
0
;
j
<
i
;
j
++
)
{
sv_ast_t
*
prev
=
pat
->
args
.
items
[
j
];
if
(
!
prev
||
prev
->
type
!=
N_PROPERTY
||
!
prev
->
left
)
continue
;
emit_delete_rest_key
(
c
,
prev
->
left
);
}
compile_destructure_store
(
c
,
prop
->
right
,
mode
,
kind
);
continue
;
}
if
(
prop
->
type
!=
N_PROPERTY
)
continue
;
sv_ast_t
*
key
=
prop
->
left
;
sv_ast_t
*
value
=
prop
->
right
;
sv_ast_t
*
default_val
=
NULL
;
if
(
value
&&
(
value
->
type
==
N_ASSIGN_PAT
||
(
value
->
type
==
N_ASSIGN
&&
value
->
op
==
TOK_ASSIGN
)))
{
default_val
=
value
->
right
;
value
=
value
->
left
;
}
emit_op
(
c
,
OP_DUP
);
if
((
prop
->
flags
&
FN_COMPUTED
))
{
compile_expr
(
c
,
key
);
emit_op
(
c
,
OP_GET_ELEM
);
}
else
if
(
key
->
type
==
N_IDENT
)
{
emit_atom_op
(
c
,
OP_GET_FIELD
,
key
->
str
,
key
->
len
);
}
else
{
compile_expr
(
c
,
key
);
emit_op
(
c
,
OP_GET_ELEM
);
}
if
(
default_val
)
{
emit_op
(
c
,
OP_DUP
);
emit_op
(
c
,
OP_IS_UNDEF
);
int
skip
=
emit_jump
(
c
,
OP_JMP_FALSE
);
emit_op
(
c
,
OP_POP
);
compile_expr
(
c
,
default_val
);
patch_jump
(
c
,
skip
);
}
compile_destructure_store
(
c
,
value
,
mode
,
kind
);
}
}
if
(
consume_source
)
emit_op
(
c
,
OP_POP
);
}
static
void
compile_array_destructure
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
bool
keep
)
{
compile_destructure_pattern
(
c
,
pat
,
keep
,
true
,
DESTRUCTURE_ASSIGN
,
VAR_LET
);
}
static
void
compile_object_destructure
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
bool
keep
)
{
compile_destructure_pattern
(
c
,
pat
,
keep
,
true
,
DESTRUCTURE_ASSIGN
,
VAR_LET
);
}
static
bool
is_tail_callable
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
c
->
try_depth
>
0
)
return
false
;
if
(
node
->
type
!=
N_CALL
)
return
false
;
if
(
call_has_spread_arg
(
node
))
return
false
;
sv_ast_t
*
callee
=
node
->
left
;
if
(
callee
->
type
==
N_IDENT
&&
callee
->
len
==
5
&&
memcmp
(
callee
->
str
,
"super"
,
5
)
==
0
)
return
false
;
return
true
;
}
static
void
compile_tail_call
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
sv_ast_t
*
callee
=
node
->
left
;
if
(
callee
->
type
==
N_OPTIONAL
)
{
compile_call
(
c
,
node
);
emit_close_upvals
(
c
);
emit_op
(
c
,
OP_RETURN
);
return
;
}
sv_call_kind_t
kind
=
compile_call_setup_non_optional
(
c
,
callee
);
int
argc
=
node
->
args
.
count
;
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
compile_expr
(
c
,
node
->
args
.
items
[
i
]);
emit_close_upvals
(
c
);
emit_op
(
c
,
kind
==
SV_CALL_METHOD
?
OP_TAIL_CALL_METHOD
:
OP_TAIL_CALL
);
emit_u16
(
c
,
(
uint16_t
)
argc
);
}
static
void
compile_tail_return_expr
(
sv_compiler_t
*
c
,
sv_ast_t
*
expr
)
{
if
(
expr
->
type
==
N_TERNARY
)
{
compile_expr
(
c
,
expr
->
cond
);
int
else_jump
=
emit_jump
(
c
,
OP_JMP_FALSE
);
compile_tail_return_expr
(
c
,
expr
->
left
);
patch_jump
(
c
,
else_jump
);
compile_tail_return_expr
(
c
,
expr
->
right
);
return
;
}
if
(
is_tail_callable
(
c
,
expr
))
{
compile_tail_call
(
c
,
expr
);
return
;
}
compile_expr
(
c
,
expr
);
emit_close_upvals
(
c
);
emit_op
(
c
,
OP_RETURN
);
}
static
void
compile_stmts
(
sv_compiler_t
*
c
,
sv_ast_list_t
*
list
)
{
for
(
int
i
=
0
;
i
<
list
->
count
;
i
++
)
compile_stmt
(
c
,
list
->
items
[
i
]);
}
static
void
compile_stmt
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
!
node
)
return
;
emit_srcpos
(
c
,
node
);
switch
(
node
->
type
)
{
case
N_EMPTY
:
case
N_DEBUGGER
:
break
;
case
N_BLOCK
:
begin_scope
(
c
);
hoist_lexical_decls
(
c
,
&
node
->
args
);
hoist_func_decls
(
c
,
&
node
->
args
);
compile_stmts
(
c
,
&
node
->
args
);
end_scope
(
c
);
break
;
case
N_VAR
:
compile_var_decl
(
c
,
node
);
break
;
case
N_IMPORT_DECL
:
compile_import_decl
(
c
,
node
);
break
;
case
N_EXPORT
:
compile_export_decl
(
c
,
node
);
break
;
case
N_IF
:
compile_if
(
c
,
node
);
break
;
case
N_WHILE
:
compile_while
(
c
,
node
);
break
;
case
N_DO_WHILE
:
compile_do_while
(
c
,
node
);
break
;
case
N_FOR
:
compile_for
(
c
,
node
);
break
;
case
N_FOR_IN
:
compile_for_in
(
c
,
node
);
break
;
case
N_FOR_OF
:
case
N_FOR_AWAIT_OF
:
compile_for_of
(
c
,
node
);
break
;
case
N_RETURN
:
if
(
node
->
right
)
{
compile_tail_return_expr
(
c
,
node
->
right
);
}
else
{
emit_close_upvals
(
c
);
emit_op
(
c
,
OP_RETURN_UNDEF
);
}
break
;
case
N_THROW
:
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_THROW
);
break
;
case
N_BREAK
:
compile_break
(
c
,
node
);
break
;
case
N_CONTINUE
:
compile_continue
(
c
,
node
);
break
;
case
N_TRY
:
compile_try
(
c
,
node
);
break
;
case
N_SWITCH
:
compile_switch
(
c
,
node
);
break
;
case
N_LABEL
:
compile_label
(
c
,
node
);
break
;
case
N_FUNC
:
if
(
node
->
str
&&
!
(
node
->
flags
&
FN_ARROW
))
break
;
compile_expr
(
c
,
node
);
emit_op
(
c
,
OP_POP
);
break
;
case
N_CLASS
:
compile_class
(
c
,
node
);
emit_op
(
c
,
OP_POP
);
break
;
case
N_WITH
:
compile_expr
(
c
,
node
->
left
);
emit_op
(
c
,
OP_ENTER_WITH
);
c
->
with_depth
++
;
compile_stmt
(
c
,
node
->
body
);
c
->
with_depth
--
;
emit_op
(
c
,
OP_EXIT_WITH
);
break
;
default
:
compile_expr
(
c
,
node
);
emit_op
(
c
,
OP_POP
);
break
;
}
}
enum
{
IMPORT_BIND_DEFAULT
=
1
<<
0
,
IMPORT_BIND_NAMESPACE
=
1
<<
1
,
};
static
void
compile_import_decl
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
!
node
->
right
)
return
;
bool
repl_top
=
is_repl_top_level
(
c
);
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_IMPORT_SYNC
);
if
(
node
->
args
.
count
==
0
)
{
emit_op
(
c
,
OP_POP
);
return
;
}
int
ns_local
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
ns_local
);
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
spec
=
node
->
args
.
items
[
i
];
if
(
!
spec
||
spec
->
type
!=
N_IMPORT_SPEC
||
!
spec
->
right
||
spec
->
right
->
type
!=
N_IDENT
)
continue
;
emit_get_local
(
c
,
ns_local
);
if
(
!
(
spec
->
flags
&
IMPORT_BIND_NAMESPACE
))
{
if
((
spec
->
flags
&
IMPORT_BIND_DEFAULT
)
||
!
spec
->
left
||
spec
->
left
->
type
!=
N_IDENT
)
{
emit_op
(
c
,
OP_IMPORT_DEFAULT
);
}
else
{
emit_atom_op
(
c
,
OP_GET_FIELD
,
spec
->
left
->
str
,
spec
->
left
->
len
);
}
}
if
(
repl_top
)
{
emit_atom_op
(
c
,
OP_PUT_GLOBAL
,
spec
->
right
->
str
,
spec
->
right
->
len
);
}
else
{
int
idx
=
ensure_local_at_depth
(
c
,
spec
->
right
->
str
,
spec
->
right
->
len
,
true
,
c
->
scope_depth
);
emit_put_local
(
c
,
idx
);
}
}
}
static
void
compile_export_emit
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
emit_get_var
(
c
,
name
,
len
);
emit_atom_op
(
c
,
OP_EXPORT
,
name
,
len
);
}
static
void
defer_export
(
sv_compiler_t
*
c
,
const
char
*
name
,
uint32_t
len
)
{
if
(
c
->
deferred_export_count
>=
c
->
deferred_export_cap
)
{
int
cap
=
c
->
deferred_export_cap
?
c
->
deferred_export_cap
*
2
:
8
;
c
->
deferred_exports
=
realloc
(
c
->
deferred_exports
,
cap
*
sizeof
(
sv_deferred_export_t
));
c
->
deferred_export_cap
=
cap
;
}
c
->
deferred_exports
[
c
->
deferred_export_count
++
]
=
(
sv_deferred_export_t
){
.
name
=
name
,
.
len
=
len
};
}
static
void
defer_export_pattern
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
)
{
if
(
!
pat
)
return
;
switch
(
pat
->
type
)
{
case
N_IDENT
:
defer_export
(
c
,
pat
->
str
,
pat
->
len
);
break
;
case
N_ASSIGN_PAT
:
defer_export_pattern
(
c
,
pat
->
left
);
break
;
case
N_REST
:
case
N_SPREAD
:
defer_export_pattern
(
c
,
pat
->
right
);
break
;
case
N_ARRAY
:
case
N_ARRAY_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
defer_export_pattern
(
c
,
pat
->
args
.
items
[
i
]);
break
;
case
N_OBJECT
:
case
N_OBJECT_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
{
sv_ast_t
*
prop
=
pat
->
args
.
items
[
i
];
if
(
!
prop
)
continue
;
if
(
prop
->
type
==
N_PROPERTY
)
defer_export_pattern
(
c
,
prop
->
right
);
else
defer_export_pattern
(
c
,
prop
);
}
break
;
default
:
break
;
}
}
static
void
compile_export_pattern
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
)
{
if
(
!
pat
)
return
;
switch
(
pat
->
type
)
{
case
N_IDENT
:
compile_export_emit
(
c
,
pat
->
str
,
pat
->
len
);
break
;
case
N_ASSIGN_PAT
:
compile_export_pattern
(
c
,
pat
->
left
);
break
;
case
N_REST
:
case
N_SPREAD
:
compile_export_pattern
(
c
,
pat
->
right
);
break
;
case
N_ARRAY
:
case
N_ARRAY_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
compile_export_pattern
(
c
,
pat
->
args
.
items
[
i
]);
break
;
case
N_OBJECT
:
case
N_OBJECT_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
{
sv_ast_t
*
prop
=
pat
->
args
.
items
[
i
];
if
(
!
prop
)
continue
;
if
(
prop
->
type
==
N_PROPERTY
)
compile_export_pattern
(
c
,
prop
->
right
);
else
compile_export_pattern
(
c
,
prop
);
}
break
;
default
:
break
;
}
}
static
void
compile_export_decl
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
!
node
)
return
;
if
(
node
->
flags
&
EX_DEFAULT
)
{
if
(
node
->
left
)
compile_expr
(
c
,
node
->
left
);
else
emit_op
(
c
,
OP_UNDEF
);
emit_atom_op
(
c
,
OP_EXPORT
,
"default"
,
7
);
return
;
}
if
((
node
->
flags
&
EX_DECL
)
&&
node
->
left
)
{
sv_ast_t
*
decl
=
node
->
left
;
if
(
decl
->
type
==
N_VAR
)
{
compile_var_decl
(
c
,
decl
);
bool
is_var
=
decl
->
var_kind
==
VAR_VAR
;
for
(
int
i
=
0
;
i
<
decl
->
args
.
count
;
i
++
)
{
sv_ast_t
*
var
=
decl
->
args
.
items
[
i
];
if
(
!
var
||
var
->
type
!=
N_VARDECL
)
continue
;
if
(
is_var
&&
c
->
mode
==
SV_COMPILE_MODULE
)
defer_export_pattern
(
c
,
var
->
left
);
else
compile_export_pattern
(
c
,
var
->
left
);
}
return
;
}
compile_stmt
(
c
,
decl
);
if
((
decl
->
type
==
N_FUNC
||
decl
->
type
==
N_CLASS
)
&&
decl
->
str
&&
decl
->
len
>
0
)
{
compile_export_emit
(
c
,
decl
->
str
,
decl
->
len
);
}
return
;
}
if
((
node
->
flags
&
EX_STAR
)
&&
(
node
->
flags
&
EX_FROM
)
&&
node
->
right
)
{
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_IMPORT_SYNC
);
if
(
node
->
flags
&
EX_NAMESPACE
)
{
if
(
node
->
args
.
count
>
0
)
{
sv_ast_t
*
spec
=
node
->
args
.
items
[
0
];
if
(
spec
&&
spec
->
type
==
N_IMPORT_SPEC
&&
spec
->
right
&&
spec
->
right
->
type
==
N_IDENT
)
{
emit_atom_op
(
c
,
OP_EXPORT
,
spec
->
right
->
str
,
spec
->
right
->
len
);
return
;
}
}
emit_op
(
c
,
OP_POP
);
return
;
}
emit_op
(
c
,
OP_EXPORT_ALL
);
return
;
}
if
(
!
(
node
->
flags
&
EX_NAMED
)
||
node
->
args
.
count
==
0
)
return
;
int
ns_local
=
-1
;
if
(
node
->
flags
&
EX_FROM
)
{
if
(
!
node
->
right
)
return
;
compile_expr
(
c
,
node
->
right
);
emit_op
(
c
,
OP_IMPORT_SYNC
);
ns_local
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
ns_local
);
}
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
spec
=
node
->
args
.
items
[
i
];
if
(
!
spec
||
spec
->
type
!=
N_IMPORT_SPEC
||
!
spec
->
left
||
!
spec
->
right
||
spec
->
left
->
type
!=
N_IDENT
||
spec
->
right
->
type
!=
N_IDENT
)
continue
;
if
(
node
->
flags
&
EX_FROM
)
{
emit_get_local
(
c
,
ns_local
);
if
(
spec
->
left
->
len
==
7
&&
memcmp
(
spec
->
left
->
str
,
"default"
,
7
)
==
0
)
emit_op
(
c
,
OP_IMPORT_DEFAULT
);
else
emit_atom_op
(
c
,
OP_GET_FIELD
,
spec
->
left
->
str
,
spec
->
left
->
len
);
emit_atom_op
(
c
,
OP_EXPORT
,
spec
->
right
->
str
,
spec
->
right
->
len
);
}
else
{
emit_get_var
(
c
,
spec
->
left
->
str
,
spec
->
left
->
len
);
emit_atom_op
(
c
,
OP_EXPORT
,
spec
->
right
->
str
,
spec
->
right
->
len
);
}
}
}
static
void
compile_var_decl
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
sv_var_kind_t
kind
=
node
->
var_kind
;
bool
is_const
=
(
kind
==
VAR_CONST
);
bool
repl_top
=
is_repl_top_level
(
c
);
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
decl
=
node
->
args
.
items
[
i
];
if
(
decl
->
type
!=
N_VARDECL
)
continue
;
sv_ast_t
*
target
=
decl
->
left
;
if
(
repl_top
)
{
if
(
!
decl
->
right
&&
kind
==
VAR_VAR
)
continue
;
if
(
decl
->
right
)
{
compile_expr
(
c
,
decl
->
right
);
}
else
{
emit_op
(
c
,
OP_UNDEF
);
}
if
(
target
->
type
==
N_IDENT
)
{
emit_atom_op
(
c
,
OP_PUT_GLOBAL
,
target
->
str
,
target
->
len
);
}
else
{
compile_destructure_pattern
(
c
,
target
,
false
,
true
,
DESTRUCTURE_ASSIGN
,
kind
);
}
}
else
if
(
kind
==
VAR_VAR
)
{
if
(
decl
->
right
)
{
compile_expr
(
c
,
decl
->
right
);
compile_lhs_set
(
c
,
target
,
false
);
}
}
else
{
if
(
target
->
type
==
N_IDENT
)
{
int
idx
=
ensure_local_at_depth
(
c
,
target
->
str
,
target
->
len
,
is_const
,
c
->
scope_depth
);
if
(
decl
->
right
)
{
compile_expr
(
c
,
decl
->
right
);
}
else
if
(
!
is_const
)
{
emit_op
(
c
,
OP_UNDEF
);
}
if
(
decl
->
right
||
!
is_const
)
{
emit_put_local
(
c
,
idx
);
c
->
locals
[
idx
].
is_tdz
=
false
;
}
}
else
{
if
(
decl
->
right
)
{
compile_expr
(
c
,
decl
->
right
);
compile_destructure_binding
(
c
,
target
,
kind
);
emit_op
(
c
,
OP_POP
);
}
}
}
}
}
static
void
compile_destructure_binding
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
sv_var_kind_t
kind
)
{
compile_destructure_pattern
(
c
,
pat
,
false
,
false
,
DESTRUCTURE_BIND
,
kind
);
}
static
void
compile_if
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
compile_expr
(
c
,
node
->
cond
);
int
else_jump
=
emit_jump
(
c
,
OP_JMP_FALSE
);
compile_stmt
(
c
,
node
->
left
);
if
(
node
->
right
)
{
int
end_jump
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
else_jump
);
compile_stmt
(
c
,
node
->
right
);
patch_jump
(
c
,
end_jump
);
}
else
{
patch_jump
(
c
,
else_jump
);
}
}
static
void
compile_while
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
int
loop_start
=
c
->
code_len
;
push_loop
(
c
,
loop_start
,
NULL
,
0
);
compile_expr
(
c
,
node
->
cond
);
int
exit_jump
=
emit_jump
(
c
,
OP_JMP_FALSE
);
compile_stmt
(
c
,
node
->
body
);
sv_loop_t
*
loop
=
&
c
->
loops
[
c
->
loop_count
-
1
];
for
(
int
i
=
0
;
i
<
loop
->
continues
.
count
;
i
++
)
patch_jump
(
c
,
loop
->
continues
.
offsets
[
i
]);
emit_loop
(
c
,
loop_start
);
patch_jump
(
c
,
exit_jump
);
pop_loop
(
c
);
}
static
void
compile_do_while
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
int
loop_start
=
c
->
code_len
;
push_loop
(
c
,
loop_start
,
NULL
,
0
);
compile_stmt
(
c
,
node
->
body
);
sv_loop_t
*
loop
=
&
c
->
loops
[
c
->
loop_count
-
1
];
int
cond_start
=
c
->
code_len
;
for
(
int
i
=
0
;
i
<
loop
->
continues
.
count
;
i
++
)
patch_jump
(
c
,
loop
->
continues
.
offsets
[
i
]);
compile_expr
(
c
,
node
->
cond
);
int
exit_jump
=
emit_jump
(
c
,
OP_JMP_FALSE
);
emit_loop
(
c
,
loop_start
);
patch_jump
(
c
,
exit_jump
);
pop_loop
(
c
);
(
void
)
cond_start
;
}
static
void
for_add_slot_unique
(
int
**
slots
,
int
*
count
,
int
*
cap
,
int
slot
)
{
if
(
slot
<
0
)
return
;
for
(
int
i
=
0
;
i
<
*
count
;
i
++
)
{
if
((
*
slots
)[
i
]
==
slot
)
return
;
}
if
(
*
count
>=
*
cap
)
{
int
new_cap
=
(
*
cap
>
0
)
?
(
*
cap
*
2
)
:
8
;
int
*
new_slots
=
realloc
(
*
slots
,
(
size_t
)
new_cap
*
sizeof
(
int
));
if
(
!
new_slots
)
return
;
*
slots
=
new_slots
;
*
cap
=
new_cap
;
}
(
*
slots
)[(
*
count
)
++
]
=
slot
;
}
static
void
for_collect_pattern_slots
(
sv_compiler_t
*
c
,
sv_ast_t
*
pat
,
int
**
slots
,
int
*
count
,
int
*
cap
)
{
if
(
!
pat
)
return
;
switch
(
pat
->
type
)
{
case
N_IDENT
:
{
int
slot
=
resolve_local
(
c
,
pat
->
str
,
pat
->
len
);
for_add_slot_unique
(
slots
,
count
,
cap
,
slot
);
break
;
}
case
N_ASSIGN_PAT
:
for_collect_pattern_slots
(
c
,
pat
->
left
,
slots
,
count
,
cap
);
break
;
case
N_REST
:
case
N_SPREAD
:
for_collect_pattern_slots
(
c
,
pat
->
right
,
slots
,
count
,
cap
);
break
;
case
N_ARRAY
:
case
N_ARRAY_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
for_collect_pattern_slots
(
c
,
pat
->
args
.
items
[
i
],
slots
,
count
,
cap
);
break
;
case
N_OBJECT
:
case
N_OBJECT_PAT
:
for
(
int
i
=
0
;
i
<
pat
->
args
.
count
;
i
++
)
{
sv_ast_t
*
prop
=
pat
->
args
.
items
[
i
];
if
(
!
prop
)
continue
;
if
(
prop
->
type
==
N_PROPERTY
)
for_collect_pattern_slots
(
c
,
prop
->
right
,
slots
,
count
,
cap
);
else
for_collect_pattern_slots
(
c
,
prop
,
slots
,
count
,
cap
);
}
break
;
default
:
break
;
}
}
static
void
for_collect_var_decl_slots
(
sv_compiler_t
*
c
,
sv_ast_t
*
init_var
,
int
**
slots
,
int
*
count
,
int
*
cap
)
{
if
(
!
init_var
||
init_var
->
type
!=
N_VAR
)
return
;
for
(
int
i
=
0
;
i
<
init_var
->
args
.
count
;
i
++
)
{
sv_ast_t
*
decl
=
init_var
->
args
.
items
[
i
];
if
(
!
decl
||
decl
->
type
!=
N_VARDECL
||
!
decl
->
left
)
continue
;
for_collect_pattern_slots
(
c
,
decl
->
left
,
slots
,
count
,
cap
);
}
}
static
void
compile_for
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
begin_scope
(
c
);
int
*
iter_slots
=
NULL
;
int
iter_count
=
0
;
int
iter_cap
=
0
;
if
(
node
->
init
)
{
if
(
node
->
init
->
type
==
N_VAR
)
{
sv_var_kind_t
kind
=
node
->
init
->
var_kind
;
compile_var_decl
(
c
,
node
->
init
);
if
(
kind
==
VAR_LET
||
kind
==
VAR_CONST
)
for_collect_var_decl_slots
(
c
,
node
->
init
,
&
iter_slots
,
&
iter_count
,
&
iter_cap
);
}
else
{
compile_expr
(
c
,
node
->
init
);
emit_op
(
c
,
OP_POP
);
}
}
int
loop_start
=
c
->
code_len
;
push_loop
(
c
,
loop_start
,
NULL
,
0
);
int
exit_jump
=
-1
;
if
(
node
->
cond
)
{
compile_expr
(
c
,
node
->
cond
);
exit_jump
=
emit_jump
(
c
,
OP_JMP_FALSE
);
}
int
iter_inner_start
=
-1
;
if
(
iter_count
>
0
)
{
begin_scope
(
c
);
iter_inner_start
=
c
->
local_count
;
for
(
int
i
=
0
;
i
<
iter_count
;
i
++
)
{
int
outer_idx
=
iter_slots
[
i
];
sv_local_t
outer
=
c
->
locals
[
outer_idx
];
emit_get_local
(
c
,
outer_idx
);
int
inner_idx
=
add_local
(
c
,
outer
.
name
,
outer
.
name_len
,
outer
.
is_const
,
c
->
scope_depth
);
emit_put_local
(
c
,
inner_idx
);
}
}
compile_stmt
(
c
,
node
->
body
);
sv_loop_t
*
loop
=
&
c
->
loops
[
c
->
loop_count
-
1
];
for
(
int
i
=
0
;
i
<
loop
->
continues
.
count
;
i
++
)
patch_jump
(
c
,
loop
->
continues
.
offsets
[
i
]);
int
break_close_slot
=
-1
;
if
(
iter_count
>
0
)
{
for
(
int
i
=
0
;
i
<
iter_count
;
i
++
)
{
int
inner_idx
=
iter_inner_start
+
i
;
if
(
!
c
->
locals
[
inner_idx
].
captured
)
continue
;
int
slot
=
local_to_frame_slot
(
c
,
inner_idx
);
if
(
break_close_slot
<
0
||
slot
<
break_close_slot
)
break_close_slot
=
slot
;
}
for
(
int
i
=
0
;
i
<
iter_count
;
i
++
)
{
emit_get_local
(
c
,
iter_inner_start
+
i
);
emit_put_local
(
c
,
iter_slots
[
i
]);
}
end_scope
(
c
);
}
if
(
node
->
update
)
{
sv_ast_t
*
upd
=
node
->
update
;
int
slot
;
if
(
upd
->
type
==
N_UPDATE
&&
upd
->
right
&&
upd
->
right
->
type
==
N_IDENT
&&
(
slot
=
resolve_local_slot
(
c
,
upd
->
right
->
str
,
upd
->
right
->
len
))
>=
0
)
{
emit_op
(
c
,
upd
->
op
==
TOK_POSTINC
?
OP_INC_LOCAL
:
OP_DEC_LOCAL
);
emit
(
c
,
(
uint8_t
)
slot
);
}
else
{
compile_expr
(
c
,
upd
);
emit_op
(
c
,
OP_POP
);
}
}
emit_loop
(
c
,
loop_start
);
if
(
exit_jump
>=
0
)
patch_jump
(
c
,
exit_jump
);
int
skip_break_cleanup
=
-1
;
if
(
break_close_slot
>=
0
)
skip_break_cleanup
=
emit_jump
(
c
,
OP_JMP
);
pop_loop
(
c
);
if
(
break_close_slot
>=
0
)
{
emit_op
(
c
,
OP_CLOSE_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
break_close_slot
);
patch_jump
(
c
,
skip_break_cleanup
);
}
free
(
iter_slots
);
end_scope
(
c
);
}
static
void
compile_for_each
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
,
bool
is_for_of
);
static
void
compile_for_in
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
compile_for_each
(
c
,
node
,
false
);
}
static
void
compile_for_of
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
compile_for_each
(
c
,
node
,
true
);
}
static
void
compile_for_each_assign_target
(
sv_compiler_t
*
c
,
sv_ast_t
*
lhs
)
{
if
(
!
lhs
)
return
;
if
(
lhs
->
type
==
N_VAR
&&
lhs
->
args
.
count
>
0
)
{
sv_ast_t
*
decl
=
lhs
->
args
.
items
[
0
];
sv_ast_t
*
target
=
decl
->
left
?
decl
->
left
:
decl
;
if
(
target
->
type
==
N_IDENT
)
{
int
loc
=
resolve_local
(
c
,
target
->
str
,
target
->
len
);
if
(
loc
==
-1
)
{
bool
is_const
=
(
lhs
->
var_kind
==
VAR_CONST
);
loc
=
add_local
(
c
,
target
->
str
,
target
->
len
,
is_const
,
c
->
scope_depth
);
}
emit_put_local
(
c
,
loc
);
}
else
{
if
(
lhs
->
var_kind
==
VAR_VAR
)
{
compile_lhs_set
(
c
,
target
,
false
);
}
else
{
compile_destructure_binding
(
c
,
target
,
lhs
->
var_kind
);
emit_op
(
c
,
OP_POP
);
}
}
return
;
}
if
(
lhs
->
type
==
N_IDENT
)
{
emit_set_var
(
c
,
lhs
->
str
,
lhs
->
len
,
false
);
return
;
}
compile_lhs_set
(
c
,
lhs
,
false
);
}
static
void
compile_for_each
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
,
bool
is_for_of
)
{
begin_scope
(
c
);
int
*
iter_slots
=
NULL
;
int
iter_count
=
0
;
int
iter_cap
=
0
;
if
(
node
->
left
&&
node
->
left
->
type
==
N_VAR
&&
node
->
left
->
var_kind
!=
VAR_VAR
)
{
bool
is_const
=
(
node
->
left
->
var_kind
==
VAR_CONST
);
int
lb
=
c
->
local_count
;
for
(
int
i
=
0
;
i
<
node
->
left
->
args
.
count
;
i
++
)
{
sv_ast_t
*
decl
=
node
->
left
->
args
.
items
[
i
];
if
(
!
decl
||
decl
->
type
!=
N_VARDECL
||
!
decl
->
left
)
continue
;
hoist_lexical_pattern
(
c
,
decl
->
left
,
is_const
);
}
for
(
int
i
=
lb
;
i
<
c
->
local_count
;
i
++
)
{
c
->
locals
[
i
].
is_tdz
=
true
;
int
slot
=
i
-
c
->
param_locals
;
emit_op
(
c
,
OP_SET_LOCAL_UNDEF
);
emit_u16
(
c
,
(
uint16_t
)
slot
);
}
for_collect_var_decl_slots
(
c
,
node
->
left
,
&
iter_slots
,
&
iter_count
,
&
iter_cap
);
}
if
(
!
is_for_of
&&
node
->
left
&&
node
->
left
->
type
==
N_VAR
&&
node
->
left
->
var_kind
==
VAR_VAR
&&
node
->
left
->
args
.
count
>
0
)
{
sv_ast_t
*
decl
=
node
->
left
->
args
.
items
[
0
];
if
(
decl
&&
decl
->
right
)
{
compile_expr
(
c
,
decl
->
right
);
sv_ast_t
*
target
=
decl
->
left
?
decl
->
left
:
decl
;
if
(
target
->
type
==
N_IDENT
)
{
int
loc
=
resolve_local
(
c
,
target
->
str
,
target
->
len
);
if
(
loc
==
-1
)
loc
=
add_local
(
c
,
target
->
str
,
target
->
len
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
loc
);
}
else
{
compile_lhs_set
(
c
,
target
,
false
);
}
}
}
int
iter_local
=
-1
;
int
idx_local
=
-1
;
int
exit_jump
=
-1
;
int
try_jump_for_of
=
-1
;
int
iter_err_local
=
-1
;
int
break_close_slot
=
-1
;
int
iter_inner_start
=
-1
;
bool
is_for_await
=
(
node
->
type
==
N_FOR_AWAIT_OF
);
compile_expr
(
c
,
node
->
right
);
if
(
is_for_of
)
{
emit_op
(
c
,
is_for_await
?
OP_FOR_AWAIT_OF
:
OP_FOR_OF
);
iter_err_local
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
try_jump_for_of
=
emit_jump
(
c
,
OP_TRY_PUSH
);
c
->
try_depth
++
;
}
else
{
emit_op
(
c
,
OP_FOR_IN
);
iter_local
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
iter_local
);
emit_number
(
c
,
0
);
idx_local
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
idx_local
);
}
int
loop_start
=
c
->
code_len
;
push_loop
(
c
,
loop_start
,
NULL
,
0
);
if
(
is_for_of
)
{
emit_op
(
c
,
is_for_await
?
OP_AWAIT_ITER_NEXT
:
OP_ITER_NEXT
);
exit_jump
=
emit_jump
(
c
,
OP_JMP_TRUE
);
}
else
{
emit_get_local
(
c
,
idx_local
);
emit_get_local
(
c
,
iter_local
);
emit_op
(
c
,
OP_GET_LENGTH
);
emit_op
(
c
,
OP_LT
);
exit_jump
=
emit_jump
(
c
,
OP_JMP_FALSE
);
emit_get_local
(
c
,
iter_local
);
emit_get_local
(
c
,
idx_local
);
emit_op
(
c
,
OP_GET_ELEM
);
}
compile_for_each_assign_target
(
c
,
node
->
left
);
if
(
iter_count
>
0
)
{
begin_scope
(
c
);
iter_inner_start
=
c
->
local_count
;
for
(
int
i
=
0
;
i
<
iter_count
;
i
++
)
{
int
outer_idx
=
iter_slots
[
i
];
sv_local_t
outer
=
c
->
locals
[
outer_idx
];
emit_get_local
(
c
,
outer_idx
);
int
inner_idx
=
add_local
(
c
,
outer
.
name
,
outer
.
name_len
,
outer
.
is_const
,
c
->
scope_depth
);
emit_put_local
(
c
,
inner_idx
);
}
}
compile_stmt
(
c
,
node
->
body
);
sv_loop_t
*
loop
=
&
c
->
loops
[
c
->
loop_count
-
1
];
for
(
int
i
=
0
;
i
<
loop
->
continues
.
count
;
i
++
)
patch_jump
(
c
,
loop
->
continues
.
offsets
[
i
]);
if
(
iter_count
>
0
)
{
for
(
int
i
=
0
;
i
<
iter_count
;
i
++
)
{
int
inner_idx
=
iter_inner_start
+
i
;
if
(
!
c
->
locals
[
inner_idx
].
captured
)
continue
;
int
slot
=
local_to_frame_slot
(
c
,
inner_idx
);
if
(
break_close_slot
<
0
||
slot
<
break_close_slot
)
break_close_slot
=
slot
;
}
end_scope
(
c
);
}
if
(
!
is_for_of
)
{
emit_get_local
(
c
,
idx_local
);
emit_op
(
c
,
OP_INC
);
emit_put_local
(
c
,
idx_local
);
}
emit_loop
(
c
,
loop_start
);
patch_jump
(
c
,
exit_jump
);
int
skip_break_cleanup
=
-1
;
if
(
break_close_slot
>=
0
)
skip_break_cleanup
=
emit_jump
(
c
,
OP_JMP
);
if
(
is_for_of
)
{
emit_op
(
c
,
OP_POP
);
emit_op
(
c
,
OP_TRY_POP
);
pop_loop
(
c
);
if
(
break_close_slot
>=
0
)
{
emit_op
(
c
,
OP_CLOSE_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
break_close_slot
);
patch_jump
(
c
,
skip_break_cleanup
);
}
emit_op
(
c
,
OP_TRY_POP
);
emit_op
(
c
,
OP_ITER_CLOSE
);
int
end_jump
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
try_jump_for_of
);
int
catch_tag
=
emit_jump
(
c
,
OP_CATCH
);
emit_put_local
(
c
,
iter_err_local
);
emit_op
(
c
,
OP_ITER_CLOSE
);
emit_get_local
(
c
,
iter_err_local
);
emit_op
(
c
,
OP_THROW
);
patch_jump
(
c
,
catch_tag
);
patch_jump
(
c
,
end_jump
);
c
->
try_depth
--
;
}
else
{
pop_loop
(
c
);
if
(
break_close_slot
>=
0
)
{
emit_op
(
c
,
OP_CLOSE_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
break_close_slot
);
patch_jump
(
c
,
skip_break_cleanup
);
}
}
free
(
iter_slots
);
end_scope
(
c
);
}
static
void
compile_break
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
c
->
loop_count
==
0
)
return
;
int
target
=
c
->
loop_count
-
1
;
if
(
node
->
str
)
{
for
(
int
i
=
c
->
loop_count
-
1
;
i
>=
0
;
i
--
)
{
if
(
c
->
loops
[
i
].
label
&&
c
->
loops
[
i
].
label_len
==
node
->
len
&&
memcmp
(
c
->
loops
[
i
].
label
,
node
->
str
,
node
->
len
)
==
0
)
{
target
=
i
;
break
;
}
}
}
int
offset
=
emit_jump
(
c
,
OP_JMP
);
patch_list_add
(
&
c
->
loops
[
target
].
breaks
,
offset
);
}
static
void
compile_continue
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
c
->
loop_count
==
0
)
return
;
int
target
=
c
->
loop_count
-
1
;
if
(
node
->
str
)
{
for
(
int
i
=
c
->
loop_count
-
1
;
i
>=
0
;
i
--
)
{
if
(
c
->
loops
[
i
].
label
&&
c
->
loops
[
i
].
label_len
==
node
->
len
&&
memcmp
(
c
->
loops
[
i
].
label
,
node
->
str
,
node
->
len
)
==
0
)
{
target
=
i
;
break
;
}
}
}
int
offset
=
emit_jump
(
c
,
OP_JMP
);
patch_list_add
(
&
c
->
loops
[
target
].
continues
,
offset
);
}
static
void
compile_finally_block
(
sv_compiler_t
*
c
,
sv_ast_t
*
finally_body
)
{
int
finally_jump
=
emit_jump
(
c
,
OP_FINALLY
);
compile_stmt
(
c
,
finally_body
);
emit_op
(
c
,
OP_FINALLY_RET
);
patch_jump
(
c
,
finally_jump
);
}
static
void
compile_catch_body
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
begin_scope
(
c
);
if
(
node
->
catch_param
&&
node
->
catch_param
->
type
==
N_IDENT
)
{
int
loc
=
add_local
(
c
,
node
->
catch_param
->
str
,
node
->
catch_param
->
len
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
loc
);
}
else
if
(
node
->
catch_param
&&
is_destructure_pattern_node
(
node
->
catch_param
))
{
compile_destructure_binding
(
c
,
node
->
catch_param
,
VAR_LET
);
emit_op
(
c
,
OP_POP
);
}
else
{
emit_op
(
c
,
OP_POP
);
}
compile_stmt
(
c
,
node
->
catch_body
);
end_scope
(
c
);
}
static
void
compile_try
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
c
->
try_depth
++
;
int
try_jump
=
emit_jump
(
c
,
OP_TRY_PUSH
);
compile_stmt
(
c
,
node
->
body
);
emit_op
(
c
,
OP_TRY_POP
);
bool
has_catch
=
(
node
->
catch_body
!=
NULL
);
bool
has_finally
=
(
node
->
finally_body
!=
NULL
);
if
(
!
has_finally
)
{
int
end_jump
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
try_jump
);
int
catch_tag
=
emit_jump
(
c
,
OP_CATCH
);
if
(
has_catch
)
compile_catch_body
(
c
,
node
);
else
emit_op
(
c
,
OP_POP
);
patch_jump
(
c
,
catch_tag
);
patch_jump
(
c
,
end_jump
);
c
->
try_depth
--
;
return
;
}
int
to_finally_from_try
=
emit_jump
(
c
,
OP_JMP
);
int
to_finally_from_catch
=
-1
;
int
to_finally_from_throw
=
-1
;
patch_jump
(
c
,
try_jump
);
if
(
has_catch
)
{
int
catch_tag
=
emit_jump
(
c
,
OP_CATCH
);
int
catch_throw_jump
=
emit_jump
(
c
,
OP_TRY_PUSH
);
compile_catch_body
(
c
,
node
);
emit_op
(
c
,
OP_TRY_POP
);
to_finally_from_catch
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
catch_throw_jump
);
emit_op
(
c
,
OP_POP
);
to_finally_from_throw
=
emit_jump
(
c
,
OP_JMP
);
patch_jump
(
c
,
catch_tag
);
}
else
{
emit_op
(
c
,
OP_POP
);
to_finally_from_throw
=
emit_jump
(
c
,
OP_JMP
);
}
patch_jump
(
c
,
to_finally_from_try
);
if
(
to_finally_from_catch
>=
0
)
patch_jump
(
c
,
to_finally_from_catch
);
if
(
to_finally_from_throw
>=
0
)
patch_jump
(
c
,
to_finally_from_throw
);
compile_finally_block
(
c
,
node
->
finally_body
);
c
->
try_depth
--
;
}
static
void
compile_switch
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
int
case_count
=
node
->
args
.
count
;
int
default_case
=
-1
;
int
*
match_to_stub
=
NULL
;
int
*
stub_to_body
=
NULL
;
if
(
case_count
>
0
)
{
match_to_stub
=
calloc
((
size_t
)
case_count
,
sizeof
(
int
));
stub_to_body
=
calloc
((
size_t
)
case_count
,
sizeof
(
int
));
for
(
int
i
=
0
;
i
<
case_count
;
i
++
)
{
match_to_stub
[
i
]
=
-1
;
stub_to_body
[
i
]
=
-1
;
}
}
compile_expr
(
c
,
node
->
cond
);
begin_scope
(
c
);
push_loop
(
c
,
c
->
code_len
,
NULL
,
0
);
for
(
int
i
=
0
;
i
<
case_count
;
i
++
)
{
sv_ast_t
*
cas
=
node
->
args
.
items
[
i
];
if
(
!
cas
->
left
)
{
default_case
=
i
;
continue
;
}
emit_op
(
c
,
OP_DUP
);
compile_expr
(
c
,
cas
->
left
);
emit_op
(
c
,
OP_SEQ
);
match_to_stub
[
i
]
=
emit_jump
(
c
,
OP_JMP_TRUE
);
}
int
miss_jump
=
emit_jump
(
c
,
OP_JMP
);
int
default_to_body
=
-1
;
int
miss_to_end
=
-1
;
for
(
int
i
=
0
;
i
<
case_count
;
i
++
)
{
if
(
match_to_stub
[
i
]
<
0
)
continue
;
patch_jump
(
c
,
match_to_stub
[
i
]);
emit_op
(
c
,
OP_POP
);
stub_to_body
[
i
]
=
emit_jump
(
c
,
OP_JMP
);
}
patch_jump
(
c
,
miss_jump
);
emit_op
(
c
,
OP_POP
);
if
(
default_case
>=
0
)
default_to_body
=
emit_jump
(
c
,
OP_JMP
);
else
miss_to_end
=
emit_jump
(
c
,
OP_JMP
);
for
(
int
i
=
0
;
i
<
case_count
;
i
++
)
{
if
(
i
==
default_case
&&
default_to_body
>=
0
)
patch_jump
(
c
,
default_to_body
);
if
(
stub_to_body
[
i
]
>=
0
)
patch_jump
(
c
,
stub_to_body
[
i
]);
sv_ast_t
*
cas
=
node
->
args
.
items
[
i
];
compile_stmts
(
c
,
&
cas
->
args
);
}
if
(
miss_to_end
>=
0
)
patch_jump
(
c
,
miss_to_end
);
free
(
match_to_stub
);
free
(
stub_to_body
);
pop_loop
(
c
);
end_scope
(
c
);
}
static
inline
bool
is_loop_node
(
sv_ast_t
*
n
)
{
return
n
&&
(
n
->
type
==
N_WHILE
||
n
->
type
==
N_DO_WHILE
||
n
->
type
==
N_FOR
||
n
->
type
==
N_FOR_IN
||
n
->
type
==
N_FOR_OF
||
n
->
type
==
N_FOR_AWAIT_OF
);
}
static
void
compile_label
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
if
(
is_loop_node
(
node
->
body
))
{
c
->
pending_label
=
node
->
str
;
c
->
pending_label_len
=
node
->
len
;
compile_stmt
(
c
,
node
->
body
);
c
->
pending_label
=
NULL
;
c
->
pending_label_len
=
0
;
}
else
{
push_loop
(
c
,
c
->
code_len
,
node
->
str
,
node
->
len
);
compile_stmt
(
c
,
node
->
body
);
pop_loop
(
c
);
}
}
static
void
emit_field_inits
(
sv_compiler_t
*
c
,
sv_ast_t
**
fields
,
int
count
)
{
sv_compiler_t
*
enc
=
c
->
enclosing
;
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
sv_ast_t
*
m
=
fields
[
i
];
emit_op
(
c
,
OP_THIS
);
if
(
m
->
right
)
compile_expr
(
c
,
m
->
right
);
else
emit_op
(
c
,
OP_UNDEF
);
if
(
m
->
flags
&
FN_COMPUTED
)
{
int
key_local
=
enc
->
computed_key_locals
[
i
];
enc
->
locals
[
key_local
].
captured
=
true
;
uint16_t
slot
=
(
uint16_t
)
local_to_frame_slot
(
enc
,
key_local
);
int
uv
=
add_upvalue
(
c
,
slot
,
true
,
false
);
emit_op
(
c
,
OP_GET_UPVAL
);
emit_u16
(
c
,
(
uint16_t
)
uv
);
}
else
{
compile_static_property_key
(
c
,
m
->
left
);
}
emit_op
(
c
,
OP_SWAP
);
emit_op
(
c
,
OP_DEFINE_METHOD_COMP
);
emit
(
c
,
0
);
emit_op
(
c
,
OP_POP
);
}
}
static
sv_ast_t
*
find_class_constructor
(
sv_ast_t
*
node
)
{
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
m
=
node
->
args
.
items
[
i
];
if
(
m
->
type
==
N_METHOD
&&
m
->
left
&&
m
->
left
->
type
==
N_IDENT
&&
m
->
left
->
len
==
11
&&
memcmp
(
m
->
left
->
str
,
"constructor"
,
11
)
==
0
)
{
return
m
;
}
}
return
NULL
;
}
static
void
compile_class_method
(
sv_compiler_t
*
c
,
sv_ast_t
*
m
,
int
ctor_local
,
int
proto_local
,
int
preeval_key
)
{
bool
is_static
=
!!
(
m
->
flags
&
FN_STATIC
);
int
home_local
=
is_static
?
ctor_local
:
proto_local
;
bool
is_fn
=
(
m
->
right
&&
m
->
right
->
type
==
N_FUNC
);
if
(
is_fn
)
{
compile_func_expr
(
c
,
m
->
right
);
emit_get_local
(
c
,
home_local
);
emit_op
(
c
,
OP_SET_HOME_OBJ
);
emit_op
(
c
,
OP_SWAP
);
}
else
{
emit_get_local
(
c
,
home_local
);
if
(
m
->
right
)
compile_expr
(
c
,
m
->
right
);
else
emit_op
(
c
,
OP_UNDEF
);
}
uint8_t
method_flags
=
0
;
if
(
m
->
flags
&
FN_GETTER
)
method_flags
|=
1
;
if
(
m
->
flags
&
FN_SETTER
)
method_flags
|=
2
;
if
(
m
->
flags
&
FN_COMPUTED
)
{
if
(
preeval_key
>=
0
)
emit_get_local
(
c
,
preeval_key
);
else
compile_expr
(
c
,
m
->
left
);
}
else
compile_static_property_key
(
c
,
m
->
left
);
emit_op
(
c
,
OP_SWAP
);
emit_op
(
c
,
OP_DEFINE_METHOD_COMP
);
emit
(
c
,
method_flags
);
emit_op
(
c
,
OP_POP
);
}
static
void
compile_class
(
sv_compiler_t
*
c
,
sv_ast_t
*
node
)
{
int
outer_name_local
=
-1
;
bool
class_repl_top
=
is_repl_top_level
(
c
);
if
(
node
->
str
)
outer_name_local
=
resolve_local
(
c
,
node
->
str
,
node
->
len
);
if
(
node
->
left
)
compile_expr
(
c
,
node
->
left
);
else
emit_op
(
c
,
OP_UNDEF
);
sv_ast_t
*
ctor_method
=
find_class_constructor
(
node
);
int
field_count
=
0
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
m
=
node
->
args
.
items
[
i
];
if
(
m
->
type
!=
N_METHOD
)
continue
;
if
(
m
==
ctor_method
)
continue
;
if
(
m
->
flags
&
FN_STATIC
)
continue
;
bool
is_fn
=
(
m
->
right
&&
m
->
right
->
type
==
N_FUNC
);
if
(
!
is_fn
)
field_count
++
;
}
sv_ast_t
**
field_inits
=
NULL
;
int
*
computed_key_locals
=
NULL
;
if
(
field_count
>
0
)
{
field_inits
=
malloc
(
sizeof
(
sv_ast_t
*
)
*
field_count
);
computed_key_locals
=
malloc
(
sizeof
(
int
)
*
field_count
);
int
fi
=
0
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
m
=
node
->
args
.
items
[
i
];
if
(
m
->
type
!=
N_METHOD
)
continue
;
if
(
m
==
ctor_method
)
continue
;
if
(
m
->
flags
&
FN_STATIC
)
continue
;
bool
is_fn
=
(
m
->
right
&&
m
->
right
->
type
==
N_FUNC
);
if
(
!
is_fn
)
{
field_inits
[
fi
]
=
m
;
if
(
m
->
flags
&
FN_COMPUTED
)
{
compile_expr
(
c
,
m
->
left
);
int
loc
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
loc
);
computed_key_locals
[
fi
]
=
loc
;
}
else
{
computed_key_locals
[
fi
]
=
-1
;
}
fi
++
;
}
}
}
int
*
method_comp_keys
=
NULL
;
if
(
node
->
str
)
{
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
m
=
node
->
args
.
items
[
i
];
if
(
m
->
type
!=
N_METHOD
||
!
(
m
->
flags
&
FN_COMPUTED
))
continue
;
if
(
m
==
ctor_method
)
continue
;
bool
is_fn
=
(
m
->
right
&&
m
->
right
->
type
==
N_FUNC
);
if
(
!
is_fn
&&
!
(
m
->
flags
&
FN_STATIC
))
continue
;
if
(
!
method_comp_keys
)
{
method_comp_keys
=
malloc
(
sizeof
(
int
)
*
node
->
args
.
count
);
for
(
int
j
=
0
;
j
<
node
->
args
.
count
;
j
++
)
method_comp_keys
[
j
]
=
-1
;
}
compile_expr
(
c
,
m
->
left
);
int
loc
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
loc
);
method_comp_keys
[
i
]
=
loc
;
}
}
int
inner_name_local
=
-1
;
if
(
node
->
str
)
{
begin_scope
(
c
);
inner_name_local
=
add_local
(
c
,
node
->
str
,
node
->
len
,
true
,
c
->
scope_depth
);
}
if
(
ctor_method
&&
ctor_method
->
right
)
{
c
->
field_inits
=
field_inits
;
c
->
field_init_count
=
field_count
;
c
->
computed_key_locals
=
computed_key_locals
;
compile_func_expr
(
c
,
ctor_method
->
right
);
c
->
field_inits
=
NULL
;
c
->
field_init_count
=
0
;
c
->
computed_key_locals
=
NULL
;
}
else
if
(
field_count
>
0
)
{
c
->
computed_key_locals
=
computed_key_locals
;
sv_compiler_t
comp
=
{
0
};
comp
.
js
=
c
->
js
;
comp
.
source
=
c
->
source
;
comp
.
source_len
=
c
->
source_len
;
comp
.
enclosing
=
c
;
comp
.
scope_depth
=
0
;
comp
.
is_strict
=
c
->
is_strict
;
comp
.
param_count
=
0
;
emit_field_inits
(
&
comp
,
field_inits
,
field_count
);
emit_op
(
&
comp
,
OP_RETURN_UNDEF
);
sv_func_t
*
fn
=
code_arena_bump
(
sizeof
(
sv_func_t
));
memset
(
fn
,
0
,
sizeof
(
sv_func_t
));
fn
->
code
=
code_arena_bump
((
size_t
)
comp
.
code_len
);
memcpy
(
fn
->
code
,
comp
.
code
,
(
size_t
)
comp
.
code_len
);
fn
->
code_len
=
comp
.
code_len
;
if
(
comp
.
const_count
>
0
)
{
fn
->
constants
=
code_arena_bump
((
size_t
)
comp
.
const_count
*
sizeof
(
jsval_t
));
memcpy
(
fn
->
constants
,
comp
.
constants
,
(
size_t
)
comp
.
const_count
*
sizeof
(
jsval_t
));
fn
->
const_count
=
comp
.
const_count
;
}
if
(
comp
.
atom_count
>
0
)
{
fn
->
atoms
=
code_arena_bump
((
size_t
)
comp
.
atom_count
*
sizeof
(
sv_atom_t
));
memcpy
(
fn
->
atoms
,
comp
.
atoms
,
(
size_t
)
comp
.
atom_count
*
sizeof
(
sv_atom_t
));
fn
->
atom_count
=
comp
.
atom_count
;
}
if
(
comp
.
upvalue_count
>
0
)
{
fn
->
upval_descs
=
code_arena_bump
(
(
size_t
)
comp
.
upvalue_count
*
sizeof
(
sv_upval_desc_t
));
memcpy
(
fn
->
upval_descs
,
comp
.
upval_descs
,
(
size_t
)
comp
.
upvalue_count
*
sizeof
(
sv_upval_desc_t
));
fn
->
upvalue_count
=
comp
.
upvalue_count
;
}
fn
->
max_locals
=
comp
.
max_local_count
;
fn
->
max_stack
=
fn
->
max_locals
+
64
;
fn
->
param_count
=
comp
.
param_count
;
fn
->
is_strict
=
comp
.
is_strict
;
fn
->
filename
=
c
->
js
->
filename
;
fn
->
source_line
=
(
int
)
node
->
line
;
if
(
node
->
str
&&
node
->
len
>
0
)
{
char
*
name
=
code_arena_bump
(
node
->
len
+
1
);
memcpy
(
name
,
node
->
str
,
node
->
len
);
name
[
node
->
len
]
=
'\0'
;
fn
->
name
=
name
;
}
free
(
comp
.
code
);
free
(
comp
.
constants
);
free
(
comp
.
atoms
);
free
(
comp
.
locals
);
free
(
comp
.
upval_descs
);
free
(
comp
.
loops
);
int
idx
=
add_constant
(
c
,
mkval
(
T_CFUNC
,
(
uintptr_t
)
fn
));
emit_op
(
c
,
OP_CLOSURE
);
emit_u32
(
c
,
(
uint32_t
)
idx
);
}
else
emit_op
(
c
,
OP_UNDEF
);
free
(
field_inits
);
free
(
computed_key_locals
);
c
->
computed_key_locals
=
NULL
;
bool
has_static_name
=
false
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
m
=
node
->
args
.
items
[
i
];
if
(
m
->
type
!=
N_METHOD
)
continue
;
if
(
!
(
m
->
flags
&
FN_STATIC
))
continue
;
if
(
m
->
flags
&
FN_COMPUTED
)
continue
;
if
(
m
->
left
&&
m
->
left
->
str
&&
m
->
left
->
len
==
4
&&
memcmp
(
m
->
left
->
str
,
"name"
,
4
)
==
0
)
{
has_static_name
=
true
;
break
;
}
}
if
(
node
->
str
&&
!
has_static_name
)
{
int
atom
=
add_atom
(
c
,
node
->
str
,
node
->
len
);
emit_op
(
c
,
OP_DEFINE_CLASS
);
emit_u32
(
c
,
(
uint32_t
)
atom
);
emit
(
c
,
1
);
}
else
{
emit_op
(
c
,
OP_DEFINE_CLASS
);
emit_u32
(
c
,
0
);
emit
(
c
,
0
);
}
int
proto_local
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
int
ctor_local
=
add_local
(
c
,
""
,
0
,
false
,
c
->
scope_depth
);
emit_put_local
(
c
,
proto_local
);
emit_put_local
(
c
,
ctor_local
);
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
m
=
node
->
args
.
items
[
i
];
if
(
m
->
type
==
N_STATIC_BLOCK
)
{
begin_scope
(
c
);
compile_stmts
(
c
,
&
m
->
args
);
end_scope
(
c
);
continue
;
}
if
(
m
->
type
!=
N_METHOD
)
continue
;
if
(
m
==
ctor_method
)
continue
;
bool
is_fn
=
(
m
->
right
&&
m
->
right
->
type
==
N_FUNC
);
if
(
!
is_fn
&&
!
(
m
->
flags
&
FN_STATIC
))
continue
;
compile_class_method
(
c
,
m
,
ctor_local
,
proto_local
,
method_comp_keys
?
method_comp_keys
[
i
]
:
-1
);
}
free
(
method_comp_keys
);
emit_get_local
(
c
,
ctor_local
);
if
(
inner_name_local
>=
0
)
{
emit_op
(
c
,
OP_DUP
);
emit_put_local
(
c
,
inner_name_local
);
}
if
(
class_repl_top
&&
node
->
str
)
{
emit_op
(
c
,
OP_DUP
);
emit_atom_op
(
c
,
OP_PUT_GLOBAL
,
node
->
str
,
node
->
len
);
}
else
if
(
outer_name_local
>=
0
)
{
emit_op
(
c
,
OP_DUP
);
emit_put_local
(
c
,
outer_name_local
);
c
->
locals
[
outer_name_local
].
is_tdz
=
false
;
}
if
(
node
->
str
)
end_scope
(
c
);
}
static
sv_func_t
*
compile_function_body
(
sv_compiler_t
*
enclosing
,
sv_ast_t
*
node
,
sv_compile_mode_t
mode
)
{
sv_compiler_t
comp
=
{
0
};
comp
.
js
=
enclosing
->
js
;
comp
.
source
=
enclosing
->
source
;
comp
.
source_len
=
enclosing
->
source_len
;
comp
.
enclosing
=
enclosing
;
comp
.
scope_depth
=
0
;
comp
.
is_arrow
=
!!
(
node
->
flags
&
FN_ARROW
);
comp
.
is_strict
=
enclosing
->
is_strict
;
comp
.
mode
=
mode
;
comp
.
strict_args_local
=
-1
;
comp
.
new_target_local
=
-1
;
comp
.
super_local
=
-1
;
comp
.
param_count
=
node
->
args
.
count
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
p
=
node
->
args
.
items
[
i
];
if
(
p
->
type
==
N_IDENT
)
{
add_local
(
&
comp
,
p
->
str
,
p
->
len
,
false
,
-1
);
}
else
if
(
p
->
type
==
N_REST
&&
p
->
right
&&
p
->
right
->
type
==
N_IDENT
)
{
add_local
(
&
comp
,
p
->
right
->
str
,
p
->
right
->
len
,
false
,
-1
);
}
else
if
(
p
->
type
==
N_ASSIGN_PAT
&&
p
->
left
&&
p
->
left
->
type
==
N_IDENT
)
{
add_local
(
&
comp
,
p
->
left
->
str
,
p
->
left
->
len
,
false
,
-1
);
}
else
add_local
(
&
comp
,
""
,
0
,
false
,
-1
);
}
comp
.
param_locals
=
comp
.
local_count
;
if
(
!
comp
.
is_strict
&&
node
->
body
&&
node
->
body
->
type
==
N_BLOCK
)
{
for
(
int
i
=
0
;
i
<
node
->
body
->
args
.
count
;
i
++
)
{
sv_ast_t
*
stmt
=
node
->
body
->
args
.
items
[
i
];
if
(
!
stmt
||
stmt
->
type
==
N_EMPTY
)
continue
;
if
(
stmt
->
type
!=
N_STRING
)
break
;
if
(
sv_ast_is_use_strict
(
comp
.
js
,
stmt
))
comp
.
is_strict
=
true
;
}
}
if
(
comp
.
is_strict
)
{
const
char
*
param_names
[
256
];
uint32_t
param_lens
[
256
];
int
param_name_count
=
0
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
p
=
node
->
args
.
items
[
i
];
const
char
*
pname
=
NULL
;
uint32_t
plen
=
0
;
if
(
p
&&
p
->
type
==
N_IDENT
)
{
pname
=
p
->
str
;
plen
=
p
->
len
;
}
else
if
(
p
&&
p
->
type
==
N_REST
&&
p
->
right
&&
p
->
right
->
type
==
N_IDENT
)
{
pname
=
p
->
right
->
str
;
plen
=
p
->
right
->
len
;
}
else
if
(
p
&&
p
->
type
==
N_ASSIGN_PAT
&&
p
->
left
&&
p
->
left
->
type
==
N_IDENT
)
{
pname
=
p
->
left
->
str
;
plen
=
p
->
left
->
len
;
}
if
(
!
pname
||
plen
==
0
)
continue
;
if
(
is_strict_restricted_ident
(
pname
,
plen
))
{
js_mkerr_typed
(
comp
.
js
,
JS_ERR_SYNTAX
,
"strict mode forbids '%.*s' as a parameter name"
,
(
int
)
plen
,
pname
);
return
NULL
;
}
for
(
int
j
=
0
;
j
<
param_name_count
;
j
++
)
{
if
(
param_lens
[
j
]
==
plen
&&
memcmp
(
param_names
[
j
],
pname
,
plen
)
==
0
)
{
js_mkerr_typed
(
comp
.
js
,
JS_ERR_SYNTAX
,
"duplicate parameter name '%.*s' in strict mode"
,
(
int
)
plen
,
pname
);
return
NULL
;
}
}
if
(
param_name_count
<
(
int
)(
sizeof
(
param_names
)
/
sizeof
(
param_names
[
0
])))
{
param_names
[
param_name_count
]
=
pname
;
param_lens
[
param_name_count
]
=
plen
;
param_name_count
++
;
}
}
}
bool
has_non_simple_params
=
false
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
p
=
node
->
args
.
items
[
i
];
if
(
p
->
type
!=
N_IDENT
)
{
has_non_simple_params
=
true
;
break
;
}
}
bool
repl_top
=
is_repl_top_level
(
&
comp
);
if
(
!
has_non_simple_params
&&
node
->
body
)
{
if
(
node
->
body
->
type
==
N_BLOCK
)
{
if
(
!
repl_top
)
{
for
(
int
i
=
0
;
i
<
node
->
body
->
args
.
count
;
i
++
)
hoist_var_decls
(
&
comp
,
node
->
body
->
args
.
items
[
i
]);
hoist_lexical_decls
(
&
comp
,
&
node
->
body
->
args
);
}
hoist_func_decls
(
&
comp
,
&
node
->
body
->
args
);
}
else
if
(
!
repl_top
)
hoist_var_decls
(
&
comp
,
node
->
body
);
}
if
(
!
has_non_simple_params
)
{
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
p
=
node
->
args
.
items
[
i
];
if
(
p
->
type
==
N_ASSIGN_PAT
)
{
emit_op
(
&
comp
,
OP_GET_ARG
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
emit_op
(
&
comp
,
OP_DUP
);
emit_op
(
&
comp
,
OP_IS_UNDEF
);
int
skip
=
emit_jump
(
&
comp
,
OP_JMP_FALSE
);
emit_op
(
&
comp
,
OP_POP
);
compile_expr
(
&
comp
,
p
->
right
);
patch_jump
(
&
comp
,
skip
);
if
(
p
->
left
&&
p
->
left
->
type
==
N_IDENT
)
{
emit_op
(
&
comp
,
OP_PUT_ARG
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
}
else
if
(
p
->
left
)
{
compile_destructure_binding
(
&
comp
,
p
->
left
,
VAR_LET
);
emit_op
(
&
comp
,
OP_POP
);
}
else
emit_op
(
&
comp
,
OP_POP
);
}
else
if
(
p
->
type
==
N_ARRAY_PAT
||
p
->
type
==
N_ARRAY
||
p
->
type
==
N_OBJECT_PAT
||
p
->
type
==
N_OBJECT
)
{
emit_op
(
&
comp
,
OP_GET_ARG
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
compile_destructure_binding
(
&
comp
,
p
,
VAR_LET
);
emit_op
(
&
comp
,
OP_POP
);
}
else
if
(
p
->
type
==
N_REST
&&
p
->
right
&&
p
->
right
->
type
==
N_IDENT
)
{
emit_op
(
&
comp
,
OP_REST
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
int
loc
=
resolve_local
(
&
comp
,
p
->
right
->
str
,
p
->
right
->
len
);
if
(
loc
>=
0
)
{
emit_op
(
&
comp
,
OP_PUT_ARG
);
emit_u16
(
&
comp
,
(
uint16_t
)
loc
);
}
}
else
if
(
p
->
type
==
N_REST
&&
p
->
right
)
{
emit_op
(
&
comp
,
OP_REST
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
compile_destructure_binding
(
&
comp
,
p
->
right
,
VAR_LET
);
emit_op
(
&
comp
,
OP_POP
);
}
}
}
else
{
int
*
param_bind_locals
=
NULL
;
int
param_bind_count
=
0
;
int
param_bind_cap
=
0
;
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
p
=
node
->
args
.
items
[
i
];
const
char
*
pname
=
NULL
;
uint32_t
plen
=
0
;
if
(
p
->
type
==
N_IDENT
)
{
pname
=
p
->
str
;
plen
=
p
->
len
;
}
else
if
((
p
->
type
==
N_ASSIGN_PAT
)
&&
p
->
left
&&
p
->
left
->
type
==
N_IDENT
)
{
pname
=
p
->
left
->
str
;
plen
=
p
->
left
->
len
;
}
else
if
(
p
->
type
==
N_REST
&&
p
->
right
&&
p
->
right
->
type
==
N_IDENT
)
{
pname
=
p
->
right
->
str
;
plen
=
p
->
right
->
len
;
}
int
lb
=
comp
.
local_count
;
if
(
pname
&&
plen
)
{
int
loc
=
add_local
(
&
comp
,
pname
,
plen
,
false
,
0
);
comp
.
locals
[
loc
].
is_tdz
=
true
;
int
slot
=
loc
-
comp
.
param_locals
;
emit_op
(
&
comp
,
OP_SET_LOCAL_UNDEF
);
emit_u16
(
&
comp
,
(
uint16_t
)
slot
);
}
if
(
param_bind_count
>=
param_bind_cap
)
{
param_bind_cap
=
param_bind_cap
?
param_bind_cap
*
2
:
8
;
param_bind_locals
=
realloc
(
param_bind_locals
,
(
size_t
)
param_bind_cap
*
sizeof
(
int
));
}
param_bind_locals
[
param_bind_count
++
]
=
lb
;
}
for
(
int
i
=
0
;
i
<
node
->
args
.
count
;
i
++
)
{
sv_ast_t
*
p
=
node
->
args
.
items
[
i
];
int
bind_lb
=
param_bind_locals
[
i
];
if
(
p
->
type
==
N_IDENT
)
{
if
(
bind_lb
<
comp
.
local_count
)
{
emit_op
(
&
comp
,
OP_GET_ARG
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
int
slot
=
bind_lb
-
comp
.
param_locals
;
comp
.
locals
[
bind_lb
].
is_tdz
=
false
;
if
(
slot
<=
255
)
{
emit_op
(
&
comp
,
OP_PUT_LOCAL8
);
emit
(
&
comp
,
(
uint8_t
)
slot
);
}
else
{
emit_op
(
&
comp
,
OP_PUT_LOCAL
);
emit_u16
(
&
comp
,
(
uint16_t
)
slot
);
}
}
}
else
if
(
p
->
type
==
N_ASSIGN_PAT
)
{
emit_op
(
&
comp
,
OP_GET_ARG
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
emit_op
(
&
comp
,
OP_DUP
);
emit_op
(
&
comp
,
OP_IS_UNDEF
);
int
skip
=
emit_jump
(
&
comp
,
OP_JMP_FALSE
);
emit_op
(
&
comp
,
OP_POP
);
compile_expr
(
&
comp
,
p
->
right
);
patch_jump
(
&
comp
,
skip
);
if
(
p
->
left
&&
p
->
left
->
type
==
N_IDENT
&&
bind_lb
<
comp
.
local_count
)
{
int
slot
=
bind_lb
-
comp
.
param_locals
;
comp
.
locals
[
bind_lb
].
is_tdz
=
false
;
if
(
slot
<=
255
)
{
emit_op
(
&
comp
,
OP_PUT_LOCAL8
);
emit
(
&
comp
,
(
uint8_t
)
slot
);
}
else
{
emit_op
(
&
comp
,
OP_PUT_LOCAL
);
emit_u16
(
&
comp
,
(
uint16_t
)
slot
);
}
}
else
if
(
p
->
left
)
{
compile_destructure_binding
(
&
comp
,
p
->
left
,
VAR_LET
);
emit_op
(
&
comp
,
OP_POP
);
}
else
{
emit_op
(
&
comp
,
OP_POP
);
}
}
else
if
(
p
->
type
==
N_ARRAY_PAT
||
p
->
type
==
N_ARRAY
||
p
->
type
==
N_OBJECT_PAT
||
p
->
type
==
N_OBJECT
)
{
emit_op
(
&
comp
,
OP_GET_ARG
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
compile_destructure_binding
(
&
comp
,
p
,
VAR_LET
);
emit_op
(
&
comp
,
OP_POP
);
}
else
if
(
p
->
type
==
N_REST
&&
p
->
right
&&
p
->
right
->
type
==
N_IDENT
&&
bind_lb
<
comp
.
local_count
)
{
emit_op
(
&
comp
,
OP_REST
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
int
slot
=
bind_lb
-
comp
.
param_locals
;
comp
.
locals
[
bind_lb
].
is_tdz
=
false
;
if
(
slot
<=
255
)
{
emit_op
(
&
comp
,
OP_PUT_LOCAL8
);
emit
(
&
comp
,
(
uint8_t
)
slot
);
}
else
{
emit_op
(
&
comp
,
OP_PUT_LOCAL
);
emit_u16
(
&
comp
,
(
uint16_t
)
slot
);
}
}
else
if
(
p
->
type
==
N_REST
&&
p
->
right
)
{
emit_op
(
&
comp
,
OP_REST
);
emit_u16
(
&
comp
,
(
uint16_t
)
i
);
compile_destructure_binding
(
&
comp
,
p
->
right
,
VAR_LET
);
emit_op
(
&
comp
,
OP_POP
);
}
}
free
(
param_bind_locals
);
if
(
node
->
body
)
{
if
(
node
->
body
->
type
==
N_BLOCK
)
{
if
(
!
repl_top
)
{
for
(
int
i
=
0
;
i
<
node
->
body
->
args
.
count
;
i
++
)
hoist_var_decls
(
&
comp
,
node
->
body
->
args
.
items
[
i
]);
hoist_lexical_decls
(
&
comp
,
&
node
->
body
->
args
);
}
hoist_func_decls
(
&
comp
,
&
node
->
body
->
args
);
}
else
if
(
!
repl_top
)
{
hoist_var_decls
(
&
comp
,
node
->
body
);
}
}
}
if
(
!
comp
.
is_arrow
&&
has_implicit_arguments_obj
(
&
comp
)
&&
(
node
->
flags
&
FN_USES_ARGS
))
{
comp
.
strict_args_local
=
add_local
(
&
comp
,
""
,
0
,
false
,
comp
.
scope_depth
);
emit_op
(
&
comp
,
OP_SPECIAL_OBJ
);
emit
(
&
comp
,
0
);
emit_put_local
(
&
comp
,
comp
.
strict_args_local
);
}
if
(
!
comp
.
is_arrow
&&
comp
.
enclosing
)
{
static
const
char
nt_name
[]
=
"
\x01
new.target"
;
comp
.
new_target_local
=
add_local
(
&
comp
,
nt_name
,
sizeof
(
nt_name
)
-
1
,
false
,
comp
.
scope_depth
);
emit_op
(
&
comp
,
OP_SPECIAL_OBJ
);
emit
(
&
comp
,
1
);
emit_put_local
(
&
comp
,
comp
.
new_target_local
);
}
if
(
!
comp
.
is_arrow
&&
comp
.
enclosing
&&
(
node
->
flags
&
(
FN_METHOD
|
FN_GETTER
|
FN_SETTER
|
FN_STATIC
)))
{
static
const
char
sv_name
[]
=
"
\x01
super"
;
comp
.
super_local
=
add_local
(
&
comp
,
sv_name
,
sizeof
(
sv_name
)
-
1
,
false
,
comp
.
scope_depth
);
emit_op
(
&
comp
,
OP_SPECIAL_OBJ
);
emit
(
&
comp
,
2
);
emit_put_local
(
&
comp
,
comp
.
super_local
);
}
if
(
enclosing
->
field_init_count
>
0
)
{
emit_field_inits
(
&
comp
,
enclosing
->
field_inits
,
enclosing
->
field_init_count
);
}
if
(
node
->
body
)
{
if
(
node
->
body
->
type
==
N_BLOCK
)
{
int
last_expr_idx
=
-1
;
if
(
has_completion_value
(
&
comp
)
&&
node
->
body
->
args
.
count
>
0
)
{
sv_ast_t
*
last
=
node
->
body
->
args
.
items
[
node
->
body
->
args
.
count
-
1
];
if
(
sv_ast_can_be_expression_statement
(
last
))
last_expr_idx
=
node
->
body
->
args
.
count
-
1
;
}
for
(
int
i
=
0
;
i
<
node
->
body
->
args
.
count
;
i
++
)
{
sv_ast_t
*
stmt
=
node
->
body
->
args
.
items
[
i
];
if
(
i
==
last_expr_idx
)
{
compile_expr
(
&
comp
,
stmt
);
emit_close_upvals
(
&
comp
);
emit_op
(
&
comp
,
OP_RETURN
);
}
else
{
compile_stmt
(
&
comp
,
stmt
);
}
}
}
else
compile_tail_return_expr
(
&
comp
,
node
->
body
);
}
for
(
int
i
=
0
;
i
<
comp
.
deferred_export_count
;
i
++
)
{
sv_deferred_export_t
*
e
=
&
comp
.
deferred_exports
[
i
];
compile_export_emit
(
&
comp
,
e
->
name
,
e
->
len
);
}
free
(
comp
.
deferred_exports
);
emit_close_upvals
(
&
comp
);
emit_op
(
&
comp
,
OP_RETURN_UNDEF
);
int
max_locals
=
comp
.
max_local_count
-
comp
.
param_locals
;
sv_func_t
*
func
=
code_arena_bump
(
sizeof
(
sv_func_t
));
memset
(
func
,
0
,
sizeof
(
sv_func_t
));
func
->
code
=
code_arena_bump
((
size_t
)
comp
.
code_len
);
memcpy
(
func
->
code
,
comp
.
code
,
(
size_t
)
comp
.
code_len
);
func
->
code_len
=
comp
.
code_len
;
if
(
comp
.
const_count
>
0
)
{
func
->
constants
=
code_arena_bump
((
size_t
)
comp
.
const_count
*
sizeof
(
jsval_t
));
memcpy
(
func
->
constants
,
comp
.
constants
,
(
size_t
)
comp
.
const_count
*
sizeof
(
jsval_t
));
func
->
const_count
=
comp
.
const_count
;
}
if
(
comp
.
atom_count
>
0
)
{
func
->
atoms
=
code_arena_bump
((
size_t
)
comp
.
atom_count
*
sizeof
(
sv_atom_t
));
memcpy
(
func
->
atoms
,
comp
.
atoms
,
(
size_t
)
comp
.
atom_count
*
sizeof
(
sv_atom_t
));
func
->
atom_count
=
comp
.
atom_count
;
}
if
(
comp
.
upvalue_count
>
0
)
{
func
->
upval_descs
=
code_arena_bump
(
(
size_t
)
comp
.
upvalue_count
*
sizeof
(
sv_upval_desc_t
));
memcpy
(
func
->
upval_descs
,
comp
.
upval_descs
,
(
size_t
)
comp
.
upvalue_count
*
sizeof
(
sv_upval_desc_t
));
func
->
upvalue_count
=
comp
.
upvalue_count
;
}
if
(
comp
.
srcpos_count
>
0
)
{
func
->
srcpos
=
code_arena_bump
((
size_t
)
comp
.
srcpos_count
*
sizeof
(
sv_srcpos_t
));
memcpy
(
func
->
srcpos
,
comp
.
srcpos
,
(
size_t
)
comp
.
srcpos_count
*
sizeof
(
sv_srcpos_t
));
func
->
srcpos_count
=
comp
.
srcpos_count
;
}
if
(
enclosing
->
source
&&
enclosing
->
source_len
>
0
)
{
func
->
source
=
enclosing
->
source
;
func
->
source_len
=
(
int
)
enclosing
->
source_len
;
func
->
source_start
=
(
int
)
node
->
src_off
;
func
->
source_end
=
(
node
->
src_end
>
node
->
src_off
)
?
(
int
)
node
->
src_end
:
func
->
source_len
;
}
func
->
max_locals
=
max_locals
;
func
->
max_stack
=
max_locals
+
64
;
func
->
param_count
=
comp
.
param_count
;
func
->
is_strict
=
comp
.
is_strict
;
func
->
is_arrow
=
comp
.
is_arrow
;
func
->
is_async
=
!!
(
node
->
flags
&
FN_ASYNC
);
func
->
is_generator
=
!!
(
node
->
flags
&
FN_GENERATOR
);
func
->
is_method
=
!!
(
node
->
flags
&
FN_METHOD
);
func
->
is_tla
=
comp
.
is_tla
;
func
->
filename
=
enclosing
->
js
->
filename
;
func
->
source_line
=
(
int
)
node
->
line
;
if
(
node
->
str
&&
node
->
len
>
0
)
{
char
*
name
=
code_arena_bump
(
node
->
len
+
1
);
memcpy
(
name
,
node
->
str
,
node
->
len
);
name
[
node
->
len
]
=
'\0'
;
func
->
name
=
name
;
}
else
if
(
enclosing
->
inferred_name
&&
enclosing
->
inferred_name_len
>
0
)
{
char
*
name
=
code_arena_bump
(
enclosing
->
inferred_name_len
+
1
);
memcpy
(
name
,
enclosing
->
inferred_name
,
enclosing
->
inferred_name_len
);
name
[
enclosing
->
inferred_name_len
]
=
'\0'
;
func
->
name
=
name
;
}
free
(
comp
.
code
);
free
(
comp
.
constants
);
free
(
comp
.
atoms
);
free
(
comp
.
locals
);
free
(
comp
.
upval_descs
);
free
(
comp
.
loops
);
free
(
comp
.
srcpos
);
return
func
;
}
static
const
char
*
sv_op_names
[
OP__COUNT
]
=
{
#define OP_DEF(name, size, n_pop, n_push, f) [OP_##name] = #name,
#include
"silver/opcode.h"
};
enum
{
SVF_none
,
SVF_u8
,
SVF_i8
,
SVF_u16
,
SVF_i16
,
SVF_u32
,
SVF_i32
,
SVF_atom
,
SVF_atom_u8
,
SVF_label
,
SVF_label8
,
SVF_loc
,
SVF_loc8
,
SVF_loc_atom
,
SVF_arg
,
SVF_const
,
SVF_const8
,
SVF_npop
,
SVF_var_ref
,
};
static
const
uint8_t
sv_op_fmts
[
OP__COUNT
]
=
{
#define OP_DEF(name, size, n_pop, n_push, f) [OP_##name] = SVF_##f,
#include
"silver/opcode.h"
};
void
sv_disasm
(
ant_t
*
js
,
sv_func_t
*
func
,
const
char
*
label
)
{
const
char
*
fname
=
func
->
name
?
func
->
name
:
""
;
fprintf
(
stderr
,
"[generated bytecode for function: %s (%p <SharedFunctionInfo %s>)]
\n
"
,
fname
,
(
void
*
)
func
,
fname
);
fprintf
(
stderr
,
"Bytecode length: %d
\n
"
,
func
->
code_len
);
fprintf
(
stderr
,
"Parameter count %d
\n
"
,
func
->
param_count
);
fprintf
(
stderr
,
"Register count %d
\n
"
,
func
->
max_locals
);
fprintf
(
stderr
,
"Frame size %d
\n
"
,
func
->
max_locals
*
(
int
)
sizeof
(
jsval_t
));
int
pc
=
0
;
while
(
pc
<
func
->
code_len
)
{
uint8_t
op
=
func
->
code
[
pc
];
const
char
*
name
=
(
op
<
OP__COUNT
)
?
sv_op_names
[
op
]
:
"???"
;
uint8_t
size
=
(
op
<
OP__COUNT
)
?
sv_op_size
[
op
]
:
1
;
uint8_t
fmt
=
(
op
<
OP__COUNT
)
?
sv_op_fmts
[
op
]
:
SVF_none
;
uint32_t
line
,
col
;
if
(
sv_lookup_srcpos
(
func
,
pc
,
&
line
,
&
col
))
fprintf
(
stderr
,
"%5u S> "
,
line
);
else
fprintf
(
stderr
,
" "
);
fprintf
(
stderr
,
"%p @ %4d : "
,
(
void
*
)(
func
->
code
+
pc
),
pc
);
char
hex
[
32
];
int
hlen
=
0
;
for
(
int
i
=
0
;
i
<
size
&&
i
<
8
;
i
++
)
hlen
+=
snprintf
(
hex
+
hlen
,
sizeof
(
hex
)
-
hlen
,
"%02x "
,
func
->
code
[
pc
+
i
]);
fprintf
(
stderr
,
"%-18s"
,
hex
);
fprintf
(
stderr
,
"%s"
,
name
?
name
:
"???"
);
switch
(
fmt
)
{
case
SVF_u8
:
fprintf
(
stderr
,
" [%d]"
,
func
->
code
[
pc
+
1
]);
break
;
case
SVF_i8
:
fprintf
(
stderr
,
" [%d]"
,
(
int8_t
)
func
->
code
[
pc
+
1
]);
break
;
case
SVF_u16
:
fprintf
(
stderr
,
" [%d]"
,
sv_get_u16
(
func
->
code
+
pc
+
1
));
break
;
case
SVF_i16
:
fprintf
(
stderr
,
" [%d]"
,
(
int16_t
)
sv_get_u16
(
func
->
code
+
pc
+
1
));
break
;
case
SVF_u32
:
fprintf
(
stderr
,
" [%d]"
,
(
int
)
sv_get_u32
(
func
->
code
+
pc
+
1
));
break
;
case
SVF_i32
:
fprintf
(
stderr
,
" [%d]"
,
(
int32_t
)
sv_get_u32
(
func
->
code
+
pc
+
1
));
break
;
case
SVF_atom
:
{
uint32_t
idx
=
sv_get_u32
(
func
->
code
+
pc
+
1
);
if
(
idx
<
(
uint32_t
)
func
->
atom_count
)
fprintf
(
stderr
,
" [%.*s]"
,
(
int
)
func
->
atoms
[
idx
].
len
,
func
->
atoms
[
idx
].
str
);
else
fprintf
(
stderr
,
" a%u"
,
idx
);
break
;
}
case
SVF_atom_u8
:
{
uint32_t
idx
=
sv_get_u32
(
func
->
code
+
pc
+
1
);
uint8_t
extra
=
func
->
code
[
pc
+
5
];
if
(
idx
<
(
uint32_t
)
func
->
atom_count
)
fprintf
(
stderr
,
" [%.*s], [%d]"
,
(
int
)
func
->
atoms
[
idx
].
len
,
func
->
atoms
[
idx
].
str
,
extra
);
else
fprintf
(
stderr
,
" a%u, [%d]"
,
idx
,
extra
);
break
;
}
case
SVF_label
:
{
int32_t
off
=
(
int32_t
)
sv_get_u32
(
func
->
code
+
pc
+
1
);
fprintf
(
stderr
,
" [%d] (%d)"
,
off
,
pc
+
size
+
off
);
break
;
}
case
SVF_label8
:
{
int8_t
off
=
(
int8_t
)
func
->
code
[
pc
+
1
];
fprintf
(
stderr
,
" [%d] (%d)"
,
off
,
pc
+
size
+
off
);
break
;
}
case
SVF_loc
:
fprintf
(
stderr
,
" r%d"
,
sv_get_u16
(
func
->
code
+
pc
+
1
));
break
;
case
SVF_loc8
:
fprintf
(
stderr
,
" r%d"
,
func
->
code
[
pc
+
1
]);
break
;
case
SVF_loc_atom
:
{
uint16_t
slot
=
sv_get_u16
(
func
->
code
+
pc
+
1
);
uint32_t
aidx
=
sv_get_u32
(
func
->
code
+
pc
+
3
);
fprintf
(
stderr
,
" r%d"
,
slot
);
if
(
aidx
<
(
uint32_t
)
func
->
atom_count
)
fprintf
(
stderr
,
", [%.*s]"
,
(
int
)
func
->
atoms
[
aidx
].
len
,
func
->
atoms
[
aidx
].
str
);
else
fprintf
(
stderr
,
", a%u"
,
aidx
);
break
;
}
case
SVF_arg
:
fprintf
(
stderr
,
" a%d"
,
sv_get_u16
(
func
->
code
+
pc
+
1
));
break
;
case
SVF_const
:
{
uint32_t
idx
=
sv_get_u32
(
func
->
code
+
pc
+
1
);
fprintf
(
stderr
,
" [%u]"
,
idx
);
break
;
}
case
SVF_const8
:
fprintf
(
stderr
,
" [%d]"
,
func
->
code
[
pc
+
1
]);
break
;
case
SVF_npop
:
fprintf
(
stderr
,
" %d"
,
sv_get_u16
(
func
->
code
+
pc
+
1
));
break
;
case
SVF_var_ref
:
fprintf
(
stderr
,
" [%d]"
,
sv_get_u16
(
func
->
code
+
pc
+
1
));
break
;
default
:
break
;
}
fprintf
(
stderr
,
"
\n
"
);
pc
+=
size
;
}
fprintf
(
stderr
,
"Constant pool (size = %d)
\n
"
,
func
->
const_count
);
for
(
int
i
=
0
;
i
<
func
->
const_count
;
i
++
)
{
jsval_t
v
=
func
->
constants
[
i
];
uint8_t
t
=
vtype
(
v
);
if
(
t
==
T_STR
)
{
jsoff_t
slen
;
jsoff_t
soff
=
vstr
(
js
,
v
,
&
slen
);
fprintf
(
stderr
,
" %d: <String[%d]: #%.*s>
\n
"
,
i
,
(
int
)
slen
,
(
int
)
slen
,
&
js
->
mem
[
soff
]);
}
else
if
(
t
==
T_NUM
)
{
fprintf
(
stderr
,
" %d: <Number [%g]>
\n
"
,
i
,
tod
(
v
));
}
else
if
(
t
==
T_CFUNC
)
{
sv_func_t
*
child
=
(
sv_func_t
*
)(
uintptr_t
)
vdata
(
v
);
const
char
*
cname
=
child
->
name
?
child
->
name
:
""
;
fprintf
(
stderr
,
" %d: <SharedFunctionInfo %s>
\n
"
,
i
,
cname
);
}
else
fprintf
(
stderr
,
" %d: <Unknown type=%d>
\n
"
,
i
,
t
);
}
if
(
func
->
atom_count
>
0
)
{
fprintf
(
stderr
,
"Atom table (size = %d)
\n
"
,
func
->
atom_count
);
for
(
int
i
=
0
;
i
<
func
->
atom_count
;
i
++
)
fprintf
(
stderr
,
" %d: <String[%d]: #%.*s>
\n
"
,
i
,
(
int
)
func
->
atoms
[
i
].
len
,
(
int
)
func
->
atoms
[
i
].
len
,
func
->
atoms
[
i
].
str
);
}
fprintf
(
stderr
,
"Handler Table (size = 0)
\n
"
);
fprintf
(
stderr
,
"Source Position Table (size = %d)
\n
"
,
func
->
srcpos_count
);
fprintf
(
stderr
,
"
\n
"
);
for
(
int
i
=
0
;
i
<
func
->
const_count
;
i
++
)
{
if
(
vtype
(
func
->
constants
[
i
])
==
T_CFUNC
)
{
sv_func_t
*
child
=
(
sv_func_t
*
)(
uintptr_t
)
vdata
(
func
->
constants
[
i
]);
char
child_label
[
256
];
snprintf
(
child_label
,
sizeof
(
child_label
),
"%s/closure[%d]"
,
label
,
i
);
sv_disasm
(
js
,
child
,
child_label
);
}
}
}
sv_func_t
*
sv_compile
(
ant_t
*
js
,
sv_ast_t
*
program
,
sv_compile_mode_t
mode
,
const
char
*
source
,
jsoff_t
source_len
)
{
if
(
!
program
||
program
->
type
!=
N_PROGRAM
)
return
NULL
;
static
const
char
*
k_top_name_script
=
"<script>"
;
static
const
char
*
k_top_name_module
=
"<module>"
;
static
const
char
*
k_top_name_eval
=
"<eval>"
;
static
const
char
*
k_top_name_repl
=
"<repl>"
;
const
char
*
top_name
=
k_top_name_script
;
switch
(
mode
)
{
case
SV_COMPILE_MODULE
:
top_name
=
k_top_name_module
;
break
;
case
SV_COMPILE_EVAL
:
top_name
=
k_top_name_eval
;
break
;
case
SV_COMPILE_REPL
:
top_name
=
k_top_name_repl
;
break
;
case
SV_COMPILE_SCRIPT
:
default
:
top_name
=
k_top_name_script
;
break
;
}
sv_ast_t
top_fn
;
memset
(
&
top_fn
,
0
,
sizeof
(
top_fn
));
top_fn
.
type
=
N_FUNC
;
top_fn
.
line
=
1
;
top_fn
.
str
=
top_name
;
top_fn
.
len
=
(
uint32_t
)
strlen
(
top_name
);
top_fn
.
src_off
=
0
;
top_fn
.
src_end
=
(
source_len
>
0
)
?
(
uint32_t
)
source_len
:
0
;
top_fn
.
body
=
sv_ast_new
(
N_BLOCK
);
top_fn
.
body
->
args
=
program
->
args
;
sv_compiler_t
root
=
{
0
};
root
.
js
=
js
;
root
.
source
=
pin_source_text
(
source
,
source_len
);
root
.
source_len
=
source_len
;
root
.
mode
=
mode
;
root
.
is_strict
=
((
program
->
flags
&
FN_PARSE_STRICT
)
!=
0
);
sv_func_t
*
func
=
compile_function_body
(
&
root
,
&
top_fn
,
mode
);
if
(
js
->
thrown_exists
||
!
func
)
return
NULL
;
return
func
;
}
sv_func_t
*
sv_compile_function
(
ant_t
*
js
,
const
char
*
source
,
size_t
len
,
bool
is_async
)
{
const
char
*
prefix
=
is_async
?
"(async function"
:
"(function"
;
size_t
prefix_len
=
strlen
(
prefix
);
size_t
wrapped_len
=
prefix_len
+
len
+
1
;
char
*
wrapped
=
malloc
(
wrapped_len
+
1
);
if
(
!
wrapped
)
return
NULL
;
memcpy
(
wrapped
,
prefix
,
prefix_len
);
memcpy
(
wrapped
+
prefix_len
,
source
,
len
);
wrapped
[
prefix_len
+
len
]
=
')'
;
wrapped
[
wrapped_len
]
=
'\0'
;
bool
parse_strict
=
sv_vm_is_strict
(
js
->
vm
);
sv_ast_t
*
program
=
sv_parse
(
js
,
wrapped
,
(
jsoff_t
)
wrapped_len
,
parse_strict
);
if
(
!
program
)
{
free
(
wrapped
);
return
NULL
;
}
sv_ast_t
*
func_node
=
NULL
;
if
(
program
->
args
.
count
>
0
)
{
sv_ast_t
*
stmt
=
program
->
args
.
items
[
0
];
if
(
stmt
&&
stmt
->
type
==
N_FUNC
)
func_node
=
stmt
;
else
if
(
stmt
&&
stmt
->
left
&&
stmt
->
left
->
type
==
N_FUNC
)
func_node
=
stmt
->
left
;
}
if
(
!
func_node
)
{
free
(
wrapped
);
return
NULL
;
}
sv_compiler_t
root
=
{
0
};
root
.
js
=
js
;
root
.
source
=
pin_source_text
(
wrapped
,
(
jsoff_t
)
wrapped_len
);
root
.
source_len
=
(
jsoff_t
)
wrapped_len
;
root
.
mode
=
SV_COMPILE_SCRIPT
;
root
.
is_strict
=
(
program
->
flags
&
FN_PARSE_STRICT
)
!=
0
;
sv_func_t
*
func
=
compile_function_body
(
&
root
,
func_node
,
SV_COMPILE_SCRIPT
);
free
(
wrapped
);
if
(
js
->
thrown_exists
||
!
func
)
return
NULL
;
return
func
;
}
sv_func_t
*
sv_compile_function_parts
(
ant_t
*
js
,
const
char
*
params
,
size_t
params_len
,
const
char
*
body
,
size_t
body_len
,
bool
is_async
)
{
size_t
source_len
=
params_len
+
body_len
+
3
;
char
*
source
=
malloc
(
source_len
+
1
);
if
(
!
source
)
return
NULL
;
size_t
pos
=
0
;
source
[
pos
++
]
=
'('
;
if
(
params_len
>
0
&&
params
)
{
memcpy
(
source
+
pos
,
params
,
params_len
);
pos
+=
params_len
;
}
source
[
pos
++
]
=
')'
;
source
[
pos
++
]
=
'{'
;
if
(
body_len
>
0
&&
body
)
{
memcpy
(
source
+
pos
,
body
,
body_len
);
pos
+=
body_len
;
}
source
[
pos
++
]
=
'}'
;
source
[
pos
]
=
'\0'
;
sv_func_t
*
func
=
sv_compile_function
(
js
,
source
,
pos
,
is_async
);
free
(
source
);
return
func
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Mar 26, 4:41 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
511846
Default Alt Text
compiler.c (125 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment