Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2917333
bytecode.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
bytecode.c
View Options
#include
"bytecode.h"
#include
"ant.h"
#include
<stdlib.h>
#include
<string.h>
#include
<stdio.h>
#include
<math.h>
// ============================================================================
// Function operations
// ============================================================================
bc_func_t
*
bc_func_new
(
const
char
*
name
,
uint8_t
arity
)
{
bc_func_t
*
func
=
calloc
(
1
,
sizeof
(
bc_func_t
));
if
(
!
func
)
return
NULL
;
func
->
code_cap
=
64
;
func
->
code
=
malloc
(
sizeof
(
bc_inst_t
)
*
func
->
code_cap
);
func
->
const_cap
=
16
;
func
->
constants
=
malloc
(
sizeof
(
jsval_t
)
*
func
->
const_cap
);
func
->
names_cap
=
16
;
func
->
names
=
malloc
(
sizeof
(
char
*
)
*
func
->
names_cap
);
func
->
name
=
name
?
strdup
(
name
)
:
NULL
;
func
->
arity
=
arity
;
func
->
nlocals
=
arity
;
// params are first locals
return
func
;
}
void
bc_func_free
(
bc_func_t
*
func
)
{
if
(
!
func
)
return
;
free
(
func
->
code
);
free
(
func
->
constants
);
for
(
uint32_t
i
=
0
;
i
<
func
->
names_len
;
i
++
)
{
free
(
func
->
names
[
i
]);
}
free
(
func
->
names
);
free
((
void
*
)
func
->
name
);
free
(
func
);
}
uint32_t
bc_func_emit
(
bc_func_t
*
func
,
bc_inst_t
inst
)
{
if
(
func
->
code_len
>=
func
->
code_cap
)
{
func
->
code_cap
*=
2
;
func
->
code
=
realloc
(
func
->
code
,
sizeof
(
bc_inst_t
)
*
func
->
code_cap
);
}
func
->
code
[
func
->
code_len
]
=
inst
;
return
func
->
code_len
++
;
}
uint32_t
bc_func_emit_op
(
bc_func_t
*
func
,
bc_opcode_t
op
)
{
return
bc_func_emit
(
func
,
BC_MAKE
(
op
,
0
));
}
uint32_t
bc_func_add_const
(
bc_func_t
*
func
,
jsval_t
val
)
{
for
(
uint32_t
i
=
0
;
i
<
func
->
const_len
;
i
++
)
{
if
(
func
->
constants
[
i
]
==
val
)
return
i
;
}
if
(
func
->
const_len
>=
func
->
const_cap
)
{
func
->
const_cap
*=
2
;
func
->
constants
=
realloc
(
func
->
constants
,
sizeof
(
jsval_t
)
*
func
->
const_cap
);
}
func
->
constants
[
func
->
const_len
]
=
val
;
return
func
->
const_len
++
;
}
uint32_t
bc_func_add_name
(
bc_func_t
*
func
,
const
char
*
name
)
{
for
(
uint32_t
i
=
0
;
i
<
func
->
names_len
;
i
++
)
{
if
(
strcmp
(
func
->
names
[
i
],
name
)
==
0
)
return
i
;
}
if
(
func
->
names_len
>=
func
->
names_cap
)
{
func
->
names_cap
*=
2
;
func
->
names
=
realloc
(
func
->
names
,
sizeof
(
char
*
)
*
func
->
names_cap
);
}
func
->
names
[
func
->
names_len
]
=
strdup
(
name
);
return
func
->
names_len
++
;
}
// ============================================================================
// Chunk operations
// ============================================================================
bc_chunk_t
*
bc_chunk_new
(
void
)
{
bc_chunk_t
*
chunk
=
calloc
(
1
,
sizeof
(
bc_chunk_t
));
if
(
!
chunk
)
return
NULL
;
chunk
->
code_cap
=
64
;
chunk
->
code
=
malloc
(
sizeof
(
bc_inst_t
)
*
chunk
->
code_cap
);
chunk
->
const_cap
=
16
;
chunk
->
constants
=
malloc
(
sizeof
(
jsval_t
)
*
chunk
->
const_cap
);
chunk
->
names_cap
=
16
;
chunk
->
names
=
malloc
(
sizeof
(
char
*
)
*
chunk
->
names_cap
);
chunk
->
func_cap
=
8
;
chunk
->
functions
=
malloc
(
sizeof
(
bc_func_t
*
)
*
chunk
->
func_cap
);
return
chunk
;
}
void
bc_chunk_free
(
bc_chunk_t
*
chunk
)
{
if
(
!
chunk
)
return
;
free
(
chunk
->
code
);
free
(
chunk
->
constants
);
for
(
uint32_t
i
=
0
;
i
<
chunk
->
names_len
;
i
++
)
{
free
(
chunk
->
names
[
i
]);
}
free
(
chunk
->
names
);
for
(
uint32_t
i
=
0
;
i
<
chunk
->
func_len
;
i
++
)
{
bc_func_free
(
chunk
->
functions
[
i
]);
}
free
(
chunk
->
functions
);
free
(
chunk
->
lines
);
free
(
chunk
);
}
uint32_t
bc_emit
(
bc_chunk_t
*
chunk
,
bc_inst_t
inst
)
{
if
(
chunk
->
code_len
>=
chunk
->
code_cap
)
{
chunk
->
code_cap
*=
2
;
chunk
->
code
=
realloc
(
chunk
->
code
,
sizeof
(
bc_inst_t
)
*
chunk
->
code_cap
);
}
chunk
->
code
[
chunk
->
code_len
]
=
inst
;
return
chunk
->
code_len
++
;
}
uint32_t
bc_emit_op
(
bc_chunk_t
*
chunk
,
bc_opcode_t
op
)
{
return
bc_emit
(
chunk
,
BC_MAKE
(
op
,
0
));
}
uint32_t
bc_add_const
(
bc_chunk_t
*
chunk
,
jsval_t
val
)
{
for
(
uint32_t
i
=
0
;
i
<
chunk
->
const_len
;
i
++
)
{
if
(
chunk
->
constants
[
i
]
==
val
)
return
i
;
}
if
(
chunk
->
const_len
>=
chunk
->
const_cap
)
{
chunk
->
const_cap
*=
2
;
chunk
->
constants
=
realloc
(
chunk
->
constants
,
sizeof
(
jsval_t
)
*
chunk
->
const_cap
);
}
chunk
->
constants
[
chunk
->
const_len
]
=
val
;
return
chunk
->
const_len
++
;
}
uint32_t
bc_add_name
(
bc_chunk_t
*
chunk
,
const
char
*
name
)
{
for
(
uint32_t
i
=
0
;
i
<
chunk
->
names_len
;
i
++
)
{
if
(
strcmp
(
chunk
->
names
[
i
],
name
)
==
0
)
return
i
;
}
if
(
chunk
->
names_len
>=
chunk
->
names_cap
)
{
chunk
->
names_cap
*=
2
;
chunk
->
names
=
realloc
(
chunk
->
names
,
sizeof
(
char
*
)
*
chunk
->
names_cap
);
}
chunk
->
names
[
chunk
->
names_len
]
=
strdup
(
name
);
return
chunk
->
names_len
++
;
}
uint32_t
bc_add_func
(
bc_chunk_t
*
chunk
,
bc_func_t
*
func
)
{
if
(
chunk
->
func_len
>=
chunk
->
func_cap
)
{
chunk
->
func_cap
*=
2
;
chunk
->
functions
=
realloc
(
chunk
->
functions
,
sizeof
(
bc_func_t
*
)
*
chunk
->
func_cap
);
}
chunk
->
functions
[
chunk
->
func_len
]
=
func
;
return
chunk
->
func_len
++
;
}
void
bc_patch
(
bc_chunk_t
*
chunk
,
uint32_t
offset
,
bc_inst_t
inst
)
{
if
(
offset
<
chunk
->
code_len
)
{
chunk
->
code
[
offset
]
=
inst
;
}
}
// ============================================================================
// VM operations
// ============================================================================
bc_vm_t
*
bc_vm_new
(
struct
js
*
js
)
{
bc_vm_t
*
vm
=
calloc
(
1
,
sizeof
(
bc_vm_t
));
if
(
!
vm
)
return
NULL
;
vm
->
js
=
js
;
return
vm
;
}
void
bc_vm_free
(
bc_vm_t
*
vm
)
{
free
(
vm
);
}
void
bc_vm_reset
(
bc_vm_t
*
vm
)
{
vm
->
sp
=
0
;
vm
->
fp
=
0
;
vm
->
has_error
=
false
;
vm
->
error
=
js_mkundef
();
}
void
bc_vm_push
(
bc_vm_t
*
vm
,
jsval_t
val
)
{
if
(
vm
->
sp
>=
BC_STACK_MAX
)
{
vm
->
has_error
=
true
;
vm
->
error
=
js_mkerr
(
vm
->
js
,
"Stack overflow"
);
return
;
}
vm
->
stack
[
vm
->
sp
++
]
=
val
;
}
jsval_t
bc_vm_pop
(
bc_vm_t
*
vm
)
{
if
(
vm
->
sp
==
0
)
{
vm
->
has_error
=
true
;
vm
->
error
=
js_mkerr
(
vm
->
js
,
"Stack underflow"
);
return
js_mkundef
();
}
return
vm
->
stack
[
--
vm
->
sp
];
}
jsval_t
bc_vm_peek
(
bc_vm_t
*
vm
,
int
distance
)
{
if
(
vm
->
sp
<=
(
uint32_t
)
distance
)
return
js_mkundef
();
return
vm
->
stack
[
vm
->
sp
-
1
-
distance
];
}
void
bc_vm_set_global
(
bc_vm_t
*
vm
,
uint8_t
slot
,
jsval_t
val
)
{
vm
->
globals
[
slot
]
=
val
;
if
(
slot
>=
vm
->
nglobals
)
vm
->
nglobals
=
slot
+
1
;
}
jsval_t
bc_vm_get_global
(
bc_vm_t
*
vm
,
uint8_t
slot
)
{
return
vm
->
globals
[
slot
];
}
// Execute bytecode in a function
static
jsval_t
bc_vm_execute
(
bc_vm_t
*
vm
,
bc_inst_t
*
code
,
uint32_t
code_len
,
jsval_t
*
constants
,
uint32_t
const_len
,
char
**
names
,
uint32_t
names_len
,
bc_func_t
**
functions
,
uint32_t
func_len
,
jsval_t
*
locals
,
uint8_t
nlocals
);
// Call a bytecode function
jsval_t
bc_vm_call
(
bc_vm_t
*
vm
,
bc_func_t
*
func
,
jsval_t
*
args
,
int
nargs
)
{
if
(
vm
->
fp
>=
BC_CALL_MAX
)
{
return
js_mkerr
(
vm
->
js
,
"Call stack overflow"
);
}
bc_frame_t
*
frame
=
&
vm
->
frames
[
vm
->
fp
++
];
frame
->
func
=
func
;
frame
->
ip
=
0
;
frame
->
bp
=
vm
->
sp
;
frame
->
nlocals
=
func
->
nlocals
;
// Initialize locals with arguments
for
(
int
i
=
0
;
i
<
func
->
nlocals
;
i
++
)
{
frame
->
locals
[
i
]
=
(
i
<
nargs
&&
args
)
?
args
[
i
]
:
js_mkundef
();
}
jsval_t
result
=
bc_vm_execute
(
vm
,
func
->
code
,
func
->
code_len
,
func
->
constants
,
func
->
const_len
,
func
->
names
,
func
->
names_len
,
NULL
,
0
,
frame
->
locals
,
frame
->
nlocals
);
vm
->
fp
--
;
return
result
;
}
// Main execution loop - computed goto (threaded code) dispatch
static
jsval_t
bc_vm_execute
(
bc_vm_t
*
vm
,
bc_inst_t
*
code
,
uint32_t
code_len
,
jsval_t
*
constants
,
uint32_t
const_len
,
char
**
names
,
uint32_t
names_len
,
bc_func_t
**
functions
,
uint32_t
func_len
,
jsval_t
*
locals
,
uint8_t
nlocals
)
{
struct
js
*
js
=
vm
->
js
;
register
bc_inst_t
*
ip
=
code
;
bc_inst_t
*
ip_end
=
code
+
code_len
;
// Stack operations - inlined for speed
jsval_t
*
stack
=
vm
->
stack
;
register
uint32_t
sp
=
vm
->
sp
;
#define PUSH(v) (stack[sp++] = (v))
#define POP() (stack[--sp])
#define PEEK(n) (stack[sp - 1 - (n)])
#define TOP() (stack[sp - 1])
// Dispatch table - only initialize used opcodes, rest are NULL
static
const
void
*
dispatch
[
256
]
=
{
[
OP_NOP
]
=
&&
L_NOP
,
[
OP_POP
]
=
&&
L_POP
,
[
OP_DUP
]
=
&&
L_DUP
,
[
OP_SWAP
]
=
&&
L_SWAP
,
[
OP_ROT3
]
=
&&
L_ROT3
,
[
OP_CONST
]
=
&&
L_CONST
,
[
OP_UNDEF
]
=
&&
L_UNDEF
,
[
OP_NULL
]
=
&&
L_NULL
,
[
OP_TRUE
]
=
&&
L_TRUE
,
[
OP_FALSE
]
=
&&
L_FALSE
,
[
OP_ZERO
]
=
&&
L_ZERO
,
[
OP_ONE
]
=
&&
L_ONE
,
[
OP_IMM_I8
]
=
&&
L_IMM_I8
,
[
OP_IMM_I24
]
=
&&
L_IMM_I24
,
[
OP_ADD
]
=
&&
L_ADD
,
[
OP_SUB
]
=
&&
L_SUB
,
[
OP_MUL
]
=
&&
L_MUL
,
[
OP_DIV
]
=
&&
L_DIV
,
[
OP_MOD
]
=
&&
L_MOD
,
[
OP_EXP
]
=
&&
L_EXP
,
[
OP_NEG
]
=
&&
L_NEG
,
[
OP_POS
]
=
&&
L_POS
,
[
OP_INC
]
=
&&
L_INC
,
[
OP_DEC
]
=
&&
L_DEC
,
[
OP_BITOR
]
=
&&
L_BITOR
,
[
OP_BITAND
]
=
&&
L_BITAND
,
[
OP_BITXOR
]
=
&&
L_BITXOR
,
[
OP_BITNOT
]
=
&&
L_BITNOT
,
[
OP_SHL
]
=
&&
L_SHL
,
[
OP_SHR
]
=
&&
L_SHR
,
[
OP_USHR
]
=
&&
L_USHR
,
[
OP_EQ
]
=
&&
L_EQ
,
[
OP_NE
]
=
&&
L_NE
,
[
OP_SEQ
]
=
&&
L_SEQ
,
[
OP_SNE
]
=
&&
L_SNE
,
[
OP_LT
]
=
&&
L_LT
,
[
OP_LE
]
=
&&
L_LE
,
[
OP_GT
]
=
&&
L_GT
,
[
OP_GE
]
=
&&
L_GE
,
[
OP_NOT
]
=
&&
L_NOT
,
[
OP_VOID
]
=
&&
L_VOID
,
[
OP_JMP
]
=
&&
L_JMP
,
[
OP_JMP_REL
]
=
&&
L_JMP_REL
,
[
OP_JT
]
=
&&
L_JT
,
[
OP_JF
]
=
&&
L_JF
,
[
OP_JT_REL
]
=
&&
L_JT_REL
,
[
OP_JF_REL
]
=
&&
L_JF_REL
,
[
OP_LOOP
]
=
&&
L_LOOP
,
[
OP_LOAD_LOCAL
]
=
&&
L_LOAD_LOCAL
,
[
OP_STORE_LOCAL
]
=
&&
L_STORE_LOCAL
,
[
OP_STORE_LOCAL_KEEP
]
=
&&
L_STORE_LOCAL_KEEP
,
[
OP_LOAD_GLOBAL
]
=
&&
L_LOAD_GLOBAL
,
[
OP_STORE_GLOBAL
]
=
&&
L_STORE_GLOBAL
,
[
OP_NEW_OBJ
]
=
&&
L_NEW_OBJ
,
[
OP_NEW_ARR
]
=
&&
L_NEW_ARR
,
[
OP_GET_PROP
]
=
&&
L_GET_PROP
,
[
OP_SET_PROP
]
=
&&
L_SET_PROP
,
[
OP_GET_ELEM
]
=
&&
L_GET_ELEM
,
[
OP_SET_ELEM
]
=
&&
L_SET_ELEM
,
[
OP_CALL
]
=
&&
L_CALL
,
[
OP_CLOSURE
]
=
&&
L_CLOSURE
,
[
OP_RET
]
=
&&
L_RET
,
[
OP_RET_UNDEF
]
=
&&
L_RET_UNDEF
,
[
OP_THIS
]
=
&&
L_THIS
,
[
OP_HALT
]
=
&&
L_HALT
,
};
bc_inst_t
inst
;
uint32_t
arg
;
int32_t
sarg
;
#define FETCH() do { \
inst = *ip++; \
arg = BC_ARG(inst); \
} while(0)
#define DISPATCH() do { \
if (ip >= ip_end) goto L_DONE; \
FETCH(); \
const void *target = dispatch[BC_OP(inst)]; \
if (target) goto *target; \
goto L_UNKNOWN; \
} while(0)
// Start dispatch
DISPATCH
();
// ===== Stack operations =====
L_NOP
:
DISPATCH
();
L_POP
:
--
sp
;
DISPATCH
();
L_DUP
:
stack
[
sp
]
=
stack
[
sp
-
1
];
sp
++
;
DISPATCH
();
L_SWAP
:
{
jsval_t
tmp
=
stack
[
sp
-
1
];
stack
[
sp
-
1
]
=
stack
[
sp
-
2
];
stack
[
sp
-
2
]
=
tmp
;
DISPATCH
();
}
L_ROT3
:
{
jsval_t
c
=
stack
[
sp
-
1
];
jsval_t
b
=
stack
[
sp
-
2
];
jsval_t
a
=
stack
[
sp
-
3
];
stack
[
sp
-
3
]
=
b
;
stack
[
sp
-
2
]
=
c
;
stack
[
sp
-
1
]
=
a
;
DISPATCH
();
}
// ===== Constants =====
L_CONST
:
PUSH
(
arg
<
const_len
?
constants
[
arg
]
:
js_mkundef
());
DISPATCH
();
L_UNDEF
:
PUSH
(
js_mkundef
());
DISPATCH
();
L_NULL
:
PUSH
(
js_mknull
());
DISPATCH
();
L_TRUE
:
PUSH
(
js_mktrue
());
DISPATCH
();
L_FALSE
:
PUSH
(
js_mkfalse
());
DISPATCH
();
L_ZERO
:
PUSH
(
js_mknum
(
0
));
DISPATCH
();
L_ONE
:
PUSH
(
js_mknum
(
1
));
DISPATCH
();
L_IMM_I8
:
PUSH
(
js_mknum
((
int8_t
)(
arg
&
0xFF
)));
DISPATCH
();
L_IMM_I24
:
sarg
=
BC_SARG
(
inst
);
PUSH
(
js_mknum
(
sarg
));
DISPATCH
();
// ===== Local/Global variables =====
L_LOAD_LOCAL
:
PUSH
(
arg
<
nlocals
?
locals
[
arg
]
:
js_mkundef
());
DISPATCH
();
L_STORE_LOCAL
:
if
(
arg
<
nlocals
)
locals
[
arg
]
=
POP
();
else
--
sp
;
DISPATCH
();
L_STORE_LOCAL_KEEP
:
if
(
arg
<
nlocals
)
locals
[
arg
]
=
TOP
();
DISPATCH
();
L_LOAD_GLOBAL
:
PUSH
(
vm
->
globals
[
arg
]);
DISPATCH
();
L_STORE_GLOBAL
:
vm
->
globals
[
arg
]
=
POP
();
if
(
arg
>=
vm
->
nglobals
)
vm
->
nglobals
=
(
uint8_t
)(
arg
+
1
);
DISPATCH
();
// ===== Arithmetic =====
L_ADD
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
(
js_getnum
(
a
)
+
js_getnum
(
b
)));
DISPATCH
();
}
L_SUB
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
(
js_getnum
(
a
)
-
js_getnum
(
b
)));
DISPATCH
();
}
L_MUL
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
(
js_getnum
(
a
)
*
js_getnum
(
b
)));
DISPATCH
();
}
L_DIV
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
(
js_getnum
(
a
)
/
js_getnum
(
b
)));
DISPATCH
();
}
L_MOD
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
(
fmod
(
js_getnum
(
a
),
js_getnum
(
b
))));
DISPATCH
();
}
L_EXP
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
(
pow
(
js_getnum
(
a
),
js_getnum
(
b
))));
DISPATCH
();
}
L_NEG
:
stack
[
sp
-
1
]
=
js_mknum
(
-
js_getnum
(
stack
[
sp
-
1
]));
DISPATCH
();
L_POS
:
stack
[
sp
-
1
]
=
js_mknum
(
js_getnum
(
stack
[
sp
-
1
]));
DISPATCH
();
L_INC
:
stack
[
sp
-
1
]
=
js_mknum
(
js_getnum
(
stack
[
sp
-
1
])
+
1
);
DISPATCH
();
L_DEC
:
stack
[
sp
-
1
]
=
js_mknum
(
js_getnum
(
stack
[
sp
-
1
])
-
1
);
DISPATCH
();
// ===== Bitwise =====
L_BITOR
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
((
double
)(
js_to_int32
(
js_getnum
(
a
))
|
js_to_int32
(
js_getnum
(
b
)))));
DISPATCH
();
}
L_BITAND
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
((
double
)(
js_to_int32
(
js_getnum
(
a
))
&
js_to_int32
(
js_getnum
(
b
)))));
DISPATCH
();
}
L_BITXOR
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_mknum
((
double
)(
js_to_int32
(
js_getnum
(
a
))
^
js_to_int32
(
js_getnum
(
b
)))));
DISPATCH
();
}
L_BITNOT
:
{
jsval_t
a
=
POP
();
PUSH
(
js_mknum
((
double
)(
~
js_to_int32
(
js_getnum
(
a
)))));
DISPATCH
();
}
L_SHL
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
int32_t
shift
=
js_to_uint32
(
js_getnum
(
b
))
&
0x1F
;
PUSH
(
js_mknum
((
double
)(
js_to_int32
(
js_getnum
(
a
))
<<
shift
)));
DISPATCH
();
}
L_SHR
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
int32_t
shift
=
js_to_uint32
(
js_getnum
(
b
))
&
0x1F
;
PUSH
(
js_mknum
((
double
)(
js_to_int32
(
js_getnum
(
a
))
>>
shift
)));
DISPATCH
();
}
L_USHR
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
uint32_t
shift
=
js_to_uint32
(
js_getnum
(
b
))
&
0x1F
;
PUSH
(
js_mknum
((
double
)(
js_to_uint32
(
js_getnum
(
a
))
>>
shift
)));
DISPATCH
();
}
// ===== Comparisons =====
L_LT
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_getnum
(
a
)
<
js_getnum
(
b
)
?
js_mktrue
()
:
js_mkfalse
());
DISPATCH
();
}
L_LE
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_getnum
(
a
)
<=
js_getnum
(
b
)
?
js_mktrue
()
:
js_mkfalse
());
DISPATCH
();
}
L_GT
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_getnum
(
a
)
>
js_getnum
(
b
)
?
js_mktrue
()
:
js_mkfalse
());
DISPATCH
();
}
L_GE
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
PUSH
(
js_getnum
(
a
)
>=
js_getnum
(
b
)
?
js_mktrue
()
:
js_mkfalse
());
DISPATCH
();
}
L_EQ
:
L_SEQ
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
bool
eq
=
(
vtype
(
a
)
==
vtype
(
b
));
if
(
eq
)
{
if
(
vtype
(
a
)
==
5
)
eq
=
js_getnum
(
a
)
==
js_getnum
(
b
);
else
eq
=
(
a
==
b
);
}
PUSH
(
eq
?
js_mktrue
()
:
js_mkfalse
());
DISPATCH
();
}
L_NE
:
L_SNE
:
{
jsval_t
b
=
POP
();
jsval_t
a
=
POP
();
bool
eq
=
(
vtype
(
a
)
==
vtype
(
b
));
if
(
eq
)
{
if
(
vtype
(
a
)
==
5
)
eq
=
js_getnum
(
a
)
==
js_getnum
(
b
);
else
eq
=
(
a
==
b
);
}
PUSH
(
!
eq
?
js_mktrue
()
:
js_mkfalse
());
DISPATCH
();
}
// ===== Logical =====
L_NOT
:
{
jsval_t
a
=
POP
();
PUSH
(
!
js_truthy
(
js
,
a
)
?
js_mktrue
()
:
js_mkfalse
());
DISPATCH
();
}
L_VOID
:
--
sp
;
PUSH
(
js_mkundef
());
DISPATCH
();
// ===== Control flow =====
L_JMP
:
ip
=
code
+
arg
;
DISPATCH
();
L_JMP_REL
:
sarg
=
BC_SARG
(
inst
);
ip
+=
sarg
;
DISPATCH
();
L_LOOP
:
ip
=
code
+
arg
;
DISPATCH
();
L_JT
:
{
jsval_t
cond
=
POP
();
if
(
js_truthy
(
js
,
cond
))
ip
=
code
+
arg
;
DISPATCH
();
}
L_JF
:
{
jsval_t
cond
=
POP
();
if
(
!
js_truthy
(
js
,
cond
))
ip
=
code
+
arg
;
DISPATCH
();
}
L_JT_REL
:
{
jsval_t
cond
=
POP
();
sarg
=
BC_SARG
(
inst
);
if
(
js_truthy
(
js
,
cond
))
ip
+=
sarg
;
DISPATCH
();
}
L_JF_REL
:
{
jsval_t
cond
=
POP
();
sarg
=
BC_SARG
(
inst
);
if
(
!
js_truthy
(
js
,
cond
))
ip
+=
sarg
;
DISPATCH
();
}
// ===== Function calls =====
L_CALL
:
{
int
nargs_call
=
(
int
)
arg
;
jsval_t
*
call_args
=
(
nargs_call
>
0
)
?
&
stack
[
sp
-
nargs_call
]
:
NULL
;
jsval_t
func_val
=
stack
[
sp
-
nargs_call
-
1
];
sp
-=
(
nargs_call
+
1
);
vm
->
sp
=
sp
;
// sync before call
jsval_t
result
;
if
(
vtype
(
func_val
)
==
5
)
{
// T_NUM - bytecode function index
int
func_idx
=
(
int
)
js_getnum
(
func_val
);
if
(
functions
&&
(
uint32_t
)
func_idx
<
func_len
)
{
result
=
bc_vm_call
(
vm
,
functions
[
func_idx
],
call_args
,
nargs_call
);
}
else
{
result
=
js_mkundef
();
}
}
else
{
result
=
js_call
(
js
,
func_val
,
call_args
,
nargs_call
);
}
sp
=
vm
->
sp
;
// sync after call
PUSH
(
result
);
DISPATCH
();
}
L_CLOSURE
:
PUSH
(
js_mknum
((
double
)
arg
));
DISPATCH
();
// ===== Return =====
L_RET
:
{
jsval_t
result
=
POP
();
vm
->
sp
=
sp
;
return
result
;
}
L_RET_UNDEF
:
vm
->
sp
=
sp
;
return
js_mkundef
();
// ===== Objects =====
L_NEW_OBJ
:
vm
->
sp
=
sp
;
PUSH
(
js_mkobj
(
js
));
DISPATCH
();
L_NEW_ARR
:
vm
->
sp
=
sp
;
PUSH
(
js_mkarr
(
js
));
DISPATCH
();
L_GET_PROP
:
{
jsval_t
obj
=
POP
();
vm
->
sp
=
sp
;
jsval_t
val
=
(
arg
<
names_len
)
?
js_get
(
js
,
obj
,
names
[
arg
])
:
js_mkundef
();
PUSH
(
val
);
DISPATCH
();
}
L_SET_PROP
:
{
jsval_t
val
=
POP
();
jsval_t
obj
=
POP
();
vm
->
sp
=
sp
;
if
(
arg
<
names_len
)
js_set
(
js
,
obj
,
names
[
arg
],
val
);
PUSH
(
val
);
DISPATCH
();
}
L_GET_ELEM
:
{
jsval_t
key
=
POP
();
jsval_t
obj
=
POP
();
vm
->
sp
=
sp
;
if
(
vtype
(
key
)
==
5
)
{
char
buf
[
32
];
snprintf
(
buf
,
sizeof
(
buf
),
"%.0f"
,
js_getnum
(
key
));
PUSH
(
js_get
(
js
,
obj
,
buf
));
}
else
{
PUSH
(
js_mkundef
());
}
DISPATCH
();
}
L_SET_ELEM
:
{
jsval_t
val
=
POP
();
jsval_t
key
=
POP
();
jsval_t
obj
=
POP
();
vm
->
sp
=
sp
;
if
(
vtype
(
key
)
==
5
)
{
char
buf
[
32
];
snprintf
(
buf
,
sizeof
(
buf
),
"%.0f"
,
js_getnum
(
key
));
js_set
(
js
,
obj
,
buf
,
val
);
}
PUSH
(
val
);
DISPATCH
();
}
L_THIS
:
PUSH
(
js_getthis
(
js
));
DISPATCH
();
L_HALT
:
goto
L_DONE
;
L_UNKNOWN
:
fprintf
(
stderr
,
"Unknown opcode: 0x%02X
\n
"
,
BC_OP
(
inst
));
vm
->
sp
=
sp
;
return
js_mkerr
(
js
,
"Unknown opcode"
);
L_DONE
:
vm
->
sp
=
sp
;
return
(
sp
>
0
)
?
stack
[
sp
-
1
]
:
js_mkundef
();
#undef PUSH
#undef POP
#undef PEEK
#undef TOP
#undef FETCH
#undef DISPATCH
}
// Run a chunk (top-level code)
jsval_t
bc_vm_run
(
bc_vm_t
*
vm
,
bc_chunk_t
*
chunk
)
{
if
(
!
vm
||
!
chunk
||
!
chunk
->
code
)
{
return
js_mkerr
(
vm
->
js
,
"Invalid bytecode"
);
}
bc_frame_t
*
frame
=
&
vm
->
frames
[
0
];
frame
->
func
=
NULL
;
frame
->
chunk
=
chunk
;
frame
->
ip
=
0
;
frame
->
bp
=
vm
->
sp
;
frame
->
nlocals
=
chunk
->
nlocals
;
// Initialize top-level locals
for
(
int
i
=
0
;
i
<
chunk
->
nlocals
;
i
++
)
{
frame
->
locals
[
i
]
=
js_mkundef
();
}
vm
->
fp
=
1
;
return
bc_vm_execute
(
vm
,
chunk
->
code
,
chunk
->
code_len
,
chunk
->
constants
,
chunk
->
const_len
,
chunk
->
names
,
chunk
->
names_len
,
chunk
->
functions
,
chunk
->
func_len
,
frame
->
locals
,
frame
->
nlocals
);
}
// ============================================================================
// Disassembler
// ============================================================================
const
char
*
bc_opcode_name
(
bc_opcode_t
op
)
{
static
const
char
*
names
[]
=
{
// Stack operations
[
OP_NOP
]
=
"NOP"
,
[
OP_POP
]
=
"POP"
,
[
OP_DUP
]
=
"DUP"
,
[
OP_SWAP
]
=
"SWAP"
,
[
OP_ROT3
]
=
"ROT3"
,
[
OP_DUP2
]
=
"DUP2"
,
[
OP_POPN
]
=
"POPN"
,
// Constants
[
OP_CONST
]
=
"CONST"
,
[
OP_UNDEF
]
=
"UNDEF"
,
[
OP_NULL
]
=
"NULL"
,
[
OP_TRUE
]
=
"TRUE"
,
[
OP_FALSE
]
=
"FALSE"
,
[
OP_ZERO
]
=
"ZERO"
,
[
OP_ONE
]
=
"ONE"
,
[
OP_IMM_I8
]
=
"IMM_I8"
,
[
OP_IMM_I24
]
=
"IMM_I24"
,
[
OP_GLOBAL_THIS
]
=
"GLOBAL_THIS"
,
// Arithmetic
[
OP_ADD
]
=
"ADD"
,
[
OP_SUB
]
=
"SUB"
,
[
OP_MUL
]
=
"MUL"
,
[
OP_DIV
]
=
"DIV"
,
[
OP_MOD
]
=
"MOD"
,
[
OP_EXP
]
=
"EXP"
,
[
OP_NEG
]
=
"NEG"
,
[
OP_POS
]
=
"POS"
,
[
OP_INC
]
=
"INC"
,
[
OP_DEC
]
=
"DEC"
,
[
OP_INC_LOCAL
]
=
"INC_LOCAL"
,
[
OP_DEC_LOCAL
]
=
"DEC_LOCAL"
,
[
OP_POST_INC_LOCAL
]
=
"POST_INC_LOCAL"
,
[
OP_POST_DEC_LOCAL
]
=
"POST_DEC_LOCAL"
,
// Bitwise
[
OP_BITOR
]
=
"BITOR"
,
[
OP_BITAND
]
=
"BITAND"
,
[
OP_BITXOR
]
=
"BITXOR"
,
[
OP_BITNOT
]
=
"BITNOT"
,
[
OP_SHL
]
=
"SHL"
,
[
OP_SHR
]
=
"SHR"
,
[
OP_USHR
]
=
"USHR"
,
// Comparison
[
OP_EQ
]
=
"EQ"
,
[
OP_NE
]
=
"NE"
,
[
OP_SEQ
]
=
"SEQ"
,
[
OP_SNE
]
=
"SNE"
,
[
OP_LT
]
=
"LT"
,
[
OP_LE
]
=
"LE"
,
[
OP_GT
]
=
"GT"
,
[
OP_GE
]
=
"GE"
,
// Logical
[
OP_NOT
]
=
"NOT"
,
[
OP_TYPEOF
]
=
"TYPEOF"
,
[
OP_INSTANCEOF
]
=
"INSTANCEOF"
,
[
OP_IN
]
=
"IN"
,
[
OP_VOID
]
=
"VOID"
,
[
OP_DELETE
]
=
"DELETE"
,
// Control flow
[
OP_JMP
]
=
"JMP"
,
[
OP_JMP_REL
]
=
"JMP_REL"
,
[
OP_JT
]
=
"JT"
,
[
OP_JF
]
=
"JF"
,
[
OP_JT_REL
]
=
"JT_REL"
,
[
OP_JF_REL
]
=
"JF_REL"
,
[
OP_JNULLISH
]
=
"JNULLISH"
,
[
OP_LOOP
]
=
"LOOP"
,
[
OP_JT_KEEP
]
=
"JT_KEEP"
,
[
OP_JF_KEEP
]
=
"JF_KEEP"
,
[
OP_CASE
]
=
"CASE"
,
[
OP_JNULLISH_POP
]
=
"JNULLISH_POP"
,
// Variables
[
OP_LOAD_LOCAL
]
=
"LOAD_LOCAL"
,
[
OP_STORE_LOCAL
]
=
"STORE_LOCAL"
,
[
OP_LOAD_GLOBAL
]
=
"LOAD_GLOBAL"
,
[
OP_STORE_GLOBAL
]
=
"STORE_GLOBAL"
,
[
OP_LOAD_UPVAL
]
=
"LOAD_UPVAL"
,
[
OP_STORE_UPVAL
]
=
"STORE_UPVAL"
,
[
OP_LOAD_NAME
]
=
"LOAD_NAME"
,
[
OP_STORE_NAME
]
=
"STORE_NAME"
,
[
OP_DEF_VAR
]
=
"DEF_VAR"
,
[
OP_DEF_LET
]
=
"DEF_LET"
,
[
OP_DEF_CONST
]
=
"DEF_CONST"
,
[
OP_DEL_NAME
]
=
"DEL_NAME"
,
[
OP_STORE_LOCAL_KEEP
]
=
"STORE_LOCAL_KEEP"
,
[
OP_STORE_NAME_KEEP
]
=
"STORE_NAME_KEEP"
,
// Objects & Arrays
[
OP_NEW_OBJ
]
=
"NEW_OBJ"
,
[
OP_NEW_ARR
]
=
"NEW_ARR"
,
[
OP_GET_PROP
]
=
"GET_PROP"
,
[
OP_SET_PROP
]
=
"SET_PROP"
,
[
OP_GET_ELEM
]
=
"GET_ELEM"
,
[
OP_SET_ELEM
]
=
"SET_ELEM"
,
[
OP_DEL_PROP
]
=
"DEL_PROP"
,
[
OP_DEL_ELEM
]
=
"DEL_ELEM"
,
[
OP_INIT_PROP
]
=
"INIT_PROP"
,
[
OP_INIT_ELEM
]
=
"INIT_ELEM"
,
[
OP_SPREAD
]
=
"SPREAD"
,
[
OP_GET_SUPER
]
=
"GET_SUPER"
,
[
OP_SET_SUPER
]
=
"SET_SUPER"
,
[
OP_GET_PROP_OPT
]
=
"GET_PROP_OPT"
,
[
OP_GET_ELEM_OPT
]
=
"GET_ELEM_OPT"
,
[
OP_INIT_COMPUTED
]
=
"INIT_COMPUTED"
,
// Functions
[
OP_CALL
]
=
"CALL"
,
[
OP_CALL_METHOD
]
=
"CALL_METHOD"
,
[
OP_NEW
]
=
"NEW"
,
[
OP_RET
]
=
"RET"
,
[
OP_RET_UNDEF
]
=
"RET_UNDEF"
,
[
OP_CLOSURE
]
=
"CLOSURE"
,
[
OP_THIS
]
=
"THIS"
,
[
OP_ARGS
]
=
"ARGS"
,
[
OP_REST
]
=
"REST"
,
[
OP_SUPER
]
=
"SUPER"
,
[
OP_NEW_TARGET
]
=
"NEW_TARGET"
,
[
OP_CALL_OPT
]
=
"CALL_OPT"
,
[
OP_SUPER_CALL
]
=
"SUPER_CALL"
,
[
OP_SPREAD_ARG
]
=
"SPREAD_ARG"
,
// Iterators & Generators
[
OP_GET_ITER
]
=
"GET_ITER"
,
[
OP_ITER_NEXT
]
=
"ITER_NEXT"
,
[
OP_FOR_IN
]
=
"FOR_IN"
,
[
OP_FOR_OF
]
=
"FOR_OF"
,
[
OP_YIELD
]
=
"YIELD"
,
[
OP_YIELD_STAR
]
=
"YIELD_STAR"
,
[
OP_AWAIT
]
=
"AWAIT"
,
[
OP_ITER_CLOSE
]
=
"ITER_CLOSE"
,
[
OP_ASYNC_ITER
]
=
"ASYNC_ITER"
,
// Exception handling
[
OP_THROW
]
=
"THROW"
,
[
OP_TRY_START
]
=
"TRY_START"
,
[
OP_TRY_END
]
=
"TRY_END"
,
[
OP_CATCH
]
=
"CATCH"
,
[
OP_FINALLY
]
=
"FINALLY"
,
[
OP_RETHROW
]
=
"RETHROW"
,
[
OP_EXCEPTION
]
=
"EXCEPTION"
,
// Scope
[
OP_PUSH_SCOPE
]
=
"PUSH_SCOPE"
,
[
OP_POP_SCOPE
]
=
"POP_SCOPE"
,
[
OP_PUSH_WITH
]
=
"PUSH_WITH"
,
[
OP_POP_WITH
]
=
"POP_WITH"
,
// Classes
[
OP_CLASS
]
=
"CLASS"
,
[
OP_CLASS_EXTEND
]
=
"CLASS_EXTEND"
,
[
OP_INIT_METHOD
]
=
"INIT_METHOD"
,
[
OP_INIT_GETTER
]
=
"INIT_GETTER"
,
[
OP_INIT_SETTER
]
=
"INIT_SETTER"
,
[
OP_INIT_STATIC
]
=
"INIT_STATIC"
,
[
OP_INIT_STATIC_GET
]
=
"INIT_STATIC_GET"
,
[
OP_INIT_STATIC_SET
]
=
"INIT_STATIC_SET"
,
[
OP_INIT_FIELD
]
=
"INIT_FIELD"
,
[
OP_INIT_PRIVATE
]
=
"INIT_PRIVATE"
,
// Template literals
[
OP_TEMPLATE
]
=
"TEMPLATE"
,
[
OP_TAGGED_TEMPLATE
]
=
"TAGGED_TEMPLATE"
,
// Destructuring
[
OP_UNPACK_ARR
]
=
"UNPACK_ARR"
,
[
OP_UNPACK_OBJ
]
=
"UNPACK_OBJ"
,
[
OP_UNPACK_REST
]
=
"UNPACK_REST"
,
// Misc
[
OP_DEBUG
]
=
"DEBUG"
,
[
OP_LINE
]
=
"LINE"
,
[
OP_COL
]
=
"COL"
,
[
OP_FILENAME
]
=
"FILENAME"
,
[
OP_HALT
]
=
"HALT"
,
};
if
(
op
<
sizeof
(
names
)
/
sizeof
(
names
[
0
])
&&
names
[
op
])
return
names
[
op
];
return
"???"
;
}
static
void
disasm_inst
(
bc_inst_t
inst
,
uint32_t
offset
,
char
**
names
,
uint32_t
names_len
)
{
uint8_t
op
=
BC_OP
(
inst
);
uint32_t
arg
=
BC_ARG
(
inst
);
int32_t
sarg
=
BC_SARG
(
inst
);
printf
(
"%04u %-16s"
,
offset
,
bc_opcode_name
(
op
));
switch
(
op
)
{
// Constant pool reference
case
OP_CONST
:
printf
(
" @%u"
,
arg
);
break
;
// Immediates
case
OP_IMM_I8
:
case
OP_IMM_I24
:
printf
(
" %d"
,
sarg
);
break
;
case
OP_POPN
:
case
OP_TEMPLATE
:
printf
(
" %u"
,
arg
);
break
;
// Absolute jumps
case
OP_JMP
:
case
OP_JT
:
case
OP_JF
:
case
OP_LOOP
:
case
OP_JT_KEEP
:
case
OP_JF_KEEP
:
case
OP_JNULLISH
:
case
OP_JNULLISH_POP
:
case
OP_CASE
:
printf
(
" -> %u"
,
arg
);
break
;
// Relative jumps
case
OP_JMP_REL
:
case
OP_JT_REL
:
case
OP_JF_REL
:
printf
(
" %+d -> %u"
,
sarg
,
(
uint32_t
)((
int32_t
)
offset
+
1
+
sarg
));
break
;
// Local variable slots
case
OP_LOAD_LOCAL
:
case
OP_STORE_LOCAL
:
case
OP_STORE_LOCAL_KEEP
:
case
OP_INC_LOCAL
:
case
OP_DEC_LOCAL
:
case
OP_POST_INC_LOCAL
:
case
OP_POST_DEC_LOCAL
:
case
OP_CATCH
:
case
OP_REST
:
printf
(
" $%u"
,
arg
);
break
;
// Global/upvalue slots
case
OP_LOAD_GLOBAL
:
case
OP_STORE_GLOBAL
:
case
OP_LOAD_UPVAL
:
case
OP_STORE_UPVAL
:
printf
(
" $%u"
,
arg
);
break
;
// Name table references
case
OP_GET_PROP
:
case
OP_SET_PROP
:
case
OP_GET_PROP_OPT
:
case
OP_LOAD_NAME
:
case
OP_STORE_NAME
:
case
OP_STORE_NAME_KEEP
:
case
OP_DEL_PROP
:
case
OP_DEL_NAME
:
case
OP_INIT_PROP
:
case
OP_INIT_METHOD
:
case
OP_INIT_GETTER
:
case
OP_INIT_SETTER
:
case
OP_INIT_STATIC
:
case
OP_INIT_STATIC_GET
:
case
OP_INIT_STATIC_SET
:
case
OP_GET_SUPER
:
case
OP_SET_SUPER
:
case
OP_DEF_VAR
:
case
OP_DEF_LET
:
case
OP_DEF_CONST
:
printf
(
" '%s'"
,
(
arg
<
names_len
)
?
names
[
arg
]
:
"?"
);
break
;
// Call with arg count
case
OP_CALL
:
case
OP_CALL_METHOD
:
case
OP_CALL_OPT
:
case
OP_NEW
:
case
OP_SUPER_CALL
:
printf
(
" (%u args)"
,
arg
);
break
;
// Function pool reference
case
OP_CLOSURE
:
case
OP_CLASS
:
printf
(
" func[%u]"
,
arg
);
break
;
// Exception handling
case
OP_TRY_START
:
printf
(
" catch-> %u"
,
arg
);
break
;
// Destructuring
case
OP_UNPACK_ARR
:
case
OP_UNPACK_OBJ
:
printf
(
" %u elements"
,
arg
);
break
;
// Source info
case
OP_LINE
:
case
OP_COL
:
case
OP_FILENAME
:
printf
(
" %u"
,
arg
);
break
;
default
:
if
(
arg
)
printf
(
" %u"
,
arg
);
break
;
}
printf
(
"
\n
"
);
}
void
bc_disassemble_func_inst
(
bc_func_t
*
func
,
uint32_t
offset
)
{
if
(
offset
<
func
->
code_len
)
{
disasm_inst
(
func
->
code
[
offset
],
offset
,
func
->
names
,
func
->
names_len
);
}
}
void
bc_disassemble_inst
(
bc_chunk_t
*
chunk
,
uint32_t
offset
)
{
if
(
offset
<
chunk
->
code_len
)
{
disasm_inst
(
chunk
->
code
[
offset
],
offset
,
chunk
->
names
,
chunk
->
names_len
);
}
}
void
bc_disassemble_func
(
bc_func_t
*
func
)
{
printf
(
"--- function %s (arity=%d, locals=%d) ---
\n
"
,
func
->
name
?
func
->
name
:
"<anonymous>"
,
func
->
arity
,
func
->
nlocals
);
for
(
uint32_t
i
=
0
;
i
<
func
->
code_len
;
i
++
)
{
bc_disassemble_func_inst
(
func
,
i
);
}
printf
(
"
\n
"
);
}
void
bc_disassemble
(
bc_chunk_t
*
chunk
,
const
char
*
name
)
{
printf
(
"=== %s ===
\n
"
,
name
?
name
:
"chunk"
);
if
(
chunk
->
func_len
>
0
)
{
printf
(
"Functions:
\n
"
);
for
(
uint32_t
i
=
0
;
i
<
chunk
->
func_len
;
i
++
)
{
printf
(
" [%u] %s
\n
"
,
i
,
chunk
->
functions
[
i
]
->
name
?
chunk
->
functions
[
i
]
->
name
:
"<anon>"
);
}
}
printf
(
"Code (locals=%d):
\n
"
,
chunk
->
nlocals
);
for
(
uint32_t
i
=
0
;
i
<
chunk
->
code_len
;
i
++
)
{
bc_disassemble_inst
(
chunk
,
i
);
}
printf
(
"
\n
"
);
for
(
uint32_t
i
=
0
;
i
<
chunk
->
func_len
;
i
++
)
{
bc_disassemble_func
(
chunk
->
functions
[
i
]);
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Thu, Mar 26, 9:12 PM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
512250
Default Alt Text
bytecode.c (27 KB)
Attached To
Mode
rANT Ant
Attached
Detach File
Event Timeline
Log In to Comment