|
|
@ -8,7 +8,7 @@ use crate::{ |
|
|
|
object::{JsObject, ObjectData, PROTOTYPE}, |
|
|
|
object::{JsObject, ObjectData, PROTOTYPE}, |
|
|
|
property::PropertyDescriptor, |
|
|
|
property::PropertyDescriptor, |
|
|
|
string::utf16, |
|
|
|
string::utf16, |
|
|
|
Context, JsString, JsValue, |
|
|
|
Context, JsBigInt, JsString, JsValue, |
|
|
|
}; |
|
|
|
}; |
|
|
|
use bitflags::bitflags; |
|
|
|
use bitflags::bitflags; |
|
|
|
use boa_ast::function::FormalParameterList; |
|
|
|
use boa_ast::function::FormalParameterList; |
|
|
@ -108,6 +108,21 @@ impl Handler { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Trace, Finalize)] |
|
|
|
|
|
|
|
pub(crate) enum Constant { |
|
|
|
|
|
|
|
/// Property field names and private names `[[description]]`s.
|
|
|
|
|
|
|
|
String(JsString), |
|
|
|
|
|
|
|
Function(Gc<CodeBlock>), |
|
|
|
|
|
|
|
BigInt(#[unsafe_ignore_trace] JsBigInt), |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Compile time environments in this function.
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
// Safety: Nothing in CompileTimeEnvironment needs tracing, so this is safe.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// TODO(#3034): Maybe changing this to Gc after garbage collection would be better than Rc.
|
|
|
|
|
|
|
|
CompileTimeEnvironment(#[unsafe_ignore_trace] Rc<CompileTimeEnvironment>), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// The internal representation of a JavaScript function.
|
|
|
|
/// The internal representation of a JavaScript function.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// A `CodeBlock` is generated for each function compiled by the
|
|
|
|
/// A `CodeBlock` is generated for each function compiled by the
|
|
|
@ -135,29 +150,15 @@ pub struct CodeBlock { |
|
|
|
/// Bytecode
|
|
|
|
/// Bytecode
|
|
|
|
pub(crate) bytecode: Box<[u8]>, |
|
|
|
pub(crate) bytecode: Box<[u8]>, |
|
|
|
|
|
|
|
|
|
|
|
/// Literals
|
|
|
|
pub(crate) constants: ThinVec<Constant>, |
|
|
|
pub(crate) literals: Box<[JsValue]>, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Property field names and private names `[[description]]`s.
|
|
|
|
|
|
|
|
pub(crate) names: Box<[JsString]>, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Locators for all bindings in the codeblock.
|
|
|
|
/// Locators for all bindings in the codeblock.
|
|
|
|
#[unsafe_ignore_trace] |
|
|
|
#[unsafe_ignore_trace] |
|
|
|
pub(crate) bindings: Box<[BindingLocator]>, |
|
|
|
pub(crate) bindings: Box<[BindingLocator]>, |
|
|
|
|
|
|
|
|
|
|
|
/// Functions inside this function
|
|
|
|
|
|
|
|
pub(crate) functions: Box<[Gc<Self>]>, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Exception [`Handler`]s.
|
|
|
|
/// Exception [`Handler`]s.
|
|
|
|
#[unsafe_ignore_trace] |
|
|
|
#[unsafe_ignore_trace] |
|
|
|
pub(crate) handlers: ThinVec<Handler>, |
|
|
|
pub(crate) handlers: ThinVec<Handler>, |
|
|
|
|
|
|
|
|
|
|
|
/// Compile time environments in this function.
|
|
|
|
|
|
|
|
// Safety: Nothing in CompileTimeEnvironment needs tracing, so this is safe.
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// TODO(#3034): Maybe changing this to Gc after garbage collection would be better than Rc.
|
|
|
|
|
|
|
|
#[unsafe_ignore_trace] |
|
|
|
|
|
|
|
pub(crate) compile_environments: Box<[Rc<CompileTimeEnvironment>]>, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// ---- `CodeBlock` public API ----
|
|
|
|
/// ---- `CodeBlock` public API ----
|
|
|
@ -169,17 +170,14 @@ impl CodeBlock { |
|
|
|
flags.set(CodeBlockFlags::STRICT, strict); |
|
|
|
flags.set(CodeBlockFlags::STRICT, strict); |
|
|
|
Self { |
|
|
|
Self { |
|
|
|
bytecode: Box::default(), |
|
|
|
bytecode: Box::default(), |
|
|
|
literals: Box::default(), |
|
|
|
constants: ThinVec::default(), |
|
|
|
names: Box::default(), |
|
|
|
|
|
|
|
bindings: Box::default(), |
|
|
|
bindings: Box::default(), |
|
|
|
functions: Box::default(), |
|
|
|
|
|
|
|
name, |
|
|
|
name, |
|
|
|
flags: Cell::new(flags), |
|
|
|
flags: Cell::new(flags), |
|
|
|
length, |
|
|
|
length, |
|
|
|
this_mode: ThisMode::Global, |
|
|
|
this_mode: ThisMode::Global, |
|
|
|
params: FormalParameterList::default(), |
|
|
|
params: FormalParameterList::default(), |
|
|
|
handlers: ThinVec::default(), |
|
|
|
handlers: ThinVec::default(), |
|
|
|
compile_environments: Box::default(), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -252,6 +250,51 @@ impl CodeBlock { |
|
|
|
.rev() |
|
|
|
.rev() |
|
|
|
.find(|(_, handler)| handler.contains(pc)) |
|
|
|
.find(|(_, handler)| handler.contains(pc)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Get the [`JsString`] constant from the [`CodeBlock`].
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Panics
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// If the type of the [`Constant`] is not [`Constant::String`].
|
|
|
|
|
|
|
|
/// Or `index` is greater or equal to length of `constants`.
|
|
|
|
|
|
|
|
pub(crate) fn constant_string(&self, index: usize) -> JsString { |
|
|
|
|
|
|
|
if let Some(Constant::String(value)) = self.constants.get(index) { |
|
|
|
|
|
|
|
return value.clone(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
panic!("expected string constant at index {index}") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Get the function ([`Gc<CodeBlock>`]) constant from the [`CodeBlock`].
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Panics
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// If the type of the [`Constant`] is not [`Constant::Function`].
|
|
|
|
|
|
|
|
/// Or `index` is greater or equal to length of `constants`.
|
|
|
|
|
|
|
|
pub(crate) fn constant_function(&self, index: usize) -> Gc<Self> { |
|
|
|
|
|
|
|
if let Some(Constant::Function(value)) = self.constants.get(index) { |
|
|
|
|
|
|
|
return value.clone(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
panic!("expected function constant at index {index}") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Get the [`CompileTimeEnvironment`] constant from the [`CodeBlock`].
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// # Panics
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// If the type of the [`Constant`] is not [`Constant::CompileTimeEnvironment`].
|
|
|
|
|
|
|
|
/// Or `index` is greater or equal to length of `constants`.
|
|
|
|
|
|
|
|
pub(crate) fn constant_compile_time_environment( |
|
|
|
|
|
|
|
&self, |
|
|
|
|
|
|
|
index: usize, |
|
|
|
|
|
|
|
) -> Rc<CompileTimeEnvironment> { |
|
|
|
|
|
|
|
if let Some(Constant::CompileTimeEnvironment(value)) = self.constants.get(index) { |
|
|
|
|
|
|
|
return value.clone(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
panic!("expected compile time environment constant at index {index}") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// ---- `CodeBlock` private API ----
|
|
|
|
/// ---- `CodeBlock` private API ----
|
|
|
@ -325,11 +368,11 @@ impl CodeBlock { |
|
|
|
pattern_index: source_index, |
|
|
|
pattern_index: source_index, |
|
|
|
flags_index: flag_index, |
|
|
|
flags_index: flag_index, |
|
|
|
} => { |
|
|
|
} => { |
|
|
|
let pattern = self.names[source_index.value() as usize] |
|
|
|
let pattern = self |
|
|
|
.clone() |
|
|
|
.constant_string(source_index.value() as usize) |
|
|
|
.to_std_string_escaped(); |
|
|
|
.to_std_string_escaped(); |
|
|
|
let flags = self.names[flag_index.value() as usize] |
|
|
|
let flags = self |
|
|
|
.clone() |
|
|
|
.constant_string(flag_index.value() as usize) |
|
|
|
.to_std_string_escaped(); |
|
|
|
.to_std_string_escaped(); |
|
|
|
format!("/{pattern}/{flags}") |
|
|
|
format!("/{pattern}/{flags}") |
|
|
|
} |
|
|
|
} |
|
|
@ -383,8 +426,8 @@ impl CodeBlock { |
|
|
|
let index = index.value() as usize; |
|
|
|
let index = index.value() as usize; |
|
|
|
format!( |
|
|
|
format!( |
|
|
|
"{index:04}: '{}' (length: {}), method: {method}", |
|
|
|
"{index:04}: '{}' (length: {}), method: {method}", |
|
|
|
self.functions[index].name().to_std_string_escaped(), |
|
|
|
self.constant_function(index).name().to_std_string_escaped(), |
|
|
|
self.functions[index].length |
|
|
|
self.constant_function(index).length |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
Instruction::GetArrowFunction { index } |
|
|
|
Instruction::GetArrowFunction { index } |
|
|
@ -394,8 +437,8 @@ impl CodeBlock { |
|
|
|
let index = index.value() as usize; |
|
|
|
let index = index.value() as usize; |
|
|
|
format!( |
|
|
|
format!( |
|
|
|
"{index:04}: '{}' (length: {})", |
|
|
|
"{index:04}: '{}' (length: {})", |
|
|
|
self.functions[index].name().to_std_string_escaped(), |
|
|
|
self.constant_function(index).name().to_std_string_escaped(), |
|
|
|
self.functions[index].length |
|
|
|
self.constant_function(index).length |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
Instruction::DefVar { index } |
|
|
|
Instruction::DefVar { index } |
|
|
@ -440,7 +483,8 @@ impl CodeBlock { |
|
|
|
format!( |
|
|
|
format!( |
|
|
|
"{:04}: '{}'", |
|
|
|
"{:04}: '{}'", |
|
|
|
index.value(), |
|
|
|
index.value(), |
|
|
|
self.names[index.value() as usize].to_std_string_escaped(), |
|
|
|
self.constant_string(index.value() as usize) |
|
|
|
|
|
|
|
.to_std_string_escaped(), |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
Instruction::PushPrivateEnvironment { name_indices } => { |
|
|
|
Instruction::PushPrivateEnvironment { name_indices } => { |
|
|
@ -693,17 +737,34 @@ impl ToInternedString for CodeBlock { |
|
|
|
count += 1; |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
f.push_str("\nLiterals:\n"); |
|
|
|
f.push_str("\nConstants:"); |
|
|
|
|
|
|
|
|
|
|
|
if self.literals.is_empty() { |
|
|
|
if self.constants.is_empty() { |
|
|
|
f.push_str(" <empty>\n"); |
|
|
|
f.push_str(" <empty>\n"); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
for (i, value) in self.literals.iter().enumerate() { |
|
|
|
f.push('\n'); |
|
|
|
f.push_str(&format!( |
|
|
|
for (i, value) in self.constants.iter().enumerate() { |
|
|
|
" {i:04}: <{}> {}\n", |
|
|
|
f.push_str(&format!(" {i:04}: ")); |
|
|
|
value.type_of(), |
|
|
|
let value = match value { |
|
|
|
value.display() |
|
|
|
Constant::String(v) => { |
|
|
|
)); |
|
|
|
format!("[STRING] \"{}\"", v.to_std_string_escaped().escape_debug()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Constant::BigInt(v) => format!("[BIGINT] {v}n"), |
|
|
|
|
|
|
|
Constant::Function(code) => format!( |
|
|
|
|
|
|
|
"[FUNCTION] name: '{}' (length: {})\n", |
|
|
|
|
|
|
|
code.name().to_std_string_escaped(), |
|
|
|
|
|
|
|
code.length |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
Constant::CompileTimeEnvironment(v) => { |
|
|
|
|
|
|
|
format!( |
|
|
|
|
|
|
|
"[ENVIRONMENT] index: {}, bindings: {}", |
|
|
|
|
|
|
|
v.environment_index(), |
|
|
|
|
|
|
|
v.num_bindings() |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
f.push_str(&value); |
|
|
|
|
|
|
|
f.push('\n'); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -719,19 +780,6 @@ impl ToInternedString for CodeBlock { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
f.push_str("\nFunctions:\n"); |
|
|
|
|
|
|
|
if self.functions.is_empty() { |
|
|
|
|
|
|
|
f.push_str(" <empty>\n"); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
for (i, code) in self.functions.iter().enumerate() { |
|
|
|
|
|
|
|
f.push_str(&format!( |
|
|
|
|
|
|
|
" {i:04}: name: '{}' (length: {})\n", |
|
|
|
|
|
|
|
code.name().to_std_string_escaped(), |
|
|
|
|
|
|
|
code.length |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
f.push_str("\nHandlers:\n"); |
|
|
|
f.push_str("\nHandlers:\n"); |
|
|
|
if self.handlers.is_empty() { |
|
|
|
if self.handlers.is_empty() { |
|
|
|
f.push_str(" <empty>\n"); |
|
|
|
f.push_str(" <empty>\n"); |
|
|
|