Browse Source

fix VM branch (#1302)

Co-authored-by: Jason Williams <jwilliams720@bloomberg.net>
pull/1310/head
Jason Williams 4 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 # args: --ignore-tests
# - name: Upload to codecov.io # - name: Upload to codecov.io
# uses: codecov/codecov-action@v1 # 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: test_on_windows:
name: Test Suite on Windows name: Test Suite on Windows

24
.vscode/tasks.json vendored

@ -59,6 +59,30 @@
}, },
"problemMatcher": [] "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", "type": "process",
"label": "Get Tokens", "label": "Get Tokens",

2
boa/src/exec/mod.rs

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

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

@ -4,13 +4,16 @@ use crate::{
exec::Executable, exec::Executable,
gc::{Finalize, Trace}, gc::{Finalize, Trace},
syntax::ast::node::Node, syntax::ast::node::Node,
Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
use std::fmt; use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; 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, /// An `identifier` is a sequence of characters in the code that identifies a variable,
/// function, or property. /// function, or property.
/// ///
@ -36,10 +39,19 @@ pub struct Identifier {
impl Executable for Identifier { impl Executable for Identifier {
fn run(&self, context: &mut Context) -> Result<Value> { fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Identifier", "exec");
context.get_binding_value(self.as_ref()) 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 { impl fmt::Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.ident, f) 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 => { InterpreterState::Executing => {
// Continue execution. // Continue execution.
} }
#[cfg(feature = "vm")]
InterpreterState::Error => {}
} }
if !self.cond().run(context)?.to_boolean() { if !self.cond().run(context)?.to_boolean() {
break; break;

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

@ -205,6 +205,8 @@ impl Executable for ForInLoop {
InterpreterState::Executing => { InterpreterState::Executing => {
// Continue execution. // Continue execution.
} }
#[cfg(feature = "vm")]
InterpreterState::Error => {}
} }
let _ = context.pop_environment(); 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 => { InterpreterState::Executing => {
// Continue execution. // Continue execution.
} }
#[cfg(feature = "vm")]
InterpreterState::Error => {}
} }
if let Some(final_expr) = self.final_expr() { 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 => { InterpreterState::Executing => {
// Continue execution. // Continue execution.
} }
#[cfg(feature = "vm")]
InterpreterState::Error => {}
} }
let _ = context.pop_environment(); 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 => { InterpreterState::Executing => {
// Continue execution. // Continue execution.
} }
#[cfg(feature = "vm")]
InterpreterState::Error => {}
} }
} }
Ok(result) Ok(result)

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

@ -5,13 +5,16 @@ use crate::{
gc::{Finalize, Trace}, gc::{Finalize, Trace},
property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor}, property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor},
syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition}, syntax::ast::node::{MethodDefinitionKind, Node, PropertyDefinition},
Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
use std::fmt; use std::fmt;
#[cfg(feature = "deser")] #[cfg(feature = "deser")]
use serde::{Deserialize, Serialize}; 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 /// 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. /// 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 { impl Executable for Object {
fn run(&self, context: &mut Context) -> Result<Value> { fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("object", "exec");
let obj = Value::new_object(context); let obj = Value::new_object(context);
// TODO: Implement the rest of the property types. // 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 => { InterpreterState::Executing => {
// Continue execution // Continue execution
} }
#[cfg(feature = "vm")]
InterpreterState::Error => {}
} }
if i + 1 == self.items().len() { if i + 1 == self.items().len() {
obj = val; 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). // Continuing execution / falling through to next case statement(s).
fall_through = true; 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!(), _ => unimplemented!(),
} }
} }

11
boa/src/vm/instructions.rs

@ -1,4 +1,4 @@
#[derive(Debug, Clone, Copy)] #[derive(Debug)]
pub enum Instruction { pub enum Instruction {
Undefined, Undefined,
Null, Null,
@ -66,6 +66,13 @@ pub enum Instruction {
DefConst(usize), DefConst(usize),
/// The usize is the index of the value to initiate the variable with in the pool /// The usize is the index of the value to initiate the variable with in the pool
InitLexical(usize), 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 { impl std::fmt::Display for Instruction {
@ -90,6 +97,7 @@ impl std::fmt::Display for Instruction {
Self::BitAnd => write!(f, "BitAnd"), Self::BitAnd => write!(f, "BitAnd"),
Self::BitOr => write!(f, "BitOr"), Self::BitOr => write!(f, "BitOr"),
Self::BitXor => write!(f, "BitXor"), Self::BitXor => write!(f, "BitXor"),
Self::GetName(ref name) => write!(f, "GetName({})", name),
Self::Shl => write!(f, "Shl"), Self::Shl => write!(f, "Shl"),
Self::Shr => write!(f, "Shr"), Self::Shr => write!(f, "Shr"),
Self::UShr => write!(f, "UShr"), Self::UShr => write!(f, "UShr"),
@ -113,6 +121,7 @@ impl std::fmt::Display for Instruction {
Self::DefLet(name) => write!(f, "DefLet({})", name), Self::DefLet(name) => write!(f, "DefLet({})", name),
Self::DefConst(name) => write!(f, "DefConst({})", name), Self::DefConst(name) => write!(f, "DefConst({})", name),
Self::InitLexical(value) => write!(f, "InitLexical({})", value), 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. //! 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 //! 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 compilation;
pub(crate) mod instructions; pub(crate) mod instructions;
@ -31,6 +34,9 @@ struct Profiler {
start_flag: bool, start_flag: bool,
} }
#[cfg(test)]
mod tests;
impl<'a> VM<'a> { impl<'a> VM<'a> {
pub fn new(compiler: Compiler, ctx: &'a mut Context) -> Self { pub fn new(compiler: Compiler, ctx: &'a mut Context) -> Self {
let trace = ctx.trace; let trace = ctx.trace;
@ -227,53 +233,69 @@ impl<'a> VM<'a> {
Instruction::DefVar(name_index) => { Instruction::DefVar(name_index) => {
let name: String = self.pool[name_index].to_string(self.ctx)?.to_string(); let name: String = self.pool[name_index].to_string(self.ctx)?.to_string();
self.ctx self.ctx.create_mutable_binding(
.realm_mut() name.to_string(),
.environment false,
.create_mutable_binding(name.to_string(), false, VariableScope::Function) VariableScope::Function,
.map_err(|e| e.to_error(self.ctx))?; )?;
None None
} }
Instruction::DefLet(name_index) => { Instruction::DefLet(name_index) => {
let name = self.pool[name_index].to_string(self.ctx)?; let name = self.pool[name_index].to_string(self.ctx)?;
self.ctx self.ctx.create_mutable_binding(
.realm_mut() name.to_string(),
.environment false,
.create_mutable_binding(name.to_string(), false, VariableScope::Block) VariableScope::Block,
.map_err(|e| e.to_error(self.ctx))?; )?;
None None
} }
Instruction::DefConst(name_index) => { Instruction::DefConst(name_index) => {
let name = self.pool[name_index].to_string(self.ctx)?; let name = self.pool[name_index].to_string(self.ctx)?;
self.ctx self.ctx.create_immutable_binding(
.realm_mut() name.to_string(),
.environment false,
.create_immutable_binding(name.to_string(), false, VariableScope::Block) VariableScope::Block,
.map_err(|e| e.to_error(self.ctx))?; )?;
None None
} }
Instruction::InitLexical(name_index) => { Instruction::InitLexical(name_index) => {
let name = self.pool[name_index].to_string(self.ctx)?; let name = self.pool[name_index].to_string(self.ctx)?;
let value = self.pop(); let value = self.pop();
self.ctx self.ctx.initialize_binding(&name, value.clone())?;
.realm_mut()
.environment
.initialize_binding(&name, value.clone())
.map_err(|e| e.to_error(self.ctx))?;
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 { if let Some(value) = result {
self.push(value); self.push(value);
} }
self.idx += 1; self.idx += 1;
if matches!(
self.ctx.executor().get_current_state(),
&InterpreterState::Error,
) {
break;
}
} }
if self.is_trace { 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