Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start on ByteCode interpreter #854

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
"kind": "build",
"isDefault": true
},
"presentation": {
"clear": true
},
"options": {
"env": {
"RUST_BACKTRACE": "full"
"RUST_BACKTRACE": "1"
}
},
"presentation": {
"clear": true
}
"problemMatcher": []
},
{
"type": "process",
Expand Down
3 changes: 3 additions & 0 deletions boa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ edition = "2018"
[features]
profiler = ["measureme", "once_cell"]

# Enable Bytecode generation & execution instead of tree walking
vm = []

# Enable Boa's WHATWG console object implementation.
console = []

Expand Down
52 changes: 52 additions & 0 deletions boa/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@ use crate::{
Parser,
},
value::{RcString, RcSymbol, Value},
vm::VM,
BoaProfiler, Executable, Result,
};
use std::result::Result as StdResult;

#[cfg(feature = "console")]
use crate::builtins::console::Console;

#[cfg(feature = "vm")]
use crate::vm::compilation::CodeGen;
use crate::vm::instructions::Instruction;

/// Store a builtin constructor (such as `Object`) and its corresponding prototype.
#[derive(Debug, Clone)]
pub struct StandardConstructor {
Expand Down Expand Up @@ -196,6 +201,10 @@ pub struct Context {

/// Cached standard objects and their prototypes
standard_objects: StandardObjects,

/// Holds instructions for ByteCode generation
#[cfg(feature = "vm")]
instruction_stack: Vec<Instruction>,
}

impl Default for Context {
Expand All @@ -212,6 +221,8 @@ impl Default for Context {
well_known_symbols,
iterator_prototypes: IteratorPrototypes::default(),
standard_objects: Default::default(),
#[cfg(feature = "vm")]
instruction_stack: vec![],
};

// Add new builtIns to Context Realm
Expand All @@ -237,6 +248,10 @@ impl Context {
&mut self.realm
}

pub fn instructions_mut(&mut self) -> &mut Vec<Instruction> {
&mut self.instruction_stack
}

pub fn executor(&mut self) -> &mut Interpreter {
&mut self.executor
}
Expand Down Expand Up @@ -649,6 +664,38 @@ impl Context {
execution_result
}

/// Evaluates the given code by compiling down to bytecode, then interpreting the bytecode into a value
///
/// # Examples
/// ```
///# use boa::Context;
/// let mut context = Context::new();
///
/// let value = context.eval_bytecode("1 + 3").unwrap();
///
/// assert!(value.is_number());
/// assert_eq!(value.as_number().unwrap(), 4.0);
/// ```
pub fn eval_bytecode(&mut self, src: &str) -> Result<Value> {
let main_timer = BoaProfiler::global().start_event("Main", "Main");

let parsing_result = Parser::new(src.as_bytes())
.parse_all()
.map_err(|e| e.to_string());

let statement_list = parsing_result.expect("unable to get statementList");
// Generate Bytecode and place it into instruction_stack
statement_list.compile(self);
// Interpret the Bytecode
let mut vm = VM::new(self);
let result = vm.run();
// The main_timer needs to be dropped before the BoaProfiler is.
drop(main_timer);
BoaProfiler::global().drop();

result
}

/// Returns a structure that contains the JavaScript well known symbols.
///
/// # Examples
Expand Down Expand Up @@ -676,4 +723,9 @@ impl Context {
pub fn standard_objects(&self) -> &StandardObjects {
&self.standard_objects
}

// Add a new instruction
pub fn add_instruction(&mut self, instr: Instruction) {
self.instruction_stack.push(instr);
}
}
1 change: 1 addition & 0 deletions boa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub mod property;
pub mod realm;
pub mod syntax;
pub mod value;
pub mod vm;

pub mod context;

Expand Down
18 changes: 18 additions & 0 deletions boa/src/syntax/ast/node/operator/bin_op/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use crate::{
node::Node,
op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp},
},
vm::compilation::CodeGen,
vm::instructions::Instruction,
Context, Result, Value,
};
use gc::{Finalize, Trace};
Expand Down Expand Up @@ -185,6 +187,22 @@ impl Executable for BinOp {
}
}

impl CodeGen for BinOp {
fn compile(&self, ctx: &mut Context) {
match self.op() {
op::BinOp::Num(op) => {
self.lhs().compile(ctx);
self.rhs().compile(ctx);
match op {
NumOp::Add => ctx.add_instruction(Instruction::Add),
_ => unimplemented!(),
}
}
_ => unimplemented!(),
}
}
}

impl fmt::Display for BinOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.lhs, self.op, self.rhs)
Expand Down
15 changes: 15 additions & 0 deletions boa/src/syntax/ast/node/statement_list/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::{
exec::{Executable, InterpreterState},
syntax::ast::node::Node,
vm::compilation::CodeGen,
BoaProfiler, Context, Result, Value,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
Expand Down Expand Up @@ -94,6 +95,20 @@ impl Executable for StatementList {
}
}

impl CodeGen for StatementList {
fn compile(&self, ctx: &mut Context) {
let _timer = BoaProfiler::global().start_event("StatementList", "codeGen");

// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
// The return value is uninitialized, which means it defaults to Value::Undefined
ctx.executor()
.set_current_state(InterpreterState::Executing);
for (i, item) in self.statements().iter().enumerate() {
item.compile(ctx);
}
}
}

impl<T> From<T> for StatementList
where
T: Into<Box<[Node]>>,
Expand Down
22 changes: 22 additions & 0 deletions boa/src/vm/compilation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use super::*;
use crate::{syntax::ast::Const, syntax::ast::Node, Context};

#[derive(Debug, Default)]
pub(crate) struct Compiler {
res: Vec<Instruction>,
next_free: u8,
}

pub(crate) trait CodeGen {
fn compile(&self, ctx: &mut Context);
}

impl CodeGen for Node {
fn compile(&self, ctx: &mut Context) {
match *self {
Node::BinOp(ref op) => op.compile(ctx),
Node::Const(Const::Int(num)) => ctx.add_instruction(Instruction::Int32(num)),
_ => unimplemented!(),
}
}
}
20 changes: 20 additions & 0 deletions boa/src/vm/instructions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use std::fmt::{Debug, Error, Formatter};

#[derive(Clone, Copy)]
pub enum Instruction {
/// Adds the values from destination and source and stores the result in destination
Add,

// Loads an i32 onto the stack
Int32(i32),
}

impl Debug for Instruction {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
match self {
Self::Add => write!(f, "Add"),
Self::Int32(i) => write!(f, "Int32\t{}", format!("{}", i)),
_ => write!(f, "unimplemented"),
}
}
}
50 changes: 50 additions & 0 deletions boa/src/vm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use self::instructions::Instruction;
use crate::{Context, Value};

pub(crate) mod compilation;
pub(crate) mod instructions;

// === Execution
#[derive(Debug)]
pub struct VM<'a> {
ctx: &'a mut Context,
instructions: Vec<Instruction>,
stack: Vec<Value>,
stack_pointer: usize,
}

impl<'a> VM<'a> {
pub fn new(ctx: &'a mut Context) -> Self {
let instr = ctx.instructions_mut().clone();
VM {
ctx,
instructions: instr,
stack: vec![],
stack_pointer: 0,
}
}

pub fn run(&mut self) -> super::Result<Value> {
let mut idx = 0;

while idx < self.instructions.len() {
match self.instructions[idx] {
Instruction::Int32(i) => self.stack.push(Value::Integer(i)),
Instruction::Add => {
let r = self.stack.pop().unwrap();
let l = self.stack.pop().unwrap();
let val = l.add(&r, self.ctx)?;

self.stack.push(val);
}

_ => unimplemented!(),
}

idx += 1;
}

let res = self.stack.pop().unwrap();
Ok(res)
}
}
5 changes: 4 additions & 1 deletion boa_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ edition = "2018"
default-run = "boa"

[dependencies]
Boa = { path = "../boa", features = ["serde", "console"] }
Boa = { path = "../boa", features = ["serde", "console", "vm"] }
rustyline = "6.3.0"
rustyline-derive = "0.3.1"
structopt = "0.3.20"
Expand All @@ -21,6 +21,9 @@ colored = "2.0.0"
regex = "1.4.0"
lazy_static = "1.4.0"

# [features]
# vm = ["Boa/vm"]

[target.x86_64-unknown-linux-gnu.dependencies]
jemallocator = "0.3.2"

Expand Down
5 changes: 4 additions & 1 deletion boa_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ use rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
use std::{fs::read_to_string, path::PathBuf};
use structopt::{clap::arg_enum, StructOpt};

#[cfg(feature = "vm")]
use boa::vm::VM;

mod helper;

#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
Expand Down Expand Up @@ -149,7 +152,7 @@ pub fn main() -> Result<(), std::io::Error> {
eprintln!("{}", e);
}
} else {
match engine.eval(&buffer) {
match engine.eval_bytecode(&buffer) {
Ok(v) => println!("{}", v.display()),
Err(v) => eprintln!("Uncaught {}", v.display()),
}
Expand Down