feat: works
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
//! `CallFrame`
|
||||
//!
|
||||
//! This module will provides everything needed to implement the `CallFrame`
|
||||
|
||||
use crate::vm::CodeBlock;
|
||||
use boa_gc::{Finalize, Gc, Trace};
|
||||
|
||||
#[derive(Clone, Debug, Finalize, Trace)]
|
||||
pub struct CallFrame {
|
||||
pub(crate) code: Gc<CodeBlock>,
|
||||
pub(crate) pc: usize,
|
||||
#[unsafe_ignore_trace]
|
||||
pub(crate) catch: Vec<CatchAddresses>,
|
||||
#[unsafe_ignore_trace]
|
||||
pub(crate) finally_return: FinallyReturn,
|
||||
pub(crate) finally_jump: Vec<Option<u32>>,
|
||||
pub(crate) pop_on_return: usize,
|
||||
|
||||
// Tracks the number of environments in the current loop block.
|
||||
// On abrupt returns this is used to decide how many environments need to be pop'ed.
|
||||
pub(crate) loop_env_stack: Vec<usize>,
|
||||
|
||||
// Tracks the number of environments in the current try-catch-finally block.
|
||||
// On abrupt returns this is used to decide how many environments need to be pop'ed.
|
||||
#[unsafe_ignore_trace]
|
||||
pub(crate) try_env_stack: Vec<TryStackEntry>,
|
||||
|
||||
pub(crate) param_count: usize,
|
||||
pub(crate) arg_count: usize,
|
||||
#[unsafe_ignore_trace]
|
||||
pub(crate) generator_resume_kind: GeneratorResumeKind,
|
||||
|
||||
// Indicate that the last try block has thrown an exception.
|
||||
pub(crate) thrown: bool,
|
||||
}
|
||||
|
||||
impl CallFrame {
|
||||
/// Tracks that one environment has been pushed in the current loop block.
|
||||
pub(crate) fn loop_env_stack_inc(&mut self) {
|
||||
*self
|
||||
.loop_env_stack
|
||||
.last_mut()
|
||||
.expect("loop environment stack entry must exist") += 1;
|
||||
}
|
||||
|
||||
/// Tracks that one environment has been pop'ed in the current loop block.
|
||||
pub(crate) fn loop_env_stack_dec(&mut self) {
|
||||
*self
|
||||
.loop_env_stack
|
||||
.last_mut()
|
||||
.expect("loop environment stack entry must exist") -= 1;
|
||||
}
|
||||
|
||||
/// Tracks that one environment has been pushed in the current try-catch-finally block.
|
||||
pub(crate) fn try_env_stack_inc(&mut self) {
|
||||
self.try_env_stack
|
||||
.last_mut()
|
||||
.expect("try environment stack entry must exist")
|
||||
.num_env += 1;
|
||||
}
|
||||
|
||||
/// Tracks that one environment has been pop'ed in the current try-catch-finally block.
|
||||
pub(crate) fn try_env_stack_dec(&mut self) {
|
||||
self.try_env_stack
|
||||
.last_mut()
|
||||
.expect("try environment stack entry must exist")
|
||||
.num_env -= 1;
|
||||
}
|
||||
|
||||
/// Tracks that one loop has started in the current try-catch-finally block.
|
||||
pub(crate) fn try_env_stack_loop_inc(&mut self) {
|
||||
self.try_env_stack
|
||||
.last_mut()
|
||||
.expect("try environment stack entry must exist")
|
||||
.num_loop_stack_entries += 1;
|
||||
}
|
||||
|
||||
/// Tracks that one loop has finished in the current try-catch-finally block.
|
||||
pub(crate) fn try_env_stack_loop_dec(&mut self) {
|
||||
self.try_env_stack
|
||||
.last_mut()
|
||||
.expect("try environment stack entry must exist")
|
||||
.num_loop_stack_entries -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks the number of environments in the current try-catch-finally block.
|
||||
///
|
||||
/// Because of the interactions between loops and try-catch-finally blocks,
|
||||
/// the number of loop blocks in the try-catch-finally block also needs to be tracked.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct TryStackEntry {
|
||||
pub(crate) num_env: usize,
|
||||
pub(crate) num_loop_stack_entries: usize,
|
||||
}
|
||||
|
||||
/// Tracks the address that should be jumped to when an error is caught.
|
||||
/// Additionally the address of a finally block is tracked, to allow for special handling if it exists.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct CatchAddresses {
|
||||
pub(crate) next: u32,
|
||||
pub(crate) finally: Option<u32>,
|
||||
}
|
||||
|
||||
/// Indicates if a function should return or throw at the end of a finally block.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub(crate) enum FinallyReturn {
|
||||
None,
|
||||
Ok,
|
||||
Err,
|
||||
}
|
||||
|
||||
/// Indicates how a generator function that has been called/resumed should return.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) enum GeneratorResumeKind {
|
||||
Normal,
|
||||
Throw,
|
||||
Return,
|
||||
}
|
||||
1329
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/code_block.rs
Normal file
1329
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/code_block.rs
Normal file
File diff suppressed because it is too large
Load Diff
2606
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/mod.rs
Normal file
2606
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
1525
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/opcode.rs
Normal file
1525
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/opcode.rs
Normal file
File diff suppressed because it is too large
Load Diff
139
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/tests.rs
Normal file
139
__wasm/wit-bindgen-sample/engine/boa/boa_engine/src/vm/tests.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
use crate::{exec, Context, JsValue};
|
||||
|
||||
#[test]
|
||||
fn typeof_string() {
|
||||
let typeof_object = r#"
|
||||
const a = "hello";
|
||||
typeof a;
|
||||
"#;
|
||||
assert_eq!(&exec(typeof_object), "\"string\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typeof_number() {
|
||||
let typeof_number = r#"
|
||||
let a = 1234;
|
||||
typeof a;
|
||||
"#;
|
||||
assert_eq!(&exec(typeof_number), "\"number\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_op() {
|
||||
let basic_op = r#"
|
||||
const a = 1;
|
||||
const b = 2;
|
||||
a + b
|
||||
"#;
|
||||
assert_eq!(&exec(basic_op), "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_catch_finally_from_init() {
|
||||
// the initialisation of the array here emits a PopOnReturnAdd op
|
||||
//
|
||||
// here we test that the stack is not popped more than intended due to multiple catches in the
|
||||
// same function, which could lead to VM stack corruption
|
||||
let source = r#"
|
||||
try {
|
||||
[(() => {throw "h";})()];
|
||||
} catch (x) {
|
||||
throw "h";
|
||||
} finally {
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_eq!(Context::default().eval(source.as_bytes()), Err("h".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_catches() {
|
||||
// see explanation on `try_catch_finally_from_init`
|
||||
let source = r#"
|
||||
try {
|
||||
try {
|
||||
[(() => {throw "h";})()];
|
||||
} catch (x) {
|
||||
throw "h";
|
||||
}
|
||||
} catch (y) {
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
Context::default().eval(source.as_bytes()),
|
||||
Ok(JsValue::Undefined)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_last_expr_try_block() {
|
||||
let source = r#"
|
||||
try {
|
||||
19;
|
||||
7.5;
|
||||
"Hello!";
|
||||
} catch (y) {
|
||||
14;
|
||||
"Bye!"
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
Context::default().eval(source.as_bytes()),
|
||||
Ok(JsValue::from("Hello!"))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn use_last_expr_catch_block() {
|
||||
let source = r#"
|
||||
try {
|
||||
throw Error("generic error");
|
||||
19;
|
||||
7.5;
|
||||
} catch (y) {
|
||||
14;
|
||||
"Hello!";
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
Context::default().eval(source.as_bytes()),
|
||||
Ok(JsValue::from("Hello!"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_use_last_expr_finally_block() {
|
||||
let source = r#"
|
||||
try {
|
||||
} catch (y) {
|
||||
} finally {
|
||||
"Unused";
|
||||
}
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
Context::default().eval(source.as_bytes()),
|
||||
Ok(JsValue::undefined())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finally_block_binding_env() {
|
||||
let source = r#"
|
||||
let buf = "Hey hey"
|
||||
try {
|
||||
} catch (y) {
|
||||
} finally {
|
||||
let x = " people";
|
||||
buf += x;
|
||||
}
|
||||
buf
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
Context::default().eval(source.as_bytes()),
|
||||
Ok(JsValue::from("Hey hey people"))
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user