@ -1,4 +1,5 @@
use crate ::{
use crate ::{
environments ::{ BindingLocator , Environment } ,
vm ::{ opcode ::Operation , CompletionType } ,
vm ::{ opcode ::Operation , CompletionType } ,
Context , JsNativeError , JsResult ,
Context , JsNativeError , JsResult ,
} ;
} ;
@ -25,34 +26,70 @@ impl Operation for SetName {
context . find_runtime_binding ( & mut binding_locator ) ? ;
context . find_runtime_binding ( & mut binding_locator ) ? ;
if ! context . is_initialized_binding ( & binding_locator ) ? {
verify_initialized ( binding_locator , context ) ? ;
if binding_locator . is_global ( ) & & context . vm . frame ( ) . code_block . strict {
let key = context
. interner ( )
. resolve_expect ( binding_locator . name ( ) . sym ( ) )
. to_string ( ) ;
return Err ( JsNativeError ::reference ( )
. with_message ( format! (
"cannot assign to uninitialized global property `{key}`"
) )
. into ( ) ) ;
}
if ! binding_locator . is_global ( ) {
context . set_binding ( binding_locator , value , context . vm . frame ( ) . code_block . strict ) ? ;
let key = context
. interner ( )
. resolve_expect ( binding_locator . name ( ) . sym ( ) )
. to_string ( ) ;
return Err ( JsNativeError ::reference ( )
Ok ( CompletionType ::Normal )
. with_message ( format! ( "cannot assign to uninitialized binding `{key}`" ) )
}
. into ( ) ) ;
}
}
/// `SetNameByLocator` implements the Opcode Operation for `Opcode::SetNameByLocator`
///
/// Operation:
/// - Assigns a value to the binding pointed by the `current_binding` of the current frame.
#[ derive(Debug, Clone, Copy) ]
pub ( crate ) struct SetNameByLocator ;
impl Operation for SetNameByLocator {
const NAME : & ' static str = "SetNameByLocator" ;
const INSTRUCTION : & ' static str = "INST - SetNameByLocator" ;
fn execute ( context : & mut Context < ' _ > ) -> JsResult < CompletionType > {
let binding_locator = context
. vm
. frame_mut ( )
. binding_stack
. pop ( )
. expect ( "locator should have been popped before" ) ;
let value = context . vm . pop ( ) ;
if binding_locator . is_silent ( ) {
return Ok ( CompletionType ::Normal ) ;
}
}
binding_locator . throw_mutate_immutable ( context ) ? ;
verify_initialized ( binding_locator , context ) ? ;
context . set_binding ( binding_locator , value , context . vm . frame ( ) . code_block . strict ) ? ;
context . set_binding ( binding_locator , value , context . vm . frame ( ) . code_block . strict ) ? ;
Ok ( CompletionType ::Normal )
Ok ( CompletionType ::Normal )
}
}
}
}
/// Checks that the binding pointed by `locator` exists and is initialized.
fn verify_initialized ( locator : BindingLocator , context : & mut Context < ' _ > ) -> JsResult < ( ) > {
if ! context . is_initialized_binding ( & locator ) ? {
let key = context . interner ( ) . resolve_expect ( locator . name ( ) . sym ( ) ) ;
let strict = context . vm . frame ( ) . code_block . strict ;
let message = if locator . is_global ( ) {
strict . then ( | | format! ( "cannot assign to uninitialized global property `{key}`" ) )
} else {
match context . environment_expect ( locator . environment_index ( ) ) {
Environment ::Declarative ( _ ) = > {
Some ( format! ( "cannot assign to uninitialized binding `{key}`" ) )
}
Environment ::Object ( _ ) if strict = > {
Some ( format! ( "cannot assign to uninitialized property `{key}`" ) )
}
Environment ::Object ( _ ) = > None ,
}
} ;
if let Some ( message ) = message {
return Err ( JsNativeError ::reference ( ) . with_message ( message ) . into ( ) ) ;
}
}
Ok ( ( ) )
}