feat: works

This commit is contained in:
2022-07-17 10:11:20 +08:00
parent 4ba63b4c2e
commit 74a202f1ed
458 changed files with 125067 additions and 8 deletions

View File

@@ -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,
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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"))
);
}