@ -17,13 +17,12 @@ use crate::{
builtins ::{ self , function ::NativeFunctionSignature } ,
builtins ::{ self , function ::NativeFunctionSignature } ,
bytecompiler ::ByteCompiler ,
bytecompiler ::ByteCompiler ,
class ::{ Class , ClassBuilder } ,
class ::{ Class , ClassBuilder } ,
error ::JsNativeError ,
job ::JobCallback ,
job ::JobCallback ,
object ::{ FunctionBuilder , GlobalPropertyMap , JsObject , ObjectData } ,
object ::{ FunctionBuilder , GlobalPropertyMap , JsObject } ,
property ::{ Attribute , PropertyDescriptor , PropertyKey } ,
property ::{ Attribute , PropertyDescriptor , PropertyKey } ,
realm ::Realm ,
realm ::Realm ,
vm ::{ CallFrame , CodeBlock , FinallyReturn , GeneratorResumeKind , Vm } ,
vm ::{ CallFrame , CodeBlock , FinallyReturn , GeneratorResumeKind , Vm } ,
JsResult , JsString , Js Value ,
JsResult , JsValue ,
} ;
} ;
use boa_ast ::StatementList ;
use boa_ast ::StatementList ;
@ -131,6 +130,8 @@ impl Default for Context {
}
}
}
}
// ==== Public API ====
impl Context {
impl Context {
/// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or
/// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or
/// the icu data provider.
/// the icu data provider.
@ -139,44 +140,34 @@ impl Context {
ContextBuilder ::default ( )
ContextBuilder ::default ( )
}
}
/// Gets the string interner.
/// Evaluates the given code by compiling down to bytecode, then interpreting the bytecode into a value
#[ inline ]
///
pub const fn interner ( & self ) -> & Interner {
/// # Examples
& self . interner
/// ```
}
/// # use boa_engine::Context;
/// let mut context = Context::default();
/// Gets a mutable reference to the string interner.
///
#[ inline ]
/// let value = context.eval("1 + 3").unwrap();
pub fn interner_mut ( & mut self ) -> & mut Interner {
///
& mut self . interner
/// assert!(value.is_number());
}
/// assert_eq!(value.as_number().unwrap(), 4.0);
/// ```
/// A helper function for getting an immutable reference to the `console` object.
#[ allow(clippy::unit_arg, clippy::drop_copy) ]
#[ cfg(feature = " console " ) ]
pub fn eval < S > ( & mut self , src : S ) -> JsResult < JsValue >
pub ( crate ) const fn console ( & self ) -> & Console {
where
& self . console
S : AsRef < [ u8 ] > ,
}
{
let main_timer = Profiler ::global ( ) . start_event ( "Evaluation" , "Main" ) ;
/// A helper function for getting a mutable reference to the `console` object.
let statement_list = self . parse ( src ) ? ;
#[ cfg(feature = " console " ) ]
let code_block = self . compile ( & statement_list ) ? ;
pub ( crate ) fn console_mut ( & mut self ) -> & mut Console {
let result = self . execute ( code_block ) ;
& mut self . console
}
/// Sets up the default global objects within Global
// The main_timer needs to be dropped before the Profiler is.
fn create_intrinsics ( & mut self ) {
drop ( main_timer ) ;
let _timer = Profiler ::global ( ) . start_event ( "create_intrinsics" , "interpreter" ) ;
Profiler ::global ( ) . drop ( ) ;
// Create intrinsics, add global objects here
builtins ::init ( self ) ;
}
/// Constructs an object with the `%Object.prototype%` prototype.
result
#[ inline ]
pub fn construct_object ( & self ) -> JsObject {
JsObject ::from_proto_and_data (
self . intrinsics ( ) . constructors ( ) . object ( ) . prototype ( ) ,
ObjectData ::ordinary ( ) ,
)
}
}
/// Parse the given source text.
/// Parse the given source text.
@ -184,70 +175,91 @@ impl Context {
where
where
S : AsRef < [ u8 ] > ,
S : AsRef < [ u8 ] > ,
{
{
let _timer = Profiler ::global ( ) . start_event ( "Parsing" , "Main" ) ;
let mut parser = Parser ::new ( src . as_ref ( ) ) ;
let mut parser = Parser ::new ( src . as_ref ( ) ) ;
parser . parse_all ( & mut self . interner )
parser . parse_all ( & mut self . interner )
}
}
/// `Call ( F, V [ , argumentsList ] )`
/// Compile the AST into a `CodeBlock` ready to be executed by the VM.
///
pub fn compile ( & mut self , statement_list : & StatementList ) -> JsResult < Gc < CodeBlock > > {
/// The abstract operation `Call` takes arguments `F` (an ECMAScript language value) and `V`
let _timer = Profiler ::global ( ) . start_event ( "Compilation" , "Main" ) ;
/// (an ECMAScript language value) and optional argument `argumentsList` (a `List` of
let mut compiler = ByteCompiler ::new ( Sym ::MAIN , statement_list . strict ( ) , false , self ) ;
/// ECMAScript language values) and returns either a normal completion containing an ECMAScript
compiler . create_decls ( statement_list , false ) ;
/// language value or a throw completion. It is used to call the `[[Call]]` internal method of
compiler . compile_statement_list ( statement_list , true , false ) ? ;
/// a function object. `F` is the function object, `V` is an ECMAScript language value that is
Ok ( Gc ::new ( compiler . finish ( ) ) )
/// the `this` value of the `[[Call]]`, and `argumentsList` is the value passed to the
/// corresponding argument of the internal method. If `argumentsList` is not present, a new
/// empty `List` is used as its value.
///
/// More information:
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-call
pub ( crate ) fn call (
& mut self ,
f : & JsValue ,
v : & JsValue ,
arguments_list : & [ JsValue ] ,
) -> JsResult < JsValue > {
// 1. If argumentsList is not present, set argumentsList to a new empty List.
// 2. If IsCallable(F) is false, throw a TypeError exception.
// 3. Return ? F.[[Call]](V, argumentsList).
f . as_callable ( )
. ok_or_else ( | | {
JsNativeError ::typ ( )
. with_message ( "Value is not callable" )
. into ( )
} )
. and_then ( | f | f . call ( v , arguments_list , self ) )
}
}
/// Return the global object.
/// Call the VM with a `CodeBlock` and return the result.
#[ inline ]
///
pub const fn global_object ( & self ) -> & JsObject {
/// Since this function receives a `Gc<CodeBlock>`, cloning the code is very cheap, since it's
self . realm . global_object ( )
/// just a pointer copy. Therefore, if you'd like to execute the same `CodeBlock` multiple
}
/// times, there is no need to re-compile it, and you can just call `clone()` on the
/// `Gc<CodeBlock>` returned by the [`Self::compile()`] function.
pub fn execute ( & mut self , code_block : Gc < CodeBlock > ) -> JsResult < JsValue > {
let _timer = Profiler ::global ( ) . start_event ( "Execution" , "Main" ) ;
/// Return a mutable reference to the global object string bindings.
self . vm . push_frame ( CallFrame {
pub ( crate ) fn global_bindings_mut ( & mut self ) -> & mut GlobalPropertyMap {
code : code_block ,
self . realm . global_bindings_mut ( )
pc : 0 ,
catch : Vec ::new ( ) ,
finally_return : FinallyReturn ::None ,
finally_jump : Vec ::new ( ) ,
pop_on_return : 0 ,
loop_env_stack : Vec ::from ( [ 0 ] ) ,
try_env_stack : Vec ::from ( [ crate ::vm ::TryStackEntry {
num_env : 0 ,
num_loop_stack_entries : 0 ,
} ] ) ,
param_count : 0 ,
arg_count : 0 ,
generator_resume_kind : GeneratorResumeKind ::Normal ,
thrown : false ,
async_generator : None ,
} ) ;
self . realm . set_global_binding_number ( ) ;
let result = self . run ( ) ;
self . vm . pop_frame ( ) ;
self . clear_kept_objects ( ) ;
self . run_queued_jobs ( ) ? ;
let ( result , _ ) = result ? ;
Ok ( result )
}
}
/// Constructs a `Error` with the specified message.
/// Register a global property.
pub fn construct_error < M > ( & mut self , message : M ) -> JsValue
///
/// # Example
/// ```
/// use boa_engine::{
/// object::ObjectInitializer,
/// property::{Attribute, PropertyDescriptor},
/// Context,
/// };
///
/// let mut context = Context::default();
///
/// context.register_global_property("myPrimitiveProperty", 10, Attribute::all());
///
/// let object = ObjectInitializer::new(&mut context)
/// .property("x", 0, Attribute::all())
/// .property("y", 1, Attribute::all())
/// .build();
/// context.register_global_property("myObjectProperty", object, Attribute::all());
/// ```
pub fn register_global_property < K , V > ( & mut self , key : K , value : V , attribute : Attribute )
where
where
M : Into < JsString > ,
K : Into < PropertyKey > ,
V : Into < JsValue > ,
{
{
crate ::builtins ::error ::Error ::constructor (
self . realm . global_property_map . insert (
& self
& key . into ( ) ,
. intrinsics ( )
PropertyDescriptor ::builder ( )
. constructors ( )
. value ( value )
. error ( )
. writable ( attribute . writable ( ) )
. constructor ( )
. enumerable ( attribute . enumerable ( ) )
. into ( ) ,
. configurable ( attribute . configurable ( ) )
& [ message . into ( ) . into ( ) ] ,
. build ( ) ,
self ,
) ;
)
. expect ( "Into<String> used as message" )
}
}
/// Register a global native function.
/// Register a global native function.
@ -372,12 +384,6 @@ impl Context {
Ok ( ( ) )
Ok ( ( ) )
}
}
/// <https://tc39.es/ecma262/#sec-hasproperty>
pub ( crate ) fn has_property ( & mut self , obj : & JsValue , key : & PropertyKey ) -> JsResult < bool > {
obj . as_object ( )
. map_or ( Ok ( false ) , | obj | obj . __has_property__ ( key , self ) )
}
/// Register a global class of type `T`, where `T` implements `Class`.
/// Register a global class of type `T`, where `T` implements `Class`.
///
///
/// # Example
/// # Example
@ -410,84 +416,82 @@ impl Context {
Ok ( ( ) )
Ok ( ( ) )
}
}
/// Register a global property.
/// Gets the string interner.
///
#[ inline ]
/// # Example
pub const fn interner ( & self ) -> & Interner {
/// ```
& self . interner
/// use boa_engine::{
/// object::ObjectInitializer,
/// property::{Attribute, PropertyDescriptor},
/// Context,
/// };
///
/// let mut context = Context::default();
///
/// context.register_global_property("myPrimitiveProperty", 10, Attribute::all());
///
/// let object = ObjectInitializer::new(&mut context)
/// .property("x", 0, Attribute::all())
/// .property("y", 1, Attribute::all())
/// .build();
/// context.register_global_property("myObjectProperty", object, Attribute::all());
/// ```
pub fn register_global_property < K , V > ( & mut self , key : K , value : V , attribute : Attribute )
where
K : Into < PropertyKey > ,
V : Into < JsValue > ,
{
self . realm . global_property_map . insert (
& key . into ( ) ,
PropertyDescriptor ::builder ( )
. value ( value )
. writable ( attribute . writable ( ) )
. enumerable ( attribute . enumerable ( ) )
. configurable ( attribute . configurable ( ) )
. build ( ) ,
) ;
}
}
/// Evaluates the given code by compiling down to bytecode, then interpreting the bytecode into a value
/// Gets a mutable reference to the string interner.
#[ inline ]
pub fn interner_mut ( & mut self ) -> & mut Interner {
& mut self . interner
}
/// Return the global object.
#[ inline ]
pub const fn global_object ( & self ) -> & JsObject {
self . realm . global_object ( )
}
/// Return the intrinsic constructors and objects.
#[ inline ]
pub const fn intrinsics ( & self ) -> & Intrinsics {
& self . intrinsics
}
/// Set the value of trace on the context
pub fn set_trace ( & mut self , trace : bool ) {
self . vm . trace = trace ;
}
/// More information:
/// - [ECMAScript reference][spec]
///
///
/// # Examples
/// [spec]: https://tc39.es/ecma262/#sec-hostenqueuepromisejob
/// ```
pub fn host_enqueue_promise_job ( & mut self , job : JobCallback /* , realm: Realm */ ) {
/// # use boa_engine::Context;
// If realm is not null ...
/// let mut context = Context::default();
// TODO
// Let scriptOrModule be ...
// TODO
self . promise_job_queue . push_back ( job ) ;
}
/// Abstract operation [`ClearKeptObjects`][clear].
///
///
/// let value = context.eval("1 + 3").unwrap();
/// Clears all objects maintained alive by calls to the [`AddToKeptObjects`][add] abstract
/// operation, used within the [`WeakRef`][weak] constructor.
///
///
/// assert!(value.is_number());
/// [clear]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-clear-kept-objects
/// assert_eq!(value.as_number().unwrap(), 4.0);
/// [add]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-addtokeptobjects
/// ```
/// [weak]: https://tc39.es/ecma262/multipage/managing-memory.html#sec-weak-ref-objects
#[ allow(clippy::unit_arg, clippy::drop_copy) ]
pub fn clear_kept_objects ( & mut self ) {
pub fn eval < S > ( & mut self , src : S ) -> JsResult < JsValue >
self . kept_alive . clear ( ) ;
where
}
S : AsRef < [ u8 ] > ,
}
{
let main_timer = Profiler ::global ( ) . start_event ( "Evaluation" , "Main" ) ;
let statement_list = Parser ::new ( src . as_ref ( ) ) . parse_all ( & mut self . interner ) ? ;
let code_block = self . compile ( & statement_list ) ? ;
// ==== Private API ====
let result = self . execute ( code_block ) ;
// The main_timer needs to be dropped before the Profiler is.
impl Context {
drop ( main_timer ) ;
/// A helper function for getting an immutable reference to the `console` object.
Profiler ::global ( ) . drop ( ) ;
#[ cfg(feature = " console " ) ]
pub ( crate ) const fn console ( & self ) -> & Console {
& self . console
}
result
/// A helper function for getting a mutable reference to the `console` object.
#[ cfg(feature = " console " ) ]
pub ( crate ) fn console_mut ( & mut self ) -> & mut Console {
& mut self . console
}
}
/// Compile the AST into a `CodeBlock` ready to be executed by the VM.
/// Return a mutable reference to the global object string bindings.
pub fn compile ( & mut self , statement_list : & StatementList ) -> JsResult < Gc < CodeBlock > > {
pub ( crate ) fn global_bindings_mut ( & mut self ) -> & mut GlobalPropertyMap {
let _timer = Profiler ::global ( ) . start_event ( "Compilation" , "Main" ) ;
self . realm . global_bindings_mut ( )
let mut compiler = ByteCompiler ::new ( Sym ::MAIN , statement_list . strict ( ) , false , self ) ;
compiler . create_decls ( statement_list , false ) ;
compiler . compile_statement_list ( statement_list , true , false ) ? ;
Ok ( Gc ::new ( compiler . finish ( ) ) )
}
}
/// Compile the AST into a `CodeBlock` ready to be executed by the VM in a `JSON.parse` context.
/// Compile the AST into a `CodeBlock` ready to be executed by the VM in a `JSON.parse` context.
pub fn compile_json_parse (
pub ( crate ) fn compile_json_parse (
& mut self ,
& mut self ,
statement_list : & StatementList ,
statement_list : & StatementList ,
) -> JsResult < Gc < CodeBlock > > {
) -> JsResult < Gc < CodeBlock > > {
@ -510,41 +514,17 @@ impl Context {
Ok ( Gc ::new ( compiler . finish ( ) ) )
Ok ( Gc ::new ( compiler . finish ( ) ) )
}
}
/// Call the VM with a `CodeBlock` and return the result.
/// Get the ICU related utilities
///
#[ cfg(feature = " intl " ) ]
/// Since this function receives a `Gc<CodeBlock>`, cloning the code is very cheap, since it's
pub ( crate ) const fn icu ( & self ) -> & icu ::Icu < BoaProvider > {
/// just a pointer copy. Therefore, if you'd like to execute the same `CodeBlock` multiple
& self . icu
/// times, there is no need to re-compile it, and you can just call `clone()` on the
}
/// `Gc<CodeBlock>` returned by the [`Self::compile()`] function.
pub fn execute ( & mut self , code_block : Gc < CodeBlock > ) -> JsResult < JsValue > {
let _timer = Profiler ::global ( ) . start_event ( "Execution" , "Main" ) ;
self . vm . push_frame ( CallFrame {
code : code_block ,
pc : 0 ,
catch : Vec ::new ( ) ,
finally_return : FinallyReturn ::None ,
finally_jump : Vec ::new ( ) ,
pop_on_return : 0 ,
loop_env_stack : Vec ::from ( [ 0 ] ) ,
try_env_stack : Vec ::from ( [ crate ::vm ::TryStackEntry {
num_env : 0 ,
num_loop_stack_entries : 0 ,
} ] ) ,
param_count : 0 ,
arg_count : 0 ,
generator_resume_kind : GeneratorResumeKind ::Normal ,
thrown : false ,
async_generator : None ,
} ) ;
self . realm . set_global_binding_number ( ) ;
/// Sets up the default global objects within Global
let result = self . run ( ) ;
fn create_intrinsics ( & mut self ) {
self . vm . pop_frame ( ) ;
let _timer = Profiler ::global ( ) . start_event ( "create_intrinsics" , "interpreter" ) ;
self . clear_kept_objects ( ) ;
// Create intrinsics, add global objects here
self . run_queued_jobs ( ) ? ;
builtins ::init ( self ) ;
let ( result , _ ) = result ? ;
Ok ( result )
}
}
/// Runs all the jobs in the job queue.
/// Runs all the jobs in the job queue.
@ -555,47 +535,6 @@ impl Context {
}
}
Ok ( ( ) )
Ok ( ( ) )
}
}
/// Return the intrinsic constructors and objects.
#[ inline ]
pub const fn intrinsics ( & self ) -> & Intrinsics {
& self . intrinsics
}
/// Set the value of trace on the context
pub fn set_trace ( & mut self , trace : bool ) {
self . vm . trace = trace ;
}
#[ cfg(feature = " intl " ) ]
/// Get the ICU related utilities
pub ( crate ) const fn icu ( & self ) -> & icu ::Icu < BoaProvider > {
& self . icu
}
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-hostenqueuepromisejob
pub fn host_enqueue_promise_job ( & mut self , job : JobCallback /* , realm: Realm */ ) {
// If realm is not null ...
// TODO
// Let scriptOrModule be ...
// TODO
self . promise_job_queue . push_back ( job ) ;
}
/// Abstract operation [`ClearKeptObjects`][clear].
///
/// Clears all objects maintained alive by calls to the [`AddToKeptObjects`][add] abstract
/// operation, used within the [`WeakRef`][weak] constructor.
///
/// [clear]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-clear-kept-objects
/// [add]: https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-addtokeptobjects
/// [weak]: https://tc39.es/ecma262/multipage/managing-memory.html#sec-weak-ref-objects
pub fn clear_kept_objects ( & mut self ) {
self . kept_alive . clear ( ) ;
}
}
}
/// Builder for the [`Context`] type.
/// Builder for the [`Context`] type.
///
///