@ -18,11 +18,12 @@ use crate::{
vm ::CallFrame ,
Context , JsError , JsResult , JsString , JsValue ,
} ;
use bitflags ::bitflags ;
use boa_ast ::function ::{ FormalParameterList , PrivateName } ;
use boa_gc ::{ Finalize , Gc , GcRefCell , Trace } ;
use boa_gc ::{ empty_trace , Finalize , Gc , GcRefCell , Trace } ;
use boa_interner ::Sym ;
use boa_profiler ::Profiler ;
use std ::{ collections ::VecDeque , mem ::size_of } ;
use std ::{ cell ::Cell , c ollections ::VecDeque , mem ::size_of } ;
use thin_vec ::ThinVec ;
#[ cfg(any(feature = " trace " , feature = " flowgraph " )) ]
@ -52,6 +53,33 @@ unsafe impl Readable for i64 {}
unsafe impl Readable for f32 { }
unsafe impl Readable for f64 { }
bitflags ! {
/// Flags for [`CodeBlock`].
#[ derive(Clone, Copy, Debug, Finalize) ]
pub ( crate ) struct CodeBlockFlags : u8 {
/// Is this function in strict mode.
const STRICT = 0b0000_0001 ;
/// Indicates if the function is an expression and has a binding identifier.
const HAS_BINDING_IDENTIFIER = 0b0000_0010 ;
/// The `[[IsClassConstructor]]` internal slot.
const IS_CLASS_CONSTRUCTOR = 0b0000_0100 ;
/// Does this function have a parameters environment.
const PARAMETERS_ENV_BINDINGS = 0b0000_1000 ;
/// Trace instruction execution to `stdout`.
#[ cfg(feature = " trace " ) ]
const TRACEABLE = 0b1000_0000 ;
}
}
// SAFETY: Nothing in CodeBlockFlags needs tracing, so this is safe.
unsafe impl Trace for CodeBlockFlags {
empty_trace ! ( ) ;
}
/// The internal representation of a JavaScript function.
///
/// A `CodeBlock` is generated for each function compiled by the
@ -63,15 +91,12 @@ pub struct CodeBlock {
#[ unsafe_ignore_trace ]
pub ( crate ) name : Sym ,
/// Indicates if the function is an expression and has a binding identifier.
pub ( crate ) has_binding_identifier : bool ,
#[ unsafe_ignore_trace ]
pub ( crate ) flags : Cell < CodeBlockFlags > ,
/// The number of arguments expected.
pub ( crate ) length : u32 ,
/// Is this function in strict mode.
pub ( crate ) strict : bool ,
/// \[\[ThisMode\]\]
pub ( crate ) this_mode : ThisMode ,
@ -96,9 +121,6 @@ pub struct CodeBlock {
#[ unsafe_ignore_trace ]
pub ( crate ) bindings : Box < [ BindingLocator ] > ,
/// Number of binding for the function environment.
pub ( crate ) num_bindings : u32 ,
/// Functions inside this function
pub ( crate ) functions : Box < [ Gc < Self > ] > ,
@ -109,26 +131,9 @@ pub struct CodeBlock {
/// Compile time environments in this function.
pub ( crate ) compile_environments : Box < [ Gc < GcRefCell < CompileTimeEnvironment > > ] > ,
/// The `[[IsClassConstructor]]` internal slot.
pub ( crate ) is_class_constructor : bool ,
/// The `[[ClassFieldInitializerName]]` internal slot.
#[ unsafe_ignore_trace ]
pub ( crate ) class_field_initializer_name : Option < Sym > ,
/// Marks the location in the code where the function environment in pushed.
/// This is only relevant for functions with expressions in the parameters.
/// We execute the parameter expressions in the function code and push the function environment afterward.
/// When the execution of the parameter expressions throws an error, we do not need to pop the function environment.
pub ( crate ) function_environment_push_location : u32 ,
/// The number of bindings in the parameters environment.
pub ( crate ) parameters_env_bindings : Option < u32 > ,
#[ cfg(feature = " trace " ) ]
/// Trace instruction execution to `stdout`.
#[ unsafe_ignore_trace ]
pub ( crate ) trace : std ::cell ::Cell < bool > ,
}
/// ---- `CodeBlock` public API ----
@ -136,28 +141,23 @@ impl CodeBlock {
/// Creates a new `CodeBlock`.
#[ must_use ]
pub fn new ( name : Sym , length : u32 , strict : bool ) -> Self {
let mut flags = CodeBlockFlags ::empty ( ) ;
flags . set ( CodeBlockFlags ::STRICT , strict ) ;
Self {
bytecode : Box ::default ( ) ,
literals : Box ::default ( ) ,
names : Box ::default ( ) ,
private_names : Box ::default ( ) ,
bindings : Box ::default ( ) ,
num_bindings : 0 ,
functions : Box ::default ( ) ,
name ,
has_binding_identifier : false ,
flags : Cell ::new ( flags ) ,
length ,
strict ,
this_mode : ThisMode ::Global ,
params : FormalParameterList ::default ( ) ,
arguments_binding : None ,
compile_environments : Box ::default ( ) ,
is_class_constructor : false ,
class_field_initializer_name : None ,
function_environment_push_location : 0 ,
parameters_env_bindings : None ,
#[ cfg(feature = " trace " ) ]
trace : std ::cell ::Cell ::new ( false ) ,
}
}
@ -167,11 +167,44 @@ impl CodeBlock {
self . name
}
/// Check if the function is traced.
#[ cfg(feature = " trace " ) ]
pub ( crate ) fn traceable ( & self ) -> bool {
self . flags . get ( ) . contains ( CodeBlockFlags ::TRACEABLE )
}
/// Enable or disable instruction tracing to `stdout`.
#[ cfg(feature = " trace " ) ]
#[ inline ]
pub fn set_trace ( & self , value : bool ) {
self . trace . set ( value ) ;
pub fn set_traceable ( & self , value : bool ) {
let mut flags = self . flags . get ( ) ;
flags . set ( CodeBlockFlags ::TRACEABLE , value ) ;
self . flags . set ( flags ) ;
}
/// Check if the function is a class constructor.
pub ( crate ) fn is_class_constructor ( & self ) -> bool {
self . flags
. get ( )
. contains ( CodeBlockFlags ::IS_CLASS_CONSTRUCTOR )
}
/// Check if the function is in strict mode.
pub ( crate ) fn strict ( & self ) -> bool {
self . flags . get ( ) . contains ( CodeBlockFlags ::STRICT )
}
/// Indicates if the function is an expression and has a binding identifier.
pub ( crate ) fn has_binding_identifier ( & self ) -> bool {
self . flags
. get ( )
. contains ( CodeBlockFlags ::HAS_BINDING_IDENTIFIER )
}
/// Does this function have a parameters environment.
pub ( crate ) fn has_parameters_env_bindings ( & self ) -> bool {
self . flags
. get ( )
. contains ( CodeBlockFlags ::PARAMETERS_ENV_BINDINGS )
}
}
@ -285,9 +318,12 @@ impl CodeBlock {
* pc + = size_of ::< u32 > ( ) ;
result
}
Opcode ::PushDeclarativeEnvironment
| Opcode ::PushFunctionEnvironment
| Opcode ::CopyDataProperties
Opcode ::PushDeclarativeEnvironment | Opcode ::PushFunctionEnvironment = > {
let operand = self . read ::< u32 > ( * pc ) ;
* pc + = size_of ::< u32 > ( ) ;
format! ( "{operand}" )
}
Opcode ::CopyDataProperties
| Opcode ::Break
| Opcode ::Continue
| Opcode ::LoopStart
@ -914,7 +950,7 @@ impl JsObject {
..
} = > {
let code = code . clone ( ) ;
if code . is_class_constructor {
if code . is_class_constructor ( ) {
return Err ( JsNativeError ::typ ( )
. with_message ( "class constructor cannot be invoked without 'new'" )
. with_realm ( context . realm ( ) . clone ( ) )
@ -991,7 +1027,7 @@ impl JsObject {
let this = if lexical_this_mode {
ThisBindingStatus ::Lexical
} else if code . strict {
} else if code . strict ( ) {
ThisBindingStatus ::Initialized ( this . clone ( ) )
} else if this . is_null_or_undefined ( ) {
ThisBindingStatus ::Initialized ( context . realm ( ) . global_this ( ) . clone ( ) . into ( ) )
@ -1009,7 +1045,7 @@ impl JsObject {
let index = context
. vm
. environments
. push_lexical ( 1 , code . compile_environments [ last_env ] . clone ( ) ) ;
. push_lexical ( code . compile_environments [ last_env ] . clone ( ) ) ;
context
. vm
. environments
@ -1017,11 +1053,11 @@ impl JsObject {
last_env - = 1 ;
}
if code . has_binding_identifier {
if code . has_binding_identifier ( ) {
let index = context
. vm
. environments
. push_lexical ( 1 , code . compile_environments [ last_env ] . clone ( ) ) ;
. push_lexical ( code . compile_environments [ last_env ] . clone ( ) ) ;
context
. vm
. environments
@ -1030,21 +1066,20 @@ impl JsObject {
}
context . vm . environments . push_function (
code . num_bindings ,
code . compile_environments [ last_env ] . clone ( ) ,
FunctionSlots ::new ( this , self . clone ( ) , None ) ,
) ;
if let Some ( bindings ) = code . parameters_env_bindings {
if code . has_parameters_env_bindings ( ) {
last_env - = 1 ;
context
. vm
. environments
. push_lexical ( bindings , code . compile_environments [ last_env ] . clone ( ) ) ;
. push_lexical ( code . compile_environments [ last_env ] . clone ( ) ) ;
}
if let Some ( binding ) = code . arguments_binding {
let arguments_obj = if code . strict | | ! code . params . is_simple ( ) {
let arguments_obj = if code . strict ( ) | | ! code . params . is_simple ( ) {
Arguments ::create_unmapped_arguments_object ( args , context )
} else {
let env = context . vm . environments . current ( ) ;
@ -1271,11 +1306,11 @@ impl JsObject {
let mut last_env = code . compile_environments . len ( ) - 1 ;
if code . has_binding_identifier {
if code . has_binding_identifier ( ) {
let index = context
. vm
. environments
. push_lexical ( 1 , code . compile_environments [ last_env ] . clone ( ) ) ;
. push_lexical ( code . compile_environments [ last_env ] . clone ( ) ) ;
context
. vm
. environments
@ -1284,7 +1319,6 @@ impl JsObject {
}
context . vm . environments . push_function (
code . num_bindings ,
code . compile_environments [ last_env ] . clone ( ) ,
FunctionSlots ::new (
this . clone ( ) . map_or ( ThisBindingStatus ::Uninitialized , | o | {
@ -1295,15 +1329,15 @@ impl JsObject {
) ,
) ;
if let Some ( bindings ) = code . parameters_env_bindings {
if code . has_parameters_env_bindings ( ) {
context
. vm
. environments
. push_lexical ( bindings , code . compile_environments [ 0 ] . clone ( ) ) ;
. push_lexical ( code . compile_environments [ 0 ] . clone ( ) ) ;
}
if let Some ( binding ) = code . arguments_binding {
let arguments_obj = if code . strict | | ! code . params . is_simple ( ) {
let arguments_obj = if code . strict ( ) | | ! code . params . is_simple ( ) {
Arguments ::create_unmapped_arguments_object ( args , context )
} else {
let env = context . vm . environments . current ( ) ;
@ -1340,7 +1374,7 @@ impl JsObject {
context . vm . push ( arg . clone ( ) ) ;
}
let has_binding_identifier = code . has_binding_identifier ;
let has_binding_identifier = code . has_binding_identifier ( ) ;
std ::mem ::swap ( & mut context . vm . active_runnable , & mut script_or_module ) ;