summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2023-12-31 09:50:08 -0800
committerJesse Morgan <jesse@jesterpm.net>2023-12-31 09:50:08 -0800
commitd8d335d0cfd0a420b25ecc6c97bf5c0020e7ac16 (patch)
tree6c57dc9d840d03a6333fb4d63e378c23f5d48fd7
parent9ea74605c9537321a35f96bf41e667c3bf02cd7a (diff)
Move Compiler into new module
-rw-r--r--demo/script.js2
-rw-r--r--src/js.rs666
-rw-r--r--src/main.rs705
-rw-r--r--src/runtime.rs26
4 files changed, 703 insertions, 696 deletions
diff --git a/demo/script.js b/demo/script.js
index c829cc0..8a21276 100644
--- a/demo/script.js
+++ b/demo/script.js
@@ -1,5 +1,5 @@
-function lowest_price() {
+function main() {
const offers = [{ id: 1, price: 2.01}, { id: 2, price: 2.99 }];
let min_price = null;
diff --git a/src/js.rs b/src/js.rs
new file mode 100644
index 0000000..8de188b
--- /dev/null
+++ b/src/js.rs
@@ -0,0 +1,666 @@
+use std::collections::BTreeMap;
+
+use boa_ast::Declaration;
+use boa_ast::Expression;
+use boa_ast::Script;
+use boa_ast::Statement;
+use boa_ast::StatementList;
+use boa_ast::StatementListItem;
+use boa_ast::declaration::Binding;
+use boa_ast::expression::access::PropertyAccess;
+use boa_ast::expression::access::PropertyAccessField;
+use boa_ast::expression::literal::Literal;
+use boa_ast::expression::operator::Assign;
+use boa_ast::expression::operator::Binary;
+use boa_ast::expression::operator::assign::AssignTarget;
+use boa_ast::expression::operator::binary::BinaryOp;
+use boa_ast::expression::operator::binary::LogicalOp;
+use boa_ast::expression::operator::binary::RelationalOp;
+use boa_ast::property::PropertyDefinition;
+use boa_ast::property::PropertyName;
+use boa_ast::statement::iteration::IterableLoopInitializer;
+use boa_interner::Interner;
+use boa_interner::Sym;
+
+use ion_rs::SymbolTable;
+
+use crate::ion::IonValue;
+use crate::runtime::Function;
+use crate::runtime::OpCode;
+use crate::runtime::Runtime;
+
+
+pub struct JsCompiler {
+ interner: Interner,
+ symbols: SymbolTable,
+ vars: Vec<BTreeMap<String, usize>>,
+ max_var: Vec<usize>,
+ bytecode: Vec<u8>,
+ functions: Vec<Function>,
+}
+
+impl JsCompiler {
+
+ pub fn new(interner: Interner) -> JsCompiler {
+ let mut compiler = JsCompiler {
+ interner,
+ symbols: SymbolTable::new(),
+ vars: Vec::new(),
+ max_var: vec![0],
+ bytecode: Vec::new(),
+ functions: Vec::new(),
+ };
+ compiler.enter_block();
+ compiler
+ }
+
+ fn enter_block(&mut self) {
+ let mut vars = if let Some(vars) = self.vars.last() {
+ vars.clone()
+ } else {
+ BTreeMap::new()
+ };
+ vars.insert("this".to_string(), 0);
+ self.vars.push(vars);
+ }
+
+ fn exit_block(&mut self) {
+ self.vars.pop();
+ }
+
+ fn get_or_create_variable(&mut self, sym: Sym) -> usize {
+ let name = self.interner.resolve_expect(sym).to_string();
+ let idmaybe = self.vars.last().unwrap().len();
+ let id = *self.vars.last_mut().unwrap().entry(name).or_insert(idmaybe);
+ let max_var = *self.max_var.last().unwrap().max(&id);
+ self.max_var.pop();
+ self.max_var.push(max_var);
+ id
+ }
+
+ fn set_jump_target(&mut self, jump_pc: usize, target_pc: usize) {
+ let target_32: u32 = target_pc.try_into().unwrap();
+ for (i, b) in target_32.to_be_bytes().iter().enumerate() {
+ self.bytecode[jump_pc + 1 + i] = *b;
+ }
+ }
+
+ /// Attach a previous jump instruction to the next instruction.
+ fn set_jump(&mut self, jump_pc: usize) {
+ self.set_jump_target(jump_pc, self.bytecode.len())
+ }
+
+ fn push_jump(&mut self) -> usize {
+ self.bytecode.push(OpCode::Jump.into());
+ self.push_u32(0xFFFFFFFF);
+ self.bytecode.len() - 5
+ }
+
+ fn push_dup(&mut self) {
+ self.bytecode.push(OpCode::Dup.into());
+ }
+
+ fn push_dup2(&mut self) {
+ self.bytecode.push(OpCode::Dup2.into());
+ }
+
+ fn push_swappop(&mut self) {
+ self.bytecode.push(OpCode::SwapPop.into());
+ }
+
+ fn push_push(&mut self, value: &IonValue) {
+ self.bytecode.push(OpCode::TypePush.into());
+ self.bytecode.extend(value.bytes());
+ }
+
+ fn push_pop(&mut self) {
+ self.bytecode.push(OpCode::TypePop.into());
+ }
+
+ fn push_load(&mut self, varid: usize) {
+ self.bytecode.push(OpCode::TypeLoad.into());
+ self.push_varuint(varid);
+ }
+
+ fn push_store(&mut self, varid: usize) {
+ self.bytecode.push(OpCode::TypeStore.into());
+ self.push_varuint(varid);
+ }
+
+ fn push_iterate(&mut self) {
+ self.bytecode.push(OpCode::Iterator.into());
+ }
+
+ fn push_next(&mut self) {
+ self.bytecode.push(OpCode::Next.into());
+ }
+
+ fn push_step_in(&mut self, varid: usize) {
+ self.bytecode.push(OpCode::StepIn.into());
+ self.push_varuint(varid);
+ }
+
+ fn push_step_out(&mut self) {
+ self.bytecode.push(OpCode::StepOut.into());
+ }
+
+ fn push_load_value(&mut self) {
+ self.bytecode.push(OpCode::LoadValue.into());
+ }
+
+ fn push_load_field(&mut self) {
+ self.bytecode.push(OpCode::StructField.into());
+ }
+
+ fn push_ifeq2(&mut self) -> usize {
+ self.bytecode.push(OpCode::IfEq2.into());
+ self.push_u32(0xFFFFFFFF);
+ self.bytecode.len() - 5
+ }
+ fn push_ifne2(&mut self) -> usize {
+ self.bytecode.push(OpCode::IfNe2.into());
+ self.push_u32(0xFFFFFFFF);
+ self.bytecode.len() - 5
+ }
+ fn push_ifgt2(&mut self) -> usize {
+ self.bytecode.push(OpCode::IfGt2.into());
+ self.push_u32(0xFFFFFFFF);
+ self.bytecode.len() - 5
+ }
+ fn push_ifge2(&mut self) -> usize {
+ self.bytecode.push(OpCode::IfGe2.into());
+ self.push_u32(0xFFFFFFFF);
+ self.bytecode.len() - 5
+ }
+ fn push_iflt2(&mut self) -> usize {
+ self.bytecode.push(OpCode::IfLt2.into());
+ self.push_u32(0xFFFFFFFF);
+ self.bytecode.len() - 5
+ }
+
+ fn push_ifle2(&mut self) -> usize {
+ self.bytecode.push(OpCode::IfLe2.into());
+ self.push_u32(0xFFFFFFFF);
+ self.bytecode.len() - 5
+ }
+
+
+ fn push_newlist(&mut self) {
+ self.bytecode.push(OpCode::NewList.into());
+ }
+
+ fn push_newstruct(&mut self) {
+ self.bytecode.push(OpCode::NewStruct.into());
+ }
+
+ fn push_varuint(&mut self, mut value: usize) {
+ let mut buf = [0; (usize::BITS / 7 + 1) as usize];
+ let mut pos = 0;
+ while value != 0 {
+ buf[pos] = (value & 0x7F) as u8;
+ value >>= 7;
+ pos += 1;
+ }
+ buf[0] |= 0x80;
+ pos = pos.max(1);
+
+ for i in (0..pos).rev() {
+ self.bytecode.push(buf[i]);
+ }
+ }
+
+ fn push_u32(&mut self, value: u32) {
+ self.bytecode.extend(value.to_be_bytes())
+ }
+
+
+ pub fn compile_declaration(&mut self, decl: &Declaration) {
+ match decl {
+ Declaration::Function(func) => {
+ self.max_var.push(*self.max_var.last().unwrap());
+ let pc = self.bytecode.len();
+ self.enter_block();
+ self.compile_statement_list(func.body().statements());
+ self.exit_block();
+
+ let name_symbol = if let Some(name) = func.name() {
+ self.to_symbol_id(name.sym())
+ } else {
+ self.new_symbol_id(format!("anonFunction{}", self.functions.len()))
+ };
+
+ self.functions.push(Function {
+ name_symbol,
+ arguments: func.parameters().length() as u16,
+ variables: (self.max_var.pop().unwrap() + 1) as u16,
+ pc,
+ expected_stack_depth: 32, // TODO FIXME
+ });
+ },
+ Declaration::Generator(_) => todo!(),
+ Declaration::AsyncFunction(_) => todo!(),
+ Declaration::AsyncGenerator(_) => todo!(),
+ Declaration::Class(_) => todo!(),
+ Declaration::Lexical(decl) => {
+ for v in decl.variable_list().as_ref() {
+ match v.binding() {
+ Binding::Pattern(_) => todo!(),
+ Binding::Identifier(ident) => {
+ let varid = self.get_or_create_variable(ident.sym());
+ if let Some(init) = v.init() {
+ self.compile_expression(init);
+ } else {
+ self.push_push(&IonValue::new_null());
+ }
+ self.push_store(varid);
+ }
+ }
+ }
+ },
+ }
+ }
+
+ fn new_symbol_id(&mut self, s: String) -> usize {
+ self.symbols.intern(s)
+ }
+
+ fn to_symbol_id(&mut self, sym: Sym) -> usize {
+ let s = self.interner.resolve_expect(sym).to_string();
+ self.symbols.intern(s)
+ }
+
+ fn to_ion_symbol(&mut self, sym: Sym) -> IonValue {
+ IonValue::new_symbol(self.to_symbol_id(sym))
+ }
+
+ fn literal_to_ion(&mut self, literal: &Literal) -> IonValue {
+ match literal {
+ Literal::String(sym) => self.to_ion_symbol(*sym),
+ Literal::Num(v) => IonValue::new_f64(*v),
+ Literal::Int(v) => IonValue::new_i32(*v),
+ Literal::BigInt(v) => todo!(),
+ Literal::Bool(v) => IonValue::new_bool(*v),
+ Literal::Null => IonValue::new_null(),
+ Literal::Undefined => IonValue::new_null(), // TODO: undefined?
+ }
+ }
+
+ fn compile_assignment(&mut self, assignment: &Assign) {
+ self.compile_expression(assignment.rhs());
+ match assignment.op() {
+ boa_ast::expression::operator::assign::AssignOp::Assign => {},
+ boa_ast::expression::operator::assign::AssignOp::Add => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Sub => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Mul => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Div => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Mod => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Exp => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::And => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Or => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Xor => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Shl => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Shr => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Ushr => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::BoolAnd => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::BoolOr => todo!(),
+ boa_ast::expression::operator::assign::AssignOp::Coalesce => todo!(),
+ }
+ self.push_dup();
+ match assignment.lhs() {
+ AssignTarget::Identifier(ident) => {
+ let varid = self.get_or_create_variable(ident.sym());
+ self.push_store(varid);
+ },
+ AssignTarget::Access(_) => todo!(),
+ AssignTarget::Pattern(_) => todo!(),
+ }
+ }
+
+ fn compile_binary(&mut self, binary: &Binary) {
+ match binary.op() {
+ BinaryOp::Arithmetic(_) => todo!(),
+ BinaryOp::Bitwise(_) => todo!(),
+ BinaryOp::Relational(op) => {
+ self.compile_expression(binary.lhs());
+ self.compile_expression(binary.rhs());
+ let jump_true = match op {
+ RelationalOp::Equal => self.push_ifeq2(),
+ RelationalOp::NotEqual => self.push_ifne2(),
+ RelationalOp::StrictEqual => self.push_ifeq2(),
+ RelationalOp::StrictNotEqual => self.push_ifne2(),
+ RelationalOp::GreaterThan => self.push_ifle2(),
+ RelationalOp::GreaterThanOrEqual => self.push_iflt2(),
+ RelationalOp::LessThan => self.push_ifge2(),
+ RelationalOp::LessThanOrEqual => self.push_ifgt2(),
+ RelationalOp::In => todo!(),
+ RelationalOp::InstanceOf => todo!(),
+ };
+ self.push_push(&IonValue::new_bool(false));
+ let jump_false = self.push_jump();
+ self.set_jump(jump_true);
+ self.push_push(&IonValue::new_bool(true));
+ self.set_jump(jump_false);
+ },
+ BinaryOp::Logical(op) => {
+ match op {
+ LogicalOp::And => {
+ self.compile_expression(binary.lhs());
+ self.push_dup();
+ self.push_push(&IonValue::new_bool(false));
+ let jump_done = self.push_ifeq2();
+ self.push_pop();
+ self.compile_expression(binary.rhs());
+ self.set_jump(jump_done)
+ },
+ LogicalOp::Or => {
+ self.compile_expression(binary.lhs());
+ self.push_dup();
+ self.push_push(&IonValue::new_bool(true));
+ let jump_done = self.push_ifeq2();
+ self.push_pop();
+ self.compile_expression(binary.rhs());
+ self.set_jump(jump_done)
+ },
+ LogicalOp::Coalesce => {
+ self.compile_expression(binary.lhs());
+ self.push_dup();
+ self.push_push(&IonValue::new_null());
+ let jump_done = self.push_ifeq2();
+ self.push_pop();
+ self.compile_expression(binary.rhs());
+ self.set_jump(jump_done)
+ },
+ }
+ },
+ BinaryOp::Comma => todo!(),
+ }
+ }
+
+ fn compile_expression(&mut self, expression: &Expression) {
+ match expression {
+ Expression::This => self.push_load(0),
+ Expression::Identifier(ident) => {
+ let varid = self.get_or_create_variable(ident.sym());
+ self.push_load(varid);
+ },
+ Expression::Literal(lit) => {
+ let value = self.literal_to_ion(lit);
+ self.push_push(&value);
+ },
+ Expression::ArrayLiteral(alit) => {
+ // TODO: there's an opportunity here inject static arrays into the bytecode.
+ for exprmaybe in alit.as_ref() {
+ if let Some(expr) = exprmaybe {
+ self.compile_expression(expr);
+ } else {
+ self.push_push(&IonValue::new_null());
+ }
+ }
+ self.push_push(&alit.as_ref().len().into());
+ self.push_newlist();
+ },
+ Expression::ObjectLiteral(olit) => {
+ // TODO: there's an opportunity here inject static objects into the bytecode.
+ for propdef in olit.properties() {
+ match propdef {
+ PropertyDefinition::IdentifierReference(ident) => {
+ let varid = self.get_or_create_variable(ident.sym());
+ self.push_load(varid);
+ let symb = self.to_ion_symbol(ident.sym());
+ self.push_push(&symb);
+ },
+ PropertyDefinition::Property(name, expr) => {
+ self.compile_expression(expr);
+ match name {
+ PropertyName::Literal(sym) => {
+ let symb = self.to_ion_symbol(*sym);
+ self.push_push(&symb);
+ },
+ PropertyName::Computed(_) => todo!(),
+ }
+ },
+ PropertyDefinition::MethodDefinition(_, _) => todo!(),
+ PropertyDefinition::SpreadObject(_) => todo!(),
+ PropertyDefinition::CoverInitializedName(_, _) => todo!(),
+ }
+ }
+ self.push_push(&olit.properties().len().into());
+ self.push_newstruct();
+ },
+ Expression::Spread(_) => todo!(),
+ Expression::Function(_) => todo!(),
+ Expression::ArrowFunction(_) => todo!(),
+ Expression::AsyncArrowFunction(_) => todo!(),
+ Expression::Generator(_) => todo!(),
+ Expression::AsyncFunction(_) => todo!(),
+ Expression::AsyncGenerator(_) => todo!(),
+ Expression::Class(_) => todo!(),
+ Expression::TemplateLiteral(_) => todo!(),
+ Expression::PropertyAccess(atype) => {
+ match atype {
+ PropertyAccess::Simple(access) => {
+ self.compile_expression(access.target());
+ self.push_iterate();
+ let label_loop = self.bytecode.len();
+ self.push_dup2();
+ let jump_undef = self.push_ifge2();
+ self.push_load_field();
+ match access.field() {
+ PropertyAccessField::Const(sym) => {
+ let symbol = self.to_ion_symbol(*sym);
+ self.push_push(&symbol);
+ },
+ PropertyAccessField::Expr(expr) => {
+ self.compile_expression(expr);
+ todo!("cast to symbol")
+ },
+ }
+ let jump_found = self.push_ifeq2();
+ self.push_next();
+ let jump_loop = self.push_jump();
+ self.set_jump_target(jump_loop, label_loop);
+
+ // Found the field
+ self.set_jump(jump_found);
+ self.push_load_value();
+ let jump_cleanup = self.push_jump();
+
+ // Undefined field
+ self.set_jump(jump_undef);
+ self.push_push(&IonValue::new_null());
+
+ // Cleanup
+ // We need to pop four values under the top value:
+ // three for the iterator, one for the object.
+ self.set_jump(jump_cleanup);
+ for _ in 0..4 {
+ self.push_swappop();
+ }
+ },
+ PropertyAccess::Private(_) => todo!(),
+ PropertyAccess::Super(_) => todo!(),
+ }
+ },
+ Expression::New(_) => todo!(),
+ Expression::Call(_) => todo!(),
+ Expression::SuperCall(_) => todo!(),
+ Expression::ImportCall(_) => todo!(),
+ Expression::Optional(_) => todo!(),
+ Expression::TaggedTemplate(_) => todo!(),
+ Expression::NewTarget => todo!(),
+ Expression::ImportMeta => todo!(),
+ Expression::Assign(assignment) => self.compile_assignment(assignment),
+ Expression::Unary(_) => todo!(),
+ Expression::Update(_) => todo!(),
+ Expression::Binary(binary) => self.compile_binary(binary),
+ Expression::BinaryInPrivate(_) => todo!(),
+ Expression::Conditional(_) => todo!(),
+ Expression::Await(_) => todo!(),
+ Expression::Yield(_) => todo!(),
+ Expression::Parenthesized(_) => todo!(),
+ _ => todo!(),
+ }
+ }
+
+ fn compile_statement_list(&mut self, statements: &StatementList) {
+ for item in statements.as_ref() {
+ match item {
+ StatementListItem::Statement(statement) => self.compile_statement(statement),
+ StatementListItem::Declaration(decl) => self.compile_declaration(decl),
+ }
+ }
+ }
+
+ fn compile_statement(&mut self, statement: &Statement) {
+ match statement {
+ boa_ast::Statement::Block(block) => {
+ self.enter_block();
+ self.compile_statement_list(block.statement_list());
+ self.exit_block();
+ },
+ boa_ast::Statement::Var(_) => todo!(),
+ boa_ast::Statement::Empty => {},
+ boa_ast::Statement::Expression(expression) => {
+ self.compile_expression(expression);
+ self.push_pop();
+ },
+ boa_ast::Statement::If(flow) => {
+ self.compile_expression(flow.cond());
+ self.push_push(&IonValue::new_bool(false));
+ let jump_else = self.push_ifeq2();
+ self.compile_statement(flow.body());
+ if let Some(statement) = flow.else_node() {
+ let jump_done = self.push_jump();
+ self.set_jump(jump_else);
+ self.compile_statement(statement);
+ self.set_jump(jump_done)
+ } else {
+ self.set_jump(jump_else);
+ }
+ },
+ boa_ast::Statement::DoWhileLoop(_) => todo!(),
+ boa_ast::Statement::WhileLoop(_) => todo!(),
+ boa_ast::Statement::ForLoop(_) => todo!(),
+ boa_ast::Statement::ForInLoop(_) => todo!(),
+ boa_ast::Statement::ForOfLoop(flow) => {
+ self.compile_expression(flow.iterable());
+ self.push_iterate();
+
+ // Top of the loop
+ let label_loop = self.bytecode.len();
+ self.push_dup2();
+ let jump_done = self.push_ifge2();
+
+ self.push_load_value();
+
+ // Save the request data in the binding
+ match flow.initializer() {
+ IterableLoopInitializer::Identifier(ident) => {
+ let varid = self.get_or_create_variable(ident.sym());
+ self.push_store(varid);
+ },
+ IterableLoopInitializer::Access(_) => todo!(),
+ IterableLoopInitializer::Var(_) => todo!(),
+ IterableLoopInitializer::Let(binding) | IterableLoopInitializer::Const(binding) => {
+ self.enter_block();
+ match binding {
+ Binding::Identifier(ident) => {
+ let varid = self.get_or_create_variable(ident.sym());
+ self.push_store(varid);
+ },
+ Binding::Pattern(_) => todo!(),
+ }
+ self.exit_block();
+ },
+ IterableLoopInitializer::Pattern(_) => todo!(),
+ }
+
+ // Run the loop body
+ self.compile_statement(flow.body());
+
+ // Back to the top
+ self.push_next();
+ let jump_loop = self.push_jump();
+ self.set_jump_target(jump_loop, label_loop);
+
+ // Cleanup
+ // We need to pop four values under the top value:
+ // three for the iterator, one for the object.
+ self.set_jump(jump_done);
+ for _ in 0..4 {
+ self.push_pop();
+ }
+
+ },
+ boa_ast::Statement::Switch(_) => todo!(),
+ boa_ast::Statement::Continue(_) => todo!(),
+ boa_ast::Statement::Break(_) => todo!(),
+ boa_ast::Statement::Return(_) => todo!(),
+ boa_ast::Statement::Labelled(_) => todo!(),
+ boa_ast::Statement::Throw(_) => todo!(),
+ boa_ast::Statement::Try(_) => todo!(),
+ boa_ast::Statement::With(_) => todo!(),
+ }
+ }
+
+ pub fn dump(&self) {
+ println!("Symbols");
+ println!("-------");
+ for (id, text) in self.symbols.symbols().iter().enumerate() {
+ println!("{id:04X} {text}");
+ }
+ println!();
+
+ println!("Functions");
+ println!("---------");
+ for func in &self.functions {
+ println!("{:04X} pc: {:04X}", func.name_symbol, func.pc);
+ }
+ println!();
+
+ println!("Bytecode");
+ println!("--------");
+ println!("ADDRESS 0 1 2 3 4 5 6 7 8 9 A B C D E F TEXT");
+ for (i, bytes) in self.bytecode.chunks(16).enumerate() {
+ print!("{:08X} ", i * 16);
+ for b in bytes {
+ print!("{b:02X} ");
+ }
+ for _ in bytes.len()..16 {
+ print!(" ");
+ }
+ print!(" ");
+ for b in bytes {
+ if (0x40..0x7F).contains(b) {
+ unsafe {
+ print!("{}", char::from_u32_unchecked(*b as u32));
+ }
+ } else {
+ print!(".");
+ }
+ }
+ println!();
+ }
+ println!();
+ }
+
+ pub fn compile_script(&mut self, script: Script) {
+ for item in script.statements().statements() {
+ if let StatementListItem::Declaration(decl) = item {
+ if let Declaration::Function(_) = decl {
+ self.compile_declaration(decl);
+ } else {
+ todo!("Not supported in the global scope.");
+ }
+ } else {
+ todo!("Not supported in the global scope.");
+ }
+ }
+ }
+}
+
+impl From<JsCompiler> for Runtime {
+ fn from(compiler: JsCompiler) -> Self {
+ Runtime::new(compiler.symbols, compiler.bytecode, compiler.functions)
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index fa4225d..673c164 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,40 +1,14 @@
-use std::collections::BTreeMap;
-use std::fs::File;
use std::io;
-use std::io::prelude::*;
use std::path::Path;
-use boa_ast::Declaration;
-use boa_ast::Expression;
-use boa_ast::Statement;
-use boa_ast::StatementList;
-use boa_ast::StatementListItem;
-use boa_ast::declaration::Binding;
-use boa_ast::expression::access::PropertyAccess;
-use boa_ast::expression::access::PropertyAccessField;
-use boa_ast::expression::literal::Literal;
-use boa_ast::expression::operator::Assign;
-use boa_ast::expression::operator::Binary;
-use boa_ast::expression::operator::assign::AssignTarget;
-use boa_ast::expression::operator::binary::BinaryOp;
-use boa_ast::expression::operator::binary::LogicalOp;
-use boa_ast::expression::operator::binary::RelationalOp;
-use boa_ast::property::PropertyDefinition;
-use boa_ast::property::PropertyName;
-use boa_ast::statement::iteration::IterableLoopInitializer;
-use boa_ast::visitor::VisitWith;
-use boa_ast::visitor::Visitor;
use boa_interner::Interner;
-use boa_interner::Sym;
use boa_parser::Parser;
use boa_parser::Source;
-use ion::IonValue;
-use ion_rs::SymbolTable;
-use runtime::ByteCode;
-use runtime::Function;
-use runtime::OpCode;
-use runtime::Runtime;
+use crate::js::JsCompiler;
+use crate::runtime::Runtime;
+
+mod js;
mod ion;
mod runtime;
@@ -57,675 +31,30 @@ impl From<boa_parser::Error> for RelJsError {
}
}
-pub struct Compiler {
- interner: Interner,
- symbols: SymbolTable,
- vars: Vec<BTreeMap<String, usize>>,
- max_var: Vec<usize>,
- bytecode: Vec<u8>,
- jumps: BTreeMap<usize, usize>,
- functions: Vec<Function>,
-}
-
-impl Compiler {
-
- pub fn new(interner: Interner) -> Compiler {
- let mut compiler = Compiler {
- interner,
- symbols: SymbolTable::new(),
- vars: Vec::new(),
- max_var: vec![0],
- bytecode: Vec::new(),
- jumps: BTreeMap::new(),
- functions: Vec::new(),
- };
- compiler.enter_block();
- compiler
- }
-
- fn enter_block(&mut self) {
- let mut vars = if let Some(vars) = self.vars.last() {
- vars.clone()
- } else {
- BTreeMap::new()
- };
- vars.insert("this".to_string(), 0);
- self.vars.push(vars);
- }
-
- fn exit_block(&mut self) {
- self.vars.pop();
- }
-
- fn get_or_create_variable(&mut self, sym: Sym) -> usize {
- let name = self.interner.resolve_expect(sym).to_string();
- let idmaybe = self.vars.last().unwrap().len();
- let id = *self.vars.last_mut().unwrap().entry(name).or_insert(idmaybe);
- let max_var = *self.max_var.last().unwrap().max(&id);
- self.max_var.pop();
- self.max_var.push(max_var);
- id
- }
-
- fn set_jump_target(&mut self, jump_pc: usize, target_pc: usize) {
- let target_32: u32 = target_pc.try_into().unwrap();
- for (i, b) in target_32.to_be_bytes().iter().enumerate() {
- self.bytecode[jump_pc + 1 + i] = *b;
- }
- }
-
- /// Attach a previous jump instruction to the next instruction.
- fn set_jump(&mut self, jump_pc: usize) {
- self.set_jump_target(jump_pc, self.bytecode.len())
- }
-
- fn push_jump(&mut self) -> usize {
- self.bytecode.push(OpCode::Jump.into());
- self.push_u32(0xFFFFFFFF);
- self.bytecode.len() - 5
- }
-
- fn push_dup(&mut self) {
- self.bytecode.push(OpCode::Dup.into());
- }
-
- fn push_dup2(&mut self) {
- self.bytecode.push(OpCode::Dup2.into());
- }
-
- fn push_swappop(&mut self) {
- self.bytecode.push(OpCode::SwapPop.into());
- }
-
- fn push_push(&mut self, value: &IonValue) {
- self.bytecode.push(OpCode::TypePush.into());
- self.bytecode.extend(value.bytes());
- }
-
- fn push_pop(&mut self) {
- self.bytecode.push(OpCode::TypePop.into());
- }
-
- fn push_load(&mut self, varid: usize) {
- self.bytecode.push(OpCode::TypeLoad.into());
- self.push_varuint(varid);
- }
-
- fn push_store(&mut self, varid: usize) {
- self.bytecode.push(OpCode::TypeStore.into());
- self.push_varuint(varid);
- }
-
- fn push_iterate(&mut self) {
- self.bytecode.push(OpCode::Iterator.into());
- }
-
- fn push_next(&mut self) {
- self.bytecode.push(OpCode::Next.into());
- }
-
- fn push_step_in(&mut self, varid: usize) {
- self.bytecode.push(OpCode::StepIn.into());
- self.push_varuint(varid);
- }
-
- fn push_step_out(&mut self) {
- self.bytecode.push(OpCode::StepOut.into());
- }
-
- fn push_load_value(&mut self) {
- self.bytecode.push(OpCode::LoadValue.into());
- }
-
- fn push_load_field(&mut self) {
- self.bytecode.push(OpCode::StructField.into());
- }
-
- fn push_ifeq2(&mut self) -> usize {
- self.bytecode.push(OpCode::IfEq2.into());
- self.push_u32(0xFFFFFFFF);
- self.bytecode.len() - 5
- }
- fn push_ifne2(&mut self) -> usize {
- self.bytecode.push(OpCode::IfNe2.into());
- self.push_u32(0xFFFFFFFF);
- self.bytecode.len() - 5
- }
- fn push_ifgt2(&mut self) -> usize {
- self.bytecode.push(OpCode::IfGt2.into());
- self.push_u32(0xFFFFFFFF);
- self.bytecode.len() - 5
- }
- fn push_ifge2(&mut self) -> usize {
- self.bytecode.push(OpCode::IfGe2.into());
- self.push_u32(0xFFFFFFFF);
- self.bytecode.len() - 5
- }
- fn push_iflt2(&mut self) -> usize {
- self.bytecode.push(OpCode::IfLt2.into());
- self.push_u32(0xFFFFFFFF);
- self.bytecode.len() - 5
- }
-
- fn push_ifle2(&mut self) -> usize {
- self.bytecode.push(OpCode::IfLe2.into());
- self.push_u32(0xFFFFFFFF);
- self.bytecode.len() - 5
- }
-
-
- fn push_newlist(&mut self) {
- self.bytecode.push(OpCode::NewList.into());
- }
-
- fn push_newstruct(&mut self) {
- self.bytecode.push(OpCode::NewStruct.into());
- }
-
- fn push_varuint(&mut self, mut value: usize) {
- let mut buf = [0; (usize::BITS / 7 + 1) as usize];
- let mut pos = 0;
- while value != 0 {
- buf[pos] = (value & 0x7F) as u8;
- value >>= 7;
- pos += 1;
- }
- buf[0] |= 0x80;
- pos = pos.max(1);
-
- for i in (0..pos).rev() {
- self.bytecode.push(buf[i]);
- }
- }
-
- fn push_u32(&mut self, value: u32) {
- self.bytecode.extend(value.to_be_bytes())
- }
-
-
- fn compile_declaration(&mut self, decl: &Declaration) {
- match decl {
- Declaration::Function(func) => {
- self.max_var.push(*self.max_var.last().unwrap());
- let pc = self.bytecode.len();
- self.enter_block();
- self.compile_statement_list(func.body().statements());
- self.exit_block();
-
- let name_symbol = if let Some(name) = func.name() {
- self.to_symbol_id(name.sym())
- } else {
- self.new_symbol_id(format!("anonFunction{}", self.functions.len()))
- };
-
- self.functions.push(Function {
- name_symbol,
- arguments: func.parameters().length() as u16,
- variables: (self.max_var.pop().unwrap() + 1) as u16,
- pc,
- expected_stack_depth: 32, // TODO FIXME
- });
- },
- Declaration::Generator(_) => todo!(),
- Declaration::AsyncFunction(_) => todo!(),
- Declaration::AsyncGenerator(_) => todo!(),
- Declaration::Class(_) => todo!(),
- Declaration::Lexical(decl) => {
- for v in decl.variable_list().as_ref() {
- match v.binding() {
- Binding::Pattern(_) => todo!(),
- Binding::Identifier(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
- if let Some(init) = v.init() {
- self.compile_expression(init);
- } else {
- self.push_push(&IonValue::new_null());
- }
- self.push_store(varid);
- }
- }
- }
- },
- }
- }
-
- fn new_symbol_id(&mut self, s: String) -> usize {
- self.symbols.intern(s)
- }
-
- fn to_symbol_id(&mut self, sym: Sym) -> usize {
- let s = self.interner.resolve_expect(sym).to_string();
- self.symbols.intern(s)
- }
-
- fn to_ion_symbol(&mut self, sym: Sym) -> IonValue {
- IonValue::new_symbol(self.to_symbol_id(sym))
- }
-
- fn literal_to_ion(&mut self, literal: &Literal) -> IonValue {
- match literal {
- Literal::String(sym) => self.to_ion_symbol(*sym),
- Literal::Num(v) => IonValue::new_f64(*v),
- Literal::Int(v) => IonValue::new_i32(*v),
- Literal::BigInt(v) => todo!(),
- Literal::Bool(v) => IonValue::new_bool(*v),
- Literal::Null => IonValue::new_null(),
- Literal::Undefined => IonValue::new_null(), // TODO: undefined?
- }
- }
-
- fn compile_assignment(&mut self, assignment: &Assign) {
- self.compile_expression(assignment.rhs());
- match assignment.op() {
- boa_ast::expression::operator::assign::AssignOp::Assign => {},
- boa_ast::expression::operator::assign::AssignOp::Add => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Sub => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Mul => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Div => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Mod => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Exp => todo!(),
- boa_ast::expression::operator::assign::AssignOp::And => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Or => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Xor => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Shl => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Shr => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Ushr => todo!(),
- boa_ast::expression::operator::assign::AssignOp::BoolAnd => todo!(),
- boa_ast::expression::operator::assign::AssignOp::BoolOr => todo!(),
- boa_ast::expression::operator::assign::AssignOp::Coalesce => todo!(),
- }
- self.push_dup();
- match assignment.lhs() {
- AssignTarget::Identifier(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
- self.push_store(varid);
- },
- AssignTarget::Access(_) => todo!(),
- AssignTarget::Pattern(_) => todo!(),
- }
- }
-
- fn compile_binary(&mut self, binary: &Binary) {
- match binary.op() {
- BinaryOp::Arithmetic(_) => todo!(),
- BinaryOp::Bitwise(_) => todo!(),
- BinaryOp::Relational(op) => {
- self.compile_expression(binary.lhs());
- self.compile_expression(binary.rhs());
- let jump_true = match op {
- RelationalOp::Equal => self.push_ifeq2(),
- RelationalOp::NotEqual => self.push_ifne2(),
- RelationalOp::StrictEqual => self.push_ifeq2(),
- RelationalOp::StrictNotEqual => self.push_ifne2(),
- RelationalOp::GreaterThan => self.push_ifle2(),
- RelationalOp::GreaterThanOrEqual => self.push_iflt2(),
- RelationalOp::LessThan => self.push_ifge2(),
- RelationalOp::LessThanOrEqual => self.push_ifgt2(),
- RelationalOp::In => todo!(),
- RelationalOp::InstanceOf => todo!(),
- };
- self.push_push(&IonValue::new_bool(false));
- let jump_false = self.push_jump();
- self.set_jump(jump_true);
- self.push_push(&IonValue::new_bool(true));
- self.set_jump(jump_false);
- },
- BinaryOp::Logical(op) => {
- match op {
- LogicalOp::And => {
- self.compile_expression(binary.lhs());
- self.push_dup();
- self.push_push(&IonValue::new_bool(false));
- let jump_done = self.push_ifeq2();
- self.push_pop();
- self.compile_expression(binary.rhs());
- self.set_jump(jump_done)
- },
- LogicalOp::Or => {
- self.compile_expression(binary.lhs());
- self.push_dup();
- self.push_push(&IonValue::new_bool(true));
- let jump_done = self.push_ifeq2();
- self.push_pop();
- self.compile_expression(binary.rhs());
- self.set_jump(jump_done)
- },
- LogicalOp::Coalesce => {
- self.compile_expression(binary.lhs());
- self.push_dup();
- self.push_push(&IonValue::new_null());
- let jump_done = self.push_ifeq2();
- self.push_pop();
- self.compile_expression(binary.rhs());
- self.set_jump(jump_done)
- },
- }
- },
- BinaryOp::Comma => todo!(),
- }
- }
-
- fn compile_expression(&mut self, expression: &Expression) {
- match expression {
- Expression::This => self.push_load(0),
- Expression::Identifier(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
- self.push_load(varid);
- },
- Expression::Literal(lit) => {
- let value = self.literal_to_ion(lit);
- self.push_push(&value);
- },
- Expression::ArrayLiteral(alit) => {
- // TODO: there's an opportunity here inject static arrays into the bytecode.
- for exprmaybe in alit.as_ref() {
- if let Some(expr) = exprmaybe {
- self.compile_expression(expr);
- } else {
- self.push_push(&IonValue::new_null());
- }
- }
- self.push_push(&alit.as_ref().len().into());
- self.push_newlist();
- },
- Expression::ObjectLiteral(olit) => {
- // TODO: there's an opportunity here inject static objects into the bytecode.
- for propdef in olit.properties() {
- match propdef {
- PropertyDefinition::IdentifierReference(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
- self.push_load(varid);
- let symb = self.to_ion_symbol(ident.sym());
- self.push_push(&symb);
- },
- PropertyDefinition::Property(name, expr) => {
- self.compile_expression(expr);
- match name {
- PropertyName::Literal(sym) => {
- let symb = self.to_ion_symbol(*sym);
- self.push_push(&symb);
- },
- PropertyName::Computed(_) => todo!(),
- }
- },
- PropertyDefinition::MethodDefinition(_, _) => todo!(),
- PropertyDefinition::SpreadObject(_) => todo!(),
- PropertyDefinition::CoverInitializedName(_, _) => todo!(),
- }
- }
- self.push_push(&olit.properties().len().into());
- self.push_newstruct();
- },
- Expression::Spread(_) => todo!(),
- Expression::Function(_) => todo!(),
- Expression::ArrowFunction(_) => todo!(),
- Expression::AsyncArrowFunction(_) => todo!(),
- Expression::Generator(_) => todo!(),
- Expression::AsyncFunction(_) => todo!(),
- Expression::AsyncGenerator(_) => todo!(),
- Expression::Class(_) => todo!(),
- Expression::TemplateLiteral(_) => todo!(),
- Expression::PropertyAccess(atype) => {
- match atype {
- PropertyAccess::Simple(access) => {
- self.compile_expression(access.target());
- self.push_iterate();
- let label_loop = self.bytecode.len();
- self.push_dup2();
- let jump_undef = self.push_ifge2();
- self.push_load_field();
- match access.field() {
- PropertyAccessField::Const(sym) => {
- let symbol = self.to_ion_symbol(*sym);
- self.push_push(&symbol);
- },
- PropertyAccessField::Expr(expr) => {
- self.compile_expression(expr);
- todo!("cast to symbol")
- },
- }
- let jump_found = self.push_ifeq2();
- self.push_next();
- let jump_loop = self.push_jump();
- self.set_jump_target(jump_loop, label_loop);
-
- // Found the field
- self.set_jump(jump_found);
- self.push_load_value();
- let jump_cleanup = self.push_jump();
-
- // Undefined field
- self.set_jump(jump_undef);
- self.push_push(&IonValue::new_null());
-
- // Cleanup
- // We need to pop four values under the top value:
- // three for the iterator, one for the object.
- self.set_jump(jump_cleanup);
- for _ in 0..4 {
- self.push_swappop();
- }
- },
- PropertyAccess::Private(_) => todo!(),
- PropertyAccess::Super(_) => todo!(),
- }
- },
- Expression::New(_) => todo!(),
- Expression::Call(_) => todo!(),
- Expression::SuperCall(_) => todo!(),
- Expression::ImportCall(_) => todo!(),
- Expression::Optional(_) => todo!(),
- Expression::TaggedTemplate(_) => todo!(),
- Expression::NewTarget => todo!(),
- Expression::ImportMeta => todo!(),
- Expression::Assign(assignment) => self.compile_assignment(assignment),
- Expression::Unary(_) => todo!(),
- Expression::Update(_) => todo!(),
- Expression::Binary(binary) => self.compile_binary(binary),
- Expression::BinaryInPrivate(_) => todo!(),
- Expression::Conditional(_) => todo!(),
- Expression::Await(_) => todo!(),
- Expression::Yield(_) => todo!(),
- Expression::Parenthesized(_) => todo!(),
- _ => todo!(),
- }
- }
-
- fn compile_statement_list(&mut self, statements: &StatementList) {
- for item in statements.as_ref() {
- match item {
- StatementListItem::Statement(statement) => self.compile_statement(statement),
- StatementListItem::Declaration(decl) => self.compile_declaration(decl),
- }
- }
- }
-
- fn compile_statement(&mut self, statement: &Statement) {
- match statement {
- boa_ast::Statement::Block(block) => {
- self.enter_block();
- self.compile_statement_list(block.statement_list());
- self.exit_block();
- },
- boa_ast::Statement::Var(_) => todo!(),
- boa_ast::Statement::Empty => {},
- boa_ast::Statement::Expression(expression) => {
- self.compile_expression(expression);
- self.push_pop();
- },
- boa_ast::Statement::If(flow) => {
- self.compile_expression(flow.cond());
- self.push_push(&IonValue::new_bool(false));
- let jump_else = self.push_ifeq2();
- self.compile_statement(flow.body());
- if let Some(statement) = flow.else_node() {
- let jump_done = self.push_jump();
- self.set_jump(jump_else);
- self.compile_statement(statement);
- self.set_jump(jump_done)
- } else {
- self.set_jump(jump_else);
- }
- },
- boa_ast::Statement::DoWhileLoop(_) => todo!(),
- boa_ast::Statement::WhileLoop(_) => todo!(),
- boa_ast::Statement::ForLoop(_) => todo!(),
- boa_ast::Statement::ForInLoop(_) => todo!(),
- boa_ast::Statement::ForOfLoop(flow) => {
- self.compile_expression(flow.iterable());
- self.push_iterate();
-
- // Top of the loop
- let label_loop = self.bytecode.len();
- self.push_dup2();
- let jump_done = self.push_ifge2();
-
- self.push_load_value();
-
- // Save the request data in the binding
- match flow.initializer() {
- IterableLoopInitializer::Identifier(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
- self.push_store(varid);
- },
- IterableLoopInitializer::Access(_) => todo!(),
- IterableLoopInitializer::Var(_) => todo!(),
- IterableLoopInitializer::Let(binding) | IterableLoopInitializer::Const(binding) => {
- self.enter_block();
- match binding {
- Binding::Identifier(ident) => {
- let varid = self.get_or_create_variable(ident.sym());
- self.push_store(varid);
- },
- Binding::Pattern(_) => todo!(),
- }
- self.exit_block();
- },
- IterableLoopInitializer::Pattern(_) => todo!(),
- }
-
- // Run the loop body
- self.compile_statement(flow.body());
-
- // Back to the top
- self.push_next();
- let jump_loop = self.push_jump();
- self.set_jump_target(jump_loop, label_loop);
-
- // Cleanup
- // We need to pop four values under the top value:
- // three for the iterator, one for the object.
- self.set_jump(jump_done);
- for _ in 0..4 {
- self.push_pop();
- }
-
- },
- boa_ast::Statement::Switch(_) => todo!(),
- boa_ast::Statement::Continue(_) => todo!(),
- boa_ast::Statement::Break(_) => todo!(),
- boa_ast::Statement::Return(_) => todo!(),
- boa_ast::Statement::Labelled(_) => todo!(),
- boa_ast::Statement::Throw(_) => todo!(),
- boa_ast::Statement::Try(_) => todo!(),
- boa_ast::Statement::With(_) => todo!(),
- }
- }
-
- pub fn dump(&self) {
- println!("Symbols");
- println!("-------");
- for (id, text) in self.symbols.symbols().iter().enumerate() {
- println!("{id:04X} {text}");
- }
- println!();
-
- println!("Functions");
- println!("---------");
- for func in &self.functions {
- println!("{:04X} pc: {:04X}", func.name_symbol, func.pc);
- }
- println!();
-
- println!("Bytecode");
- println!("--------");
- println!("ADDRESS 0 1 2 3 4 5 6 7 8 9 A B C D E F TEXT");
- for (i, bytes) in self.bytecode.chunks(16).enumerate() {
- print!("{:08X} ", i * 16);
- for b in bytes {
- print!("{b:02X} ");
- }
- for _ in bytes.len()..16 {
- print!(" ");
- }
- print!(" ");
- for b in bytes {
- if (0x40..0x7F).contains(b) {
- unsafe {
- print!("{}", char::from_u32_unchecked(*b as u32));
- }
- } else {
- print!(".");
- }
- }
- println!();
- }
- println!();
- }
-}
-
-
fn main() -> Result<(), RelJsError> {
let src = Source::from_filepath(Path::new("demo/script.js"))?;
let mut parser = Parser::new(src);
let mut interner = Interner::new();
let script = parser.parse_script(&mut interner)?;
- let mut compiler = Compiler::new(interner);
-
- for item in script.statements().statements() {
- if let StatementListItem::Declaration(decl) = item {
- if let Declaration::Function(_) = decl {
- compiler.compile_declaration(decl);
- } else {
- todo!("Not supported in the global scope.");
- }
- } else {
- todo!("Not supported in the global scope.");
- }
- }
-
+ let mut compiler = JsCompiler::new(interner);
+ compiler.compile_script(script);
compiler.dump();
- let func = compiler.functions[0].name_symbol;
+ let mut runtime = Runtime::from(compiler);
- let mut runtime = Runtime::new(compiler.symbols, compiler.bytecode, compiler.functions);
+ let function_id = runtime.to_symbol_id("main")
+ .expect("script.js is missing a main function");
- let result = runtime.invoke(func, vec![]);
-
-
- /*
- let mut bin = File::open("demo/test-1.bin")?;
- let mut data = Vec::new();
- bin.read_to_end(&mut data)?;
-
- let mut runtime = Runtime::new();
- let fnref = runtime.load_bytecode(data);
+ let result = runtime.invoke(function_id, vec![]);
- let mut args = Vec::with_capacity(3);
- args.push(IonValue::new_null());
- args.push(IonValue::new_null());
- args.push(IonValue::new_null());
-
- let result = runtime.invoke(fnref, args);
- */
- for (i, v) in result.iter().enumerate() {
- println!("Result {i}:");
- for b in v.bytes() {
- print!("{b:02x} ");
- }
- println!("\n");
- }
+ for (i, v) in result.iter().enumerate() {
+ println!("Result {i}:");
+ for b in v.bytes() {
+ print!("{b:02x} ");
+ }
+ println!("\n");
+ }
Ok(())
}
diff --git a/src/runtime.rs b/src/runtime.rs
index b041561..46b2d9f 100644
--- a/src/runtime.rs
+++ b/src/runtime.rs
@@ -1,6 +1,5 @@
use std::{collections::{LinkedList, BTreeMap}, cmp::Ordering, sync::Arc, iter::{Copied, Rev}};
-use boa_interner::Sym;
use ion_rs::SymbolTable;
use crate::ion::{IonReader, IonValue};
@@ -268,7 +267,6 @@ pub struct Function {
pub(crate) expected_stack_depth: usize,
}
-pub type FnRef = usize;
pub type ByteCode = Vec<u8>;
struct Stack(Vec<u8>);
@@ -290,7 +288,17 @@ impl Runtime {
}
}
- pub fn invoke(&mut self, fnref: FnRef, arguments: Vec<IonValue>) -> Vec<IonValue> {
+ pub fn to_symbol_id<S>(&self, s: S) -> Option<usize>
+ where S: AsRef<str>
+ {
+ self.symbols.sid_for(&s)
+ }
+
+ pub fn resolve_symbol(&self, id: usize) -> Option<&str> {
+ self.symbols.text_for(id)
+ }
+
+ pub fn invoke(&mut self, fnref: usize, arguments: Vec<IonValue>) -> Vec<IonValue> {
let func = *self.functions.get(&fnref).expect("Undefined function");
let mut frame = StackFrame::new(self.bytecode.clone(), &func, arguments);
loop {
@@ -310,11 +318,15 @@ impl Runtime {
}
let op = frame.next_byte();
- for x in &frame.stack.0 {
- print!(" {x:02X}");
+
+ #[cfg(debugger)]
+ {
+ for x in &frame.stack.0 {
+ print!(" {x:02X}");
+ }
+ println!();
+ println!("PC: {:02X}, OP: {:02X}", frame.pc - 1, op);
}
- println!();
- println!("PC: {:02X}, OP: {:02X}", frame.pc - 1, op);
match op {
0x01 => { // OpType::Jump