@ -4,6 +4,9 @@ use crate::{
builtins ::function ::ThisMode ,
syntax ::ast ::{
node ::{
declaration ::{ BindingPatternTypeArray , BindingPatternTypeObject , DeclarationPattern } ,
iteration ::IterableLoopInitializer ,
template ::TemplateElement ,
Declaration , GetConstField , GetField , MethodDefinitionKind , PropertyDefinition ,
PropertyName , StatementList ,
} ,
@ -31,10 +34,17 @@ struct Label {
struct JumpControlInfo {
label : Option < Box < str > > ,
start_address : u32 ,
is_loop : bool ,
kind : JumpControlInfoKind ,
breaks : Vec < Label > ,
}
#[ derive(Debug, Clone, PartialEq) ]
enum JumpControlInfoKind {
Loop ,
Switch ,
Try ,
}
#[ derive(Debug, Clone, Copy) ]
enum Access < ' a > {
Variable { index : u32 } ,
@ -231,7 +241,7 @@ impl ByteCompiler {
self . jump_info . push ( JumpControlInfo {
label ,
start_address ,
is_loop : true ,
kind : JumpControlInfoKind ::Loop ,
breaks : Vec ::new ( ) ,
} )
}
@ -240,7 +250,7 @@ impl ByteCompiler {
fn pop_loop_control_info ( & mut self ) {
let loop_info = self . jump_info . pop ( ) . unwrap ( ) ;
assert! ( loop_info . is_l oop) ;
assert! ( loop_info . kind = = JumpControlInfoKind ::L oop) ;
for label in loop_info . breaks {
self . patch_jump ( label ) ;
@ -252,7 +262,7 @@ impl ByteCompiler {
self . jump_info . push ( JumpControlInfo {
label ,
start_address ,
is_loop : false ,
kind : JumpControlInfoKind ::Switch ,
breaks : Vec ::new ( ) ,
} )
}
@ -261,13 +271,48 @@ impl ByteCompiler {
fn pop_switch_control_info ( & mut self ) {
let info = self . jump_info . pop ( ) . unwrap ( ) ;
assert! ( ! info . is_loop ) ;
assert! ( info . kind = = JumpControlInfoKind ::Switch ) ;
for label in info . breaks {
self . patch_jump ( label ) ;
}
}
#[ inline ]
fn push_try_control_info ( & mut self ) {
self . jump_info . push ( JumpControlInfo {
label : None ,
start_address : u32 ::MAX ,
kind : JumpControlInfoKind ::Try ,
breaks : Vec ::new ( ) ,
} )
}
#[ inline ]
fn pop_try_control_info ( & mut self , finally_start_address : Option < u32 > ) {
let mut info = self . jump_info . pop ( ) . unwrap ( ) ;
assert! ( info . kind = = JumpControlInfoKind ::Try ) ;
if let Some ( finally_start_address ) = finally_start_address {
let mut breaks = Vec ::with_capacity ( info . breaks . len ( ) ) ;
let finally_end = self . jump_with_custom_opcode ( Opcode ::FinallyJump ) ;
for label in info . breaks {
if label . index < finally_start_address {
self . patch_jump_with_target ( label , finally_start_address ) ;
breaks . push ( finally_end ) ;
} else {
breaks . push ( label ) ;
}
}
if let Some ( jump_info ) = self . jump_info . last_mut ( ) {
jump_info . breaks . append ( & mut breaks ) ;
}
} else if let Some ( jump_info ) = self . jump_info . last_mut ( ) {
jump_info . breaks . append ( & mut info . breaks ) ;
}
}
#[ inline ]
fn compile_access < ' a > ( & mut self , node : & ' a Node ) -> Access < ' a > {
match node {
@ -374,7 +419,7 @@ impl ByteCompiler {
self . emit ( Opcode ::Inc , & [ ] ) ;
let access = self . compile_access ( unary . target ( ) ) ;
self . access_set ( access , None , use_expr ) ;
self . access_set ( access , None , true ) ;
None
}
UnaryOp ::DecrementPre = > {
@ -382,7 +427,7 @@ impl ByteCompiler {
self . emit ( Opcode ::Dec , & [ ] ) ;
let access = self . compile_access ( unary . target ( ) ) ;
self . access_set ( access , None , use_expr ) ;
self . access_set ( access , None , true ) ;
None
}
UnaryOp ::IncrementPost = > {
@ -392,10 +437,6 @@ impl ByteCompiler {
let access = self . compile_access ( unary . target ( ) ) ;
self . access_set ( access , None , false ) ;
if ! use_expr {
self . emit ( Opcode ::Pop , & [ ] ) ;
}
None
}
UnaryOp ::DecrementPost = > {
@ -405,10 +446,6 @@ impl ByteCompiler {
let access = self . compile_access ( unary . target ( ) ) ;
self . access_set ( access , None , false ) ;
if ! use_expr {
self . emit ( Opcode ::Pop , & [ ] ) ;
}
None
}
UnaryOp ::Delete = > match unary . target ( ) {
@ -512,10 +549,13 @@ impl ByteCompiler {
self . patch_jump ( exit ) ;
}
LogOp ::Or = > {
self . emit_opcode ( Opcode ::Dup ) ;
let exit = self . jump_with_custom_opcode ( Opcode ::LogicalOr ) ;
self . emit_opcode ( Opcode ::Pop ) ;
self . compile_expr ( binary . rhs ( ) , true ) ;
self . emit ( Opcode ::ToBoolean , & [ ] ) ;
self . emit_opcode ( Opcode ::Dup ) ;
self . patch_jump ( exit ) ;
self . emit_opcode ( Opcode ::Pop ) ;
}
LogOp ::Coalesce = > {
let exit = self . jump_with_custom_opcode ( Opcode ::Coalesce ) ;
@ -706,8 +746,12 @@ impl ByteCompiler {
}
}
}
// TODO: Spread Object
PropertyDefinition ::SpreadObject ( _ ) = > todo! ( ) ,
PropertyDefinition ::SpreadObject ( expr ) = > {
self . compile_expr ( expr , true ) ;
self . emit_opcode ( Opcode ::Swap ) ;
self . emit ( Opcode ::CopyDataProperties , & [ 0 ] ) ;
self . emit_opcode ( Opcode ::Pop ) ;
}
}
}
@ -746,16 +790,17 @@ impl ByteCompiler {
}
}
Node ::ArrayDecl ( array ) = > {
let mut count = 0 ;
for element in array . as_ref ( ) . iter ( ) . rev ( ) {
self . emit_opcode ( Opcode ::PushNewArray ) ;
for element in array . as_ref ( ) {
self . compile_expr ( element , true ) ;
if let Node ::Spread ( _ ) = element {
todo! ( "array with spread element" ) ;
self . emit_opcode ( Opcode ::InitIterator ) ;
self . emit_opcode ( Opcode ::PushIteratorToArray ) ;
} else {
self . compile_expr ( element , true ) ;
self . emit_opcode ( Opcode ::PushValueToArray ) ;
}
count + = 1 ;
}
self . emit ( Opcode ::PushNewArray , & [ count ] ) ;
if ! use_expr {
self . emit ( Opcode ::Pop , & [ ] ) ;
@ -764,13 +809,54 @@ impl ByteCompiler {
Node ::This = > {
self . access_get ( Access ::This , use_expr ) ;
}
Node ::Spread ( spread ) = > self . compile_expr ( spread . val ( ) , true ) ,
Node ::FunctionExpr ( _function ) = > self . function ( expr , use_expr ) ,
Node ::ArrowFunctionDecl ( _function ) = > self . function ( expr , use_expr ) ,
Node ::Call ( call ) = > {
for arg in call . args ( ) . iter ( ) . rev ( ) {
self . compile_expr ( arg , true ) ;
Node ::Call ( _ ) = > self . call ( expr , use_expr ) ,
Node ::New ( _ ) = > self . call ( expr , use_expr ) ,
Node ::TemplateLit ( template_literal ) = > {
for element in template_literal . elements ( ) . iter ( ) . rev ( ) {
match element {
TemplateElement ::String ( s ) = > {
self . emit_push_literal ( Literal ::String ( s . as_ref ( ) . into ( ) ) )
}
TemplateElement ::Expr ( expr ) = > {
self . compile_expr ( expr , true ) ;
}
}
}
match call . expr ( ) {
self . emit (
Opcode ::ConcatToString ,
& [ template_literal . elements ( ) . len ( ) as u32 ] ,
) ;
if ! use_expr {
self . emit ( Opcode ::Pop , & [ ] ) ;
}
}
// TODO: implement AsyncFunctionExpr
Node ::AsyncFunctionExpr ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
// TODO: implement AwaitExpr
Node ::AwaitExpr ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
// TODO: implement GeneratorExpr
Node ::GeneratorExpr ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
// TODO: implement AsyncGeneratorExpr
Node ::AsyncGeneratorExpr ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
// TODO: implement Yield
Node ::Yield ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
Node ::TaggedTemplate ( template ) = > {
match template . tag ( ) {
Node ::GetConstField ( field ) = > {
self . compile_expr ( field . obj ( ) , true ) ;
self . emit ( Opcode ::Dup , & [ ] ) ;
@ -785,17 +871,39 @@ impl ByteCompiler {
self . emit ( Opcode ::GetPropertyByValue , & [ ] ) ;
}
expr = > {
self . emit ( Opcode ::This , & [ ] ) ;
self . compile_expr ( expr , true ) ;
self . emit_opcode ( Opcode ::This ) ;
self . emit_opcode ( Opcode ::Swap ) ;
}
}
self . emit ( Opcode ::Call , & [ call . args ( ) . len ( ) as u32 ] ) ;
if ! use_expr {
self . emit ( Opcode ::Pop , & [ ] ) ;
for expr in template . exprs ( ) . iter ( ) . rev ( ) {
self . compile_expr ( expr , true ) ;
}
self . emit_opcode ( Opcode ::PushNewArray ) ;
for raw in template . raws ( ) {
self . emit_push_literal ( Literal ::String ( raw . as_ref ( ) . into ( ) ) ) ;
self . emit_opcode ( Opcode ::PushValueToArray ) ;
}
self . emit_opcode ( Opcode ::PushNewArray ) ;
for cooked in template . cookeds ( ) {
if let Some ( cooked ) = cooked {
self . emit_push_literal ( Literal ::String ( cooked . as_ref ( ) . into ( ) ) ) ;
} else {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
self . emit_opcode ( Opcode ::PushValueToArray ) ;
}
self . emit_opcode ( Opcode ::Dup ) ;
let index = self . get_or_insert_name ( "raw" ) ;
self . emit ( Opcode ::SetPropertyByName , & [ index ] ) ;
self . emit ( Opcode ::Call , & [ ( template . exprs ( ) . len ( ) + 1 ) as u32 ] ) ;
}
expr = > todo! ( "TODO compile: {}" , expr ) ,
_ = > unreachable! ( ) ,
}
}
@ -806,24 +914,31 @@ impl ByteCompiler {
for decl in list . as_ref ( ) {
match decl {
Declaration ::Identifier { ident , .. } = > {
if ident . as_ref ( ) = = "arguments" {
self . code_block . lexical_name_argument = true ;
}
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::DefVar , & [ index ] ) ;
if let Some ( expr ) = decl . init ( ) {
self . compile_expr ( expr , true ) ;
self . emit ( Opcode ::InitLexical , & [ index ] ) ;
} ;
self . emit ( Opcode ::DefInitVar , & [ index ] ) ;
} else {
self . emit ( Opcode ::DefVar , & [ index ] ) ;
}
}
Declaration ::Pattern ( pattern ) = > {
for ident in pattern . idents ( ) {
let index = self . get_or_insert_name ( ident ) ;
self . emit ( Opcode ::DefVar , & [ index ] ) ;
if let Some ( expr ) = decl . init ( ) {
self . compile_expr ( expr , true ) ;
self . emit ( Opcode ::InitLexical , & [ index ] ) ;
} ;
if pattern . idents ( ) . contains ( & "arguments" ) {
self . code_block . lexical_name_argument = true ;
}
if let Some ( init ) = decl . init ( ) {
self . compile_expr ( init , true ) ;
} else {
self . emit_opcode ( Opcode ::PushUndefined ) ;
} ;
self . compile_declaration_pattern ( pattern , Opcode ::DefInitVar ) ;
}
}
}
@ -832,24 +947,31 @@ impl ByteCompiler {
for decl in list . as_ref ( ) {
match decl {
Declaration ::Identifier { ident , .. } = > {
if ident . as_ref ( ) = = "arguments" {
self . code_block . lexical_name_argument = true ;
}
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::DefLet , & [ index ] ) ;
if let Some ( expr ) = decl . init ( ) {
self . compile_expr ( expr , true ) ;
self . emit ( Opcode ::InitLexical , & [ index ] ) ;
} ;
self . emit ( Opcode ::DefInitLet , & [ index ] ) ;
} else {
self . emit ( Opcode ::DefLet , & [ index ] ) ;
}
}
Declaration ::Pattern ( pattern ) = > {
for ident in pattern . idents ( ) {
let index = self . get_or_insert_name ( ident ) ;
self . emit ( Opcode ::DefLet , & [ index ] ) ;
if let Some ( expr ) = decl . init ( ) {
self . compile_expr ( expr , true ) ;
self . emit ( Opcode ::InitLexical , & [ index ] ) ;
} ;
if pattern . idents ( ) . contains ( & "arguments" ) {
self . code_block . lexical_name_argument = true ;
}
if let Some ( init ) = decl . init ( ) {
self . compile_expr ( init , true ) ;
} else {
self . emit_opcode ( Opcode ::PushUndefined ) ;
} ;
self . compile_declaration_pattern ( pattern , Opcode ::DefInitLet ) ;
}
}
}
@ -858,24 +980,29 @@ impl ByteCompiler {
for decl in list . as_ref ( ) {
match decl {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::DefConst , & [ index ] ) ;
if ident . as_ref ( ) = = "arguments" {
self . code_block . lexical_name_argument = true ;
}
if let Some ( expr ) = decl . init ( ) {
self . compile_expr ( expr , true ) ;
self . emit ( Opcode ::InitLexical , & [ index ] ) ;
} ;
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
let init = decl
. init ( )
. expect ( "const declaration must have initializer" ) ;
self . compile_expr ( init , true ) ;
self . emit ( Opcode ::DefInitConst , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
for ident in pattern . idents ( ) {
let index = self . get_or_insert_name ( ident ) ;
self . emit ( Opcode ::DefConst , & [ index ] ) ;
if let Some ( expr ) = decl . init ( ) {
self . compile_expr ( expr , true ) ;
self . emit ( Opcode ::InitLexical , & [ index ] ) ;
} ;
if pattern . idents ( ) . contains ( & "arguments" ) {
self . code_block . lexical_name_argument = true ;
}
if let Some ( init ) = decl . init ( ) {
self . compile_expr ( init , true ) ;
} else {
self . emit_opcode ( Opcode ::PushUndefined ) ;
} ;
self . compile_declaration_pattern ( pattern , Opcode ::DefInitConst ) ;
}
}
}
@ -898,6 +1025,149 @@ impl ByteCompiler {
}
}
}
Node ::ForLoop ( for_loop ) = > {
self . emit_opcode ( Opcode ::PushDeclarativeEnvironment ) ;
if let Some ( init ) = for_loop . init ( ) {
self . compile_stmt ( init , false ) ;
}
let initial_jump = self . jump ( ) ;
let start_address = self . next_opcode_location ( ) ;
self . push_loop_control_info ( for_loop . label ( ) . map ( Into ::into ) , start_address ) ;
if let Some ( final_expr ) = for_loop . final_expr ( ) {
self . compile_expr ( final_expr , false ) ;
}
self . patch_jump ( initial_jump ) ;
if let Some ( condition ) = for_loop . condition ( ) {
self . compile_expr ( condition , true ) ;
} else {
self . emit_opcode ( Opcode ::PushTrue ) ;
}
let exit = self . jump_if_false ( ) ;
self . compile_stmt ( for_loop . body ( ) , false ) ;
self . emit ( Opcode ::Jump , & [ start_address ] ) ;
self . patch_jump ( exit ) ;
self . pop_loop_control_info ( ) ;
self . emit_opcode ( Opcode ::PopEnvironment ) ;
}
Node ::ForInLoop ( for_in_loop ) = > {
self . compile_expr ( for_in_loop . expr ( ) , true ) ;
let early_exit = self . jump_with_custom_opcode ( Opcode ::ForInLoopInitIterator ) ;
let start_address = self . next_opcode_location ( ) ;
self . push_loop_control_info ( for_in_loop . label ( ) . map ( Into ::into ) , start_address ) ;
self . emit_opcode ( Opcode ::PushDeclarativeEnvironment ) ;
let exit = self . jump_with_custom_opcode ( Opcode ::ForInLoopNext ) ;
match for_in_loop . init ( ) {
IterableLoopInitializer ::Identifier ( ref ident ) = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
IterableLoopInitializer ::Var ( declaration ) = > match declaration {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
self . compile_declaration_pattern ( pattern , Opcode ::DefInitVar ) ;
}
} ,
IterableLoopInitializer ::Let ( declaration ) = > match declaration {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
self . compile_declaration_pattern ( pattern , Opcode ::DefInitLet ) ;
}
} ,
IterableLoopInitializer ::Const ( declaration ) = > match declaration {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
self . compile_declaration_pattern ( pattern , Opcode ::DefInitConst ) ;
}
} ,
}
self . compile_stmt ( for_in_loop . body ( ) , false ) ;
self . emit_opcode ( Opcode ::PopEnvironment ) ;
self . emit ( Opcode ::Jump , & [ start_address ] ) ;
self . pop_loop_control_info ( ) ;
self . emit_opcode ( Opcode ::Pop ) ;
self . emit_opcode ( Opcode ::Pop ) ;
self . patch_jump ( exit ) ;
self . patch_jump ( early_exit ) ;
}
Node ::ForOfLoop ( for_of_loop ) = > {
self . compile_expr ( for_of_loop . iterable ( ) , true ) ;
self . emit_opcode ( Opcode ::InitIterator ) ;
let start_address = self . next_opcode_location ( ) ;
self . push_loop_control_info ( for_of_loop . label ( ) . map ( Into ::into ) , start_address ) ;
self . emit_opcode ( Opcode ::PushDeclarativeEnvironment ) ;
let exit = self . jump_with_custom_opcode ( Opcode ::ForInLoopNext ) ;
match for_of_loop . init ( ) {
IterableLoopInitializer ::Identifier ( ref ident ) = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
IterableLoopInitializer ::Var ( declaration ) = > match declaration {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
self . compile_declaration_pattern ( pattern , Opcode ::DefInitVar ) ;
}
} ,
IterableLoopInitializer ::Let ( declaration ) = > match declaration {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
self . compile_declaration_pattern ( pattern , Opcode ::DefInitLet ) ;
}
} ,
IterableLoopInitializer ::Const ( declaration ) = > match declaration {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::SetName , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
self . compile_declaration_pattern ( pattern , Opcode ::DefInitConst ) ;
}
} ,
}
self . compile_stmt ( for_of_loop . body ( ) , false ) ;
self . emit_opcode ( Opcode ::PopEnvironment ) ;
self . emit ( Opcode ::Jump , & [ start_address ] ) ;
self . patch_jump ( exit ) ;
self . pop_loop_control_info ( ) ;
}
Node ::WhileLoop ( while_ ) = > {
let start_address = self . next_opcode_location ( ) ;
self . push_loop_control_info ( while_ . label ( ) . map ( Into ::into ) , start_address ) ;
@ -911,19 +1181,31 @@ impl ByteCompiler {
self . pop_loop_control_info ( ) ;
}
Node ::DoWhileLoop ( do_while ) = > {
let initial_label = self . jump ( ) ;
let start_address = self . next_opcode_location ( ) ;
self . push_loop_control_info ( do_while . label ( ) . map ( Into ::into ) , start_address ) ;
self . compile_stmt ( do_while . body ( ) , false ) ;
let condition_label_address = self . next_opcode_location ( ) ;
self . compile_expr ( do_while . cond ( ) , true ) ;
self . emit ( Opcode ::JumpIfTrue , & [ start_address ] ) ;
let exit = self . jump_if_false ( ) ;
self . patch_jump ( initial_label ) ;
self . compile_stmt ( do_while . body ( ) , false ) ;
self . emit ( Opcode ::Jump , & [ condition_label_address ] ) ;
self . pop_loop_control_info ( ) ;
self . patch_jump ( exit ) ;
}
Node ::Continue ( node ) = > {
let label = self . jump ( ) ;
let mut items = self . jump_info . iter_mut ( ) . rev ( ) . filter ( | info | info . is_loop ) ;
let mut items = self
. jump_info
. iter_mut ( )
. rev ( )
. filter ( | info | info . kind = = JumpControlInfoKind ::Loop ) ;
let target = if node . label ( ) . is_none ( ) {
items . next ( )
} else {
@ -948,9 +1230,11 @@ impl ByteCompiler {
}
}
Node ::Block ( block ) = > {
self . emit_opcode ( Opcode ::PushDeclarativeEnvironment ) ;
for node in block . items ( ) {
self . compile_stmt ( node , false ) ;
}
self . emit_opcode ( Opcode ::PopEnvironment ) ;
}
Node ::Throw ( throw ) = > {
self . compile_expr ( throw . expr ( ) , true ) ;
@ -992,6 +1276,74 @@ impl ByteCompiler {
}
self . emit ( Opcode ::Return , & [ ] ) ;
}
Node ::Try ( t ) = > {
self . push_try_control_info ( ) ;
let try_start = self . jump_with_custom_opcode ( Opcode ::TryStart ) ;
self . emit_opcode ( Opcode ::PushDeclarativeEnvironment ) ;
for node in t . block ( ) . items ( ) {
self . compile_stmt ( node , false ) ;
}
self . emit_opcode ( Opcode ::PopEnvironment ) ;
self . emit_opcode ( Opcode ::TryEnd ) ;
let finally = self . jump ( ) ;
self . patch_jump ( try_start ) ;
if let Some ( catch ) = t . catch ( ) {
self . emit_opcode ( Opcode ::PushDeclarativeEnvironment ) ;
if let Some ( decl ) = catch . parameter ( ) {
match decl {
Declaration ::Identifier { ident , .. } = > {
let index = self . get_or_insert_name ( ident . as_ref ( ) ) ;
self . emit ( Opcode ::DefInitLet , & [ index ] ) ;
}
Declaration ::Pattern ( pattern ) = > {
let idents = pattern . idents ( ) ;
for ( i , ident ) in idents . iter ( ) . enumerate ( ) {
if i < idents . len ( ) {
self . emit_opcode ( Opcode ::Dup ) ;
}
let index = self . get_or_insert_name ( ident ) ;
self . emit ( Opcode ::DefInitLet , & [ index ] ) ;
}
}
}
}
for node in catch . block ( ) . items ( ) {
self . compile_stmt ( node , false ) ;
}
self . emit_opcode ( Opcode ::PopEnvironment ) ;
self . emit_opcode ( Opcode ::TryEnd ) ;
}
self . patch_jump ( finally ) ;
if let Some ( finally ) = t . finally ( ) {
self . emit_opcode ( Opcode ::FinallyStart ) ;
let finally_start_address = self . next_opcode_location ( ) ;
for node in finally . items ( ) {
self . compile_stmt ( node , false ) ;
}
self . emit_opcode ( Opcode ::FinallyEnd ) ;
self . pop_try_control_info ( Some ( finally_start_address ) ) ;
} else {
self . pop_try_control_info ( None ) ;
}
}
// TODO: implement AsyncFunctionDecl
Node ::AsyncFunctionDecl ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
// TODO: implement GeneratorDecl
Node ::GeneratorDecl ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
// TODO: implement AsyncGeneratorDecl
Node ::AsyncGeneratorDecl ( _ ) = > {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
Node ::Empty = > { }
expr = > self . compile_expr ( expr , use_expr ) ,
}
@ -1075,8 +1427,212 @@ impl ByteCompiler {
}
}
pub ( crate ) fn call ( & mut self , node : & Node , use_expr : bool ) {
#[ derive(PartialEq) ]
enum CallKind {
Call ,
New ,
}
let ( call , kind ) = match node {
Node ::Call ( call ) = > ( call , CallKind ::Call ) ,
Node ::New ( new ) = > ( new . call ( ) , CallKind ::New ) ,
_ = > unreachable! ( ) ,
} ;
match call . expr ( ) {
Node ::GetConstField ( field ) = > {
self . compile_expr ( field . obj ( ) , true ) ;
self . emit ( Opcode ::Dup , & [ ] ) ;
let index = self . get_or_insert_name ( field . field ( ) ) ;
self . emit ( Opcode ::GetPropertyByName , & [ index ] ) ;
}
Node ::GetField ( field ) = > {
self . compile_expr ( field . obj ( ) , true ) ;
self . emit ( Opcode ::Dup , & [ ] ) ;
self . compile_expr ( field . field ( ) , true ) ;
self . emit ( Opcode ::Swap , & [ ] ) ;
self . emit ( Opcode ::GetPropertyByValue , & [ ] ) ;
}
expr = > {
self . compile_expr ( expr , true ) ;
if kind = = CallKind ::Call {
self . emit_opcode ( Opcode ::This ) ;
self . emit_opcode ( Opcode ::Swap ) ;
}
}
}
for arg in call . args ( ) . iter ( ) . rev ( ) {
self . compile_expr ( arg , true ) ;
}
let last_is_rest_parameter = matches! ( call . args ( ) . last ( ) , Some ( Node ::Spread ( _ ) ) ) ;
match kind {
CallKind ::Call if last_is_rest_parameter = > {
self . emit ( Opcode ::CallWithRest , & [ call . args ( ) . len ( ) as u32 ] )
}
CallKind ::Call = > self . emit ( Opcode ::Call , & [ call . args ( ) . len ( ) as u32 ] ) ,
CallKind ::New if last_is_rest_parameter = > {
self . emit ( Opcode ::NewWithRest , & [ call . args ( ) . len ( ) as u32 ] )
}
CallKind ::New = > self . emit ( Opcode ::New , & [ call . args ( ) . len ( ) as u32 ] ) ,
}
if ! use_expr {
self . emit ( Opcode ::Pop , & [ ] ) ;
}
}
#[ inline ]
pub fn finish ( self ) -> CodeBlock {
self . code_block
}
#[ inline ]
fn compile_declaration_pattern ( & mut self , pattern : & DeclarationPattern , def : Opcode ) {
match pattern {
DeclarationPattern ::Object ( pattern ) = > {
let skip_init = self . jump_with_custom_opcode ( Opcode ::JumpIfNotUndefined ) ;
if let Some ( init ) = pattern . init ( ) {
self . compile_expr ( init , true ) ;
} else {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
self . patch_jump ( skip_init ) ;
self . emit_opcode ( Opcode ::ValueNotNullOrUndefined ) ;
self . emit_opcode ( Opcode ::RequireObjectCoercible ) ;
for binding in pattern . bindings ( ) {
use BindingPatternTypeObject ::* ;
match binding {
// ObjectBindingPattern : { }
Empty = > { }
// SingleNameBinding : BindingIdentifier Initializer[opt]
SingleName {
ident ,
property_name ,
default_init ,
} = > {
self . emit_opcode ( Opcode ::Dup ) ;
let index = self . get_or_insert_name ( property_name ) ;
self . emit ( Opcode ::GetPropertyByName , & [ index ] ) ;
if let Some ( init ) = default_init {
let skip = self . jump_with_custom_opcode ( Opcode ::JumpIfNotUndefined ) ;
self . emit_opcode ( Opcode ::Pop ) ;
self . compile_expr ( init , true ) ;
self . patch_jump ( skip ) ;
}
let index = self . get_or_insert_name ( ident ) ;
self . emit ( def , & [ index ] ) ;
}
// BindingRestProperty : ... BindingIdentifier
RestProperty {
ident ,
excluded_keys ,
} = > {
self . emit_opcode ( Opcode ::Dup ) ;
self . emit_opcode ( Opcode ::PushEmptyObject ) ;
for key in excluded_keys {
self . emit_push_literal ( Literal ::String ( key . as_ref ( ) . into ( ) ) ) ;
}
self . emit ( Opcode ::CopyDataProperties , & [ excluded_keys . len ( ) as u32 ] ) ;
let index = self . get_or_insert_name ( ident ) ;
self . emit ( def , & [ index ] ) ;
}
BindingPattern {
ident ,
pattern ,
default_init ,
} = > {
self . emit_opcode ( Opcode ::Dup ) ;
let index = self . get_or_insert_name ( ident ) ;
self . emit ( Opcode ::GetPropertyByName , & [ index ] ) ;
if let Some ( init ) = default_init {
let skip = self . jump_with_custom_opcode ( Opcode ::JumpIfNotUndefined ) ;
self . emit_opcode ( Opcode ::Pop ) ;
self . compile_expr ( init , true ) ;
self . patch_jump ( skip ) ;
} else {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
self . compile_declaration_pattern ( pattern , def ) ;
}
}
}
self . emit_opcode ( Opcode ::Pop ) ;
}
DeclarationPattern ::Array ( pattern ) = > {
let skip_init = self . jump_with_custom_opcode ( Opcode ::JumpIfNotUndefined ) ;
if let Some ( init ) = pattern . init ( ) {
self . compile_expr ( init , true ) ;
} else {
self . emit_opcode ( Opcode ::PushUndefined ) ;
}
self . patch_jump ( skip_init ) ;
self . emit_opcode ( Opcode ::ValueNotNullOrUndefined ) ;
self . emit_opcode ( Opcode ::InitIterator ) ;
for binding in pattern . bindings ( ) {
use BindingPatternTypeArray ::* ;
match binding {
// ArrayBindingPattern : [ ]
Empty = > { }
// ArrayBindingPattern : [ Elision ]
Elision = > {
self . emit_opcode ( Opcode ::IteratorNext ) ;
self . emit_opcode ( Opcode ::Pop ) ;
}
// SingleNameBinding : BindingIdentifier Initializer[opt]
SingleName {
ident ,
default_init ,
} = > {
self . emit_opcode ( Opcode ::IteratorNext ) ;
if let Some ( init ) = default_init {
let skip = self . jump_with_custom_opcode ( Opcode ::JumpIfNotUndefined ) ;
self . emit_opcode ( Opcode ::Pop ) ;
self . compile_expr ( init , true ) ;
self . patch_jump ( skip ) ;
}
let index = self . get_or_insert_name ( ident ) ;
self . emit ( def , & [ index ] ) ;
}
// BindingElement : BindingPattern Initializer[opt]
BindingPattern { pattern } = > {
self . emit_opcode ( Opcode ::IteratorNext ) ;
self . compile_declaration_pattern ( pattern , def )
}
// BindingRestElement : ... BindingIdentifier
SingleNameRest { ident } = > {
self . emit_opcode ( Opcode ::IteratorToArray ) ;
let index = self . get_or_insert_name ( ident ) ;
self . emit ( def , & [ index ] ) ;
}
// BindingRestElement : ... BindingPattern
BindingPatternRest { pattern } = > {
self . emit_opcode ( Opcode ::IteratorToArray ) ;
self . compile_declaration_pattern ( pattern , def ) ;
}
}
}
self . emit_opcode ( Opcode ::Pop ) ;
}
}
}
}