Browse Source

fix VM branch (#1302)

Co-authored-by: Jason Williams <jwilliams720@bloomberg.net>
pull/1310/head
Jason Williams 3 years ago committed by GitHub
parent
commit
ba3c4e5945
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      .github/workflows/rust.yml
  2. 24
      .vscode/tasks.json
  3. 2
      boa/src/exec/mod.rs
  4. 2
      boa/src/syntax/ast/node/block/mod.rs
  5. 14
      boa/src/syntax/ast/node/identifier/mod.rs
  6. 2
      boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs
  7. 2
      boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs
  8. 2
      boa/src/syntax/ast/node/iteration/for_loop/mod.rs
  9. 2
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  10. 2
      boa/src/syntax/ast/node/iteration/while_loop/mod.rs
  11. 20
      boa/src/syntax/ast/node/object/mod.rs
  12. 2
      boa/src/syntax/ast/node/statement_list/mod.rs
  13. 2
      boa/src/syntax/ast/node/switch/mod.rs
  14. 3
      boa/src/vm/compilation.rs
  15. 11
      boa/src/vm/instructions.rs
  16. 66
      boa/src/vm/mod.rs
  17. 29
      boa/src/vm/tests.rs

22
.github/workflows/rust.yml

@ -96,6 +96,28 @@ jobs:
# args: --ignore-tests
# - name: Upload to codecov.io
# uses: codecov/codecov-action@v1
test_vm_on_linux:
name: Test VM on Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- uses: actions-rs/toolchain@v1.0.7
with:
toolchain: stable
override: true
profile: minimal
- name: Cache cargo
uses: actions/cache@v2.1.6
with:
path: |
target
~/.cargo/git
~/.cargo/registry
key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
command: test
args: ---package Boa --lib --features=vm -- vm --nocapture
test_on_windows:
name: Test Suite on Windows

24
.vscode/tasks.json vendored

@ -59,6 +59,30 @@
},
"problemMatcher": []
},
{
"type": "process",
"label": "Cargo Run (Profiler & VM)",
"command": "cargo",
"args": [
"run",
"--features",
"Boa/profiler",
"--features",
"vm",
"../tests/js/test.js"
],
"group": "build",
"options": {
"env": {
"RUST_BACKTRACE": "full"
},
"cwd": "${workspaceFolder}/boa_cli"
},
"presentation": {
"clear": true
},
"problemMatcher": []
},
{
"type": "process",
"label": "Get Tokens",

2
boa/src/exec/mod.rs

@ -14,6 +14,8 @@ pub trait Executable {
pub(crate) enum InterpreterState {
Executing,
Return,
#[cfg(feature = "vm")]
Error,
Break(Option<Box<str>>),
Continue(Option<Box<str>>),
}

2
boa/src/syntax/ast/node/block/mod.rs

@ -87,6 +87,8 @@ impl Executable for Block {
InterpreterState::Executing => {
// Continue execution
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
}

14
boa/src/syntax/ast/node/identifier/mod.rs

@ -4,13 +4,16 @@ use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::Node,
Context, Result, Value,
BoaProfiler, Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "vm")]
use crate::vm::{compilation::CodeGen, Compiler, Instruction};
/// An `identifier` is a sequence of characters in the code that identifies a variable,
/// function, or property.
///
@ -36,10 +39,19 @@ pub struct Identifier {
impl Executable for Identifier {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Identifier", "exec");
context.get_binding_value(self.as_ref())
}
}
#[cfg(feature = "vm")]
impl CodeGen for Identifier {
fn compile(&self, compiler: &mut Compiler) {
let _timer = BoaProfiler::global().start_event("Identifier", "codeGen");
compiler.add_instruction(Instruction::GetName(String::from(self.ident.as_ref())));
}
}
impl fmt::Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.ident, f)

2
boa/src/syntax/ast/node/iteration/do_while_loop/mod.rs

@ -89,6 +89,8 @@ impl Executable for DoWhileLoop {
InterpreterState::Executing => {
// Continue execution.
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
if !self.cond().run(context)?.to_boolean() {
break;

2
boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs

@ -205,6 +205,8 @@ impl Executable for ForInLoop {
InterpreterState::Executing => {
// Continue execution.
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
let _ = context.pop_environment();
}

2
boa/src/syntax/ast/node/iteration/for_loop/mod.rs

@ -133,6 +133,8 @@ impl Executable for ForLoop {
InterpreterState::Executing => {
// Continue execution.
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
if let Some(final_expr) = self.final_expr() {

2
boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs

@ -196,6 +196,8 @@ impl Executable for ForOfLoop {
InterpreterState::Executing => {
// Continue execution.
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
let _ = context.pop_environment();
}

2
boa/src/syntax/ast/node/iteration/while_loop/mod.rs

@ -87,6 +87,8 @@ impl Executable for WhileLoop {
InterpreterState::Executing => {
// Continue execution.
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
}
Ok(result)

20
boa/src/syntax/ast/node/object/mod.rs

@ -5,13 +5,16 @@ use crate::{
gc::{Finalize, Trace},
property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor},
syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition},
Context, Result, Value,
BoaProfiler, Context, Result, Value,
};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "vm")]
use crate::vm::{compilation::CodeGen, Compiler, Instruction};
/// Objects in JavaScript may be defined as an unordered collection of related data, of
/// primitive or reference types, in the form of “key: value” pairs.
///
@ -71,8 +74,23 @@ impl Object {
}
}
#[cfg(feature = "vm")]
impl CodeGen for Object {
fn compile(&self, compiler: &mut Compiler) {
let _timer = BoaProfiler::global().start_event("object", "codeGen");
// Is it a new empty object?
if self.properties.len() == 0 {
compiler.add_instruction(Instruction::NewObject);
return;
}
unimplemented!()
}
}
impl Executable for Object {
fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("object", "exec");
let obj = Value::new_object(context);
// TODO: Implement the rest of the property types.

2
boa/src/syntax/ast/node/statement_list/mod.rs

@ -123,6 +123,8 @@ impl Executable for StatementList {
InterpreterState::Executing => {
// Continue execution
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
if i + 1 == self.items().len() {
obj = val;

2
boa/src/syntax/ast/node/switch/mod.rs

@ -161,6 +161,8 @@ impl Executable for Switch {
// Continuing execution / falling through to next case statement(s).
fall_through = true;
}
#[cfg(feature = "vm")]
InterpreterState::Error => {}
}
}
}

3
boa/src/vm/compilation.rs

@ -103,6 +103,9 @@ impl CodeGen for Node {
};
}
}
Node::Identifier(ref name) => name.compile(compiler),
Node::Object(ref obj) => obj.compile(compiler),
_ => unimplemented!(),
}
}

11
boa/src/vm/instructions.rs

@ -1,4 +1,4 @@
#[derive(Debug, Clone, Copy)]
#[derive(Debug)]
pub enum Instruction {
Undefined,
Null,
@ -66,6 +66,13 @@ pub enum Instruction {
DefConst(usize),
/// The usize is the index of the value to initiate the variable with in the pool
InitLexical(usize),
// Binding values
/// Find a binding on the environment chain and push its value.
GetName(String),
// Objects
// Create and push a new object onto the stack
NewObject,
}
impl std::fmt::Display for Instruction {
@ -90,6 +97,7 @@ impl std::fmt::Display for Instruction {
Self::BitAnd => write!(f, "BitAnd"),
Self::BitOr => write!(f, "BitOr"),
Self::BitXor => write!(f, "BitXor"),
Self::GetName(ref name) => write!(f, "GetName({})", name),
Self::Shl => write!(f, "Shl"),
Self::Shr => write!(f, "Shr"),
Self::UShr => write!(f, "UShr"),
@ -113,6 +121,7 @@ impl std::fmt::Display for Instruction {
Self::DefLet(name) => write!(f, "DefLet({})", name),
Self::DefConst(name) => write!(f, "DefConst({})", name),
Self::InitLexical(value) => write!(f, "InitLexical({})", value),
Self::NewObject => write!(f, "NewObject"),
}
}
}

66
boa/src/vm/mod.rs

@ -1,7 +1,10 @@
//! The Virtual Machine (VM) handles generating instructions, then executing them.
//! This module will provide an instruction set for the AST to use, various traits, plus an interpreter to execute those instructions
use crate::{environment::lexical_environment::VariableScope, BoaProfiler, Context, Result, Value};
use crate::{
environment::lexical_environment::VariableScope, exec::InterpreterState, BoaProfiler, Context,
Result, Value,
};
pub(crate) mod compilation;
pub(crate) mod instructions;
@ -31,6 +34,9 @@ struct Profiler {
start_flag: bool,
}
#[cfg(test)]
mod tests;
impl<'a> VM<'a> {
pub fn new(compiler: Compiler, ctx: &'a mut Context) -> Self {
let trace = ctx.trace;
@ -227,53 +233,69 @@ impl<'a> VM<'a> {
Instruction::DefVar(name_index) => {
let name: String = self.pool[name_index].to_string(self.ctx)?.to_string();
self.ctx
.realm_mut()
.environment
.create_mutable_binding(name.to_string(), false, VariableScope::Function)
.map_err(|e| e.to_error(self.ctx))?;
self.ctx.create_mutable_binding(
name.to_string(),
false,
VariableScope::Function,
)?;
None
}
Instruction::DefLet(name_index) => {
let name = self.pool[name_index].to_string(self.ctx)?;
self.ctx
.realm_mut()
.environment
.create_mutable_binding(name.to_string(), false, VariableScope::Block)
.map_err(|e| e.to_error(self.ctx))?;
self.ctx.create_mutable_binding(
name.to_string(),
false,
VariableScope::Block,
)?;
None
}
Instruction::DefConst(name_index) => {
let name = self.pool[name_index].to_string(self.ctx)?;
self.ctx
.realm_mut()
.environment
.create_immutable_binding(name.to_string(), false, VariableScope::Block)
.map_err(|e| e.to_error(self.ctx))?;
self.ctx.create_immutable_binding(
name.to_string(),
false,
VariableScope::Block,
)?;
None
}
Instruction::InitLexical(name_index) => {
let name = self.pool[name_index].to_string(self.ctx)?;
let value = self.pop();
self.ctx
.realm_mut()
.environment
.initialize_binding(&name, value.clone())
.map_err(|e| e.to_error(self.ctx))?;
self.ctx.initialize_binding(&name, value.clone())?;
Some(value)
None
}
// Find a binding on the environment chain and push its value.
Instruction::GetName(ref name) => match self.ctx.get_binding_value(&name) {
Ok(val) => Some(val),
Err(val) => {
self.ctx
.executor()
.set_current_state(InterpreterState::Error);
Some(val)
}
},
// Create a new object and push to the stack
Instruction::NewObject => Some(Value::new_object(self.ctx)),
};
if let Some(value) = result {
self.push(value);
}
self.idx += 1;
if matches!(
self.ctx.executor().get_current_state(),
&InterpreterState::Error,
) {
break;
}
}
if self.is_trace {

29
boa/src/vm/tests.rs

@ -0,0 +1,29 @@
use crate::exec;
#[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");
}
Loading…
Cancel
Save