diff --git a/valuescript_compiler/src/assembler.rs b/valuescript_compiler/src/assembler.rs index 010b130..0670977 100644 --- a/valuescript_compiler/src/assembler.rs +++ b/valuescript_compiler/src/assembler.rs @@ -1,6 +1,5 @@ use std::{ collections::{HashMap, HashSet}, - rc::Rc, str::FromStr, }; @@ -13,7 +12,7 @@ use crate::asm::{ Label, LabelRef, Lazy, Module, Object, Pointer, Register, Value, }; -pub fn assemble(module: &Module) -> Rc> { +pub fn assemble(module: &Module) -> Vec { let mut assembler = Assembler { output: Vec::new(), fn_data: Default::default(), @@ -25,8 +24,7 @@ pub fn assemble(module: &Module) -> Rc> { assembler.module(module); - // TODO: Don't use Rc - return Rc::new(assembler.output); + assembler.output } struct Assembler { diff --git a/valuescript_program_embed/src/main.rs b/valuescript_program_embed/src/main.rs index 70bb3fb..d7a5f3a 100644 --- a/valuescript_program_embed/src/main.rs +++ b/valuescript_program_embed/src/main.rs @@ -1,11 +1,11 @@ use std::{process::exit, rc::Rc}; -use valuescript_vm::VirtualMachine; +use valuescript_vm::{Bytecode, VirtualMachine}; pub fn main() { let mut vm = VirtualMachine::new(); let result = vm.run( - &Rc::new(vec![ + Rc::new(Bytecode::new(vec![ // // This is the compiled bytecode for inputs/passing/projEuler/p28.ts. // @@ -46,7 +46,7 @@ pub fn main() { 0x05, 0x02, 0x06, 0x06, 0x03, 0x0e, 0x04, 0x06, 0x05, 0x0e, 0x02, 0x0e, 0x06, 0x07, 0x04, 0x0e, 0x07, 0x0e, 0x03, 0x06, 0x01, 0x09, 0x0e, 0x04, 0x0e, 0x05, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x0b, 0x05, 0x02, 0x04, 0x0e, 0x02, 0x0e, 0x03, 0x00, 0x00, - ]), + ])), None, &[], ); diff --git a/valuescript_vm/src/bytecode.rs b/valuescript_vm/src/bytecode.rs new file mode 100644 index 0000000..d6e12d2 --- /dev/null +++ b/valuescript_vm/src/bytecode.rs @@ -0,0 +1,44 @@ +use std::{cell::RefCell, collections::HashMap, fmt, ops::Index, rc::Rc, slice::SliceIndex}; + +use crate::{bytecode_decoder::BytecodeDecoder, vs_value::Val}; + +pub struct Bytecode { + pub code: Vec, + pub cache: RefCell>, +} + +impl> Index for Bytecode { + type Output = I::Output; + + fn index(&self, index: I) -> &Self::Output { + &self.code[index] + } +} + +impl fmt::Debug for Bytecode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Bytecode {{ code: {:?} }}", self.code) + } +} + +impl Bytecode { + pub fn new(code: Vec) -> Bytecode { + Bytecode { + code, + cache: RefCell::new(HashMap::new()), + } + } +} + +pub trait DecoderMaker { + fn decoder(&self, pos: usize) -> BytecodeDecoder; +} + +impl DecoderMaker for Rc { + fn decoder(&self, pos: usize) -> BytecodeDecoder { + BytecodeDecoder { + bytecode: self.clone(), + pos, + } + } +} diff --git a/valuescript_vm/src/bytecode_decoder.rs b/valuescript_vm/src/bytecode_decoder.rs index 99dcd66..21ff02e 100644 --- a/valuescript_vm/src/bytecode_decoder.rs +++ b/valuescript_vm/src/bytecode_decoder.rs @@ -6,17 +6,17 @@ use num_bigint::Sign; use valuescript_common::InstructionByte; use crate::builtins::BUILTIN_VALS; +use crate::bytecode::Bytecode; use crate::vs_class::VsClass; use crate::vs_function::VsFunction; use crate::vs_object::VsObject; -use crate::vs_pointer::VsPointer; use crate::vs_symbol::VsSymbol; use crate::vs_value::ToVal; use crate::vs_value::Val; pub struct BytecodeDecoder { // TODO: Enable borrow usage to avoid the rc overhead - pub data: Rc>, + pub bytecode: Rc, pub pos: usize, } @@ -74,13 +74,13 @@ impl BytecodeType { impl BytecodeDecoder { pub fn decode_byte(&mut self) -> u8 { - let byte = self.data[self.pos]; + let byte = self.bytecode[self.pos]; self.pos += 1; return byte; } pub fn peek_byte(&self) -> u8 { - return self.data[self.pos]; + return self.bytecode[self.pos]; } pub fn decode_type(&mut self) -> BytecodeType { @@ -138,7 +138,7 @@ impl BytecodeDecoder { .to_val() } BytecodeType::Function => self.decode_function_header(), - BytecodeType::Pointer => self.decode_pointer(), + BytecodeType::Pointer => self.decode_pointer(registers), BytecodeType::Register => match registers[self.decode_register_index().unwrap()].clone() { Val::Void => Val::Undefined, val => val, @@ -155,7 +155,7 @@ impl BytecodeDecoder { } pub fn decode_signed_byte(&mut self) -> i8 { - let res = self.data[self.pos] as i8; + let res = self.bytecode[self.pos] as i8; self.pos += 1; return res; } @@ -163,7 +163,7 @@ impl BytecodeDecoder { pub fn decode_number(&mut self) -> f64 { let mut buf = [0u8; 8]; let next_pos = self.pos + 8; - buf.clone_from_slice(&self.data[self.pos..next_pos]); + buf.clone_from_slice(&self.bytecode[self.pos..next_pos]); self.pos = next_pos; return f64::from_le_bytes(buf); } @@ -178,7 +178,7 @@ impl BytecodeDecoder { }; let len = self.decode_varsize_uint(); - let bytes = &self.data[self.pos..self.pos + len]; + let bytes = &self.bytecode[self.pos..self.pos + len]; self.pos += len; return BigInt::from_bytes_le(sign, bytes); @@ -188,7 +188,7 @@ impl BytecodeDecoder { let len = self.decode_varsize_uint(); let start = self.pos; // Start after decoding varsize let end = self.pos + len; - let res = String::from_utf8_lossy(&self.data[start..end]).into_owned(); + let res = String::from_utf8_lossy(&self.bytecode[start..end]).into_owned(); self.pos = end; return res; @@ -229,12 +229,12 @@ impl BytecodeDecoder { pub fn clone_at(&self, pos: usize) -> BytecodeDecoder { return BytecodeDecoder { - data: self.data.clone(), - pos: pos, + bytecode: self.bytecode.clone(), + pos, }; } - pub fn decode_pointer(&mut self) -> Val { + pub fn decode_pointer(&mut self, registers: &Vec) -> Val { let from_pos = self.pos; let pos = self.decode_pos(); @@ -250,7 +250,22 @@ impl BytecodeDecoder { } } - VsPointer::new(&self.data, pos).to_val() + let cached_val = self + .bytecode + .cache + .borrow() + .get(&pos) + .map(|val| val.clone()); + + match cached_val { + Some(val) => val, + None => { + let val = self.clone_at(pos).decode_val(registers); + self.bytecode.cache.borrow_mut().insert(pos, val.clone()); + + val + } + } } pub fn decode_function_header(&mut self) -> Val { @@ -259,7 +274,7 @@ impl BytecodeDecoder { let parameter_count = self.decode_byte() as usize; return VsFunction { - bytecode: self.data.clone(), + bytecode: self.bytecode.clone(), register_count, parameter_count, start: self.pos, diff --git a/valuescript_vm/src/lib.rs b/valuescript_vm/src/lib.rs index 8f8f1a6..4ff4113 100644 --- a/valuescript_vm/src/lib.rs +++ b/valuescript_vm/src/lib.rs @@ -1,6 +1,7 @@ mod array_higher_functions; mod bigint_methods; mod builtins; +mod bytecode; mod bytecode_decoder; mod bytecode_stack_frame; mod first_stack_frame; @@ -17,9 +18,9 @@ pub mod vs_array; mod vs_class; mod vs_function; pub mod vs_object; -mod vs_pointer; mod vs_symbol; pub mod vs_value; +pub use bytecode::Bytecode; pub use virtual_machine::VirtualMachine; pub use vs_value::{LoadFunctionResult, ValTrait}; diff --git a/valuescript_vm/src/virtual_machine.rs b/valuescript_vm/src/virtual_machine.rs index 9ce3382..ffd8b69 100644 --- a/valuescript_vm/src/virtual_machine.rs +++ b/valuescript_vm/src/virtual_machine.rs @@ -1,7 +1,8 @@ use std::rc::Rc; use crate::builtins::error_builtin::ToError; -use crate::bytecode_decoder::BytecodeDecoder; +use crate::bytecode::Bytecode; +use crate::bytecode::DecoderMaker; use crate::first_stack_frame::FirstStackFrame; use crate::stack_frame::FrameStepOk; use crate::stack_frame::StackFrame; @@ -15,14 +16,11 @@ pub struct VirtualMachine { impl VirtualMachine { pub fn run( &mut self, - bytecode: &Rc>, + bytecode: Rc, step_limit: Option, params: &[Val], ) -> Result { - let mut bd = BytecodeDecoder { - data: bytecode.clone(), - pos: 0, - }; + let mut bd = bytecode.decoder(0); let main_fn = bd.decode_val(&Vec::new()); @@ -122,12 +120,7 @@ impl VirtualMachine { Err(exception) } - pub fn read_default_export(bytecode: &Rc>) -> Val { - let mut bd = BytecodeDecoder { - data: bytecode.clone(), - pos: 0, - }; - - bd.decode_val(&Vec::new()) + pub fn read_default_export(bytecode: Rc) -> Val { + bytecode.decoder(0).decode_val(&Vec::new()) } } diff --git a/valuescript_vm/src/vs_function.rs b/valuescript_vm/src/vs_function.rs index c5f7e95..74040ee 100644 --- a/valuescript_vm/src/vs_function.rs +++ b/valuescript_vm/src/vs_function.rs @@ -1,5 +1,6 @@ use std::rc::Rc; +use crate::bytecode::Bytecode; use crate::vs_value::ToVal; use super::bytecode_decoder::BytecodeDecoder; @@ -9,7 +10,7 @@ use super::vs_value::Val; #[derive(Debug)] pub struct VsFunction { - pub bytecode: Rc>, + pub bytecode: Rc, pub register_count: usize, pub parameter_count: usize, pub start: usize, @@ -49,7 +50,7 @@ impl VsFunction { return Box::new(BytecodeStackFrame { decoder: BytecodeDecoder { - data: self.bytecode.clone(), + bytecode: self.bytecode.clone(), pos: self.start, }, registers, diff --git a/valuescript_vm/src/vs_pointer.rs b/valuescript_vm/src/vs_pointer.rs deleted file mode 100644 index c9e3c35..0000000 --- a/valuescript_vm/src/vs_pointer.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::cell::RefCell; -use std::fmt; -use std::rc::Rc; - -use num_bigint::BigInt; - -use crate::builtins::error_builtin::ToError; -use crate::vs_value::ToVal; - -use super::bytecode_decoder::{BytecodeDecoder, BytecodeType}; -use super::vs_array::VsArray; -use super::vs_class::VsClass; -use super::vs_object::VsObject; -use super::vs_value::{LoadFunctionResult, Val, ValTrait, VsType}; - -pub struct VsPointer { - bytecode: Rc>, - pos: usize, - resolved: RefCell>, -} - -impl VsPointer { - pub fn new(bytecode: &Rc>, pos: usize) -> VsPointer { - VsPointer { - bytecode: bytecode.clone(), - pos, - resolved: RefCell::new(None), - } - } - - fn resolve(&self) -> Val { - let mut resolved = self.resolved.borrow_mut(); - - if resolved.is_some() { - return resolved.clone().unwrap(); - } - - let mut bd = BytecodeDecoder { - data: self.bytecode.clone(), - pos: self.pos, - }; - - let val = bd.decode_val(&Vec::new()); - - // TODO: Check that this actually inserts into the cell and not just a copy - // somehow - *resolved = Some(val.clone()); - - return val; - } -} - -impl ValTrait for VsPointer { - fn typeof_(&self) -> VsType { - let mut bd = BytecodeDecoder { - data: self.bytecode.clone(), - pos: self.pos, - }; - - return match bd.decode_type() { - BytecodeType::End => panic!("Invalid: pointer to end"), - BytecodeType::Void => panic!("Invalid: pointer to void"), - BytecodeType::Undefined => VsType::Undefined, - BytecodeType::Null => VsType::Null, - BytecodeType::False => VsType::Bool, - BytecodeType::True => VsType::Bool, - BytecodeType::SignedByte => VsType::Number, - BytecodeType::Number => VsType::Number, - BytecodeType::String => VsType::String, - BytecodeType::Array => VsType::Array, - BytecodeType::Object => VsType::Object, - BytecodeType::Function => VsType::Function, - BytecodeType::Pointer => panic!("Invalid: pointer to pointer"), - BytecodeType::Register => panic!("Invalid: pointer to register"), - BytecodeType::Builtin => panic!("Invalid: pointer to builtin"), - BytecodeType::Class => VsType::Class, - BytecodeType::BigInt => VsType::BigInt, - BytecodeType::Unrecognized => panic!("Unrecognized bytecode type at {}", self.pos - 1), - }; - } - - fn to_number(&self) -> f64 { - return self.resolve().to_number(); - } - - fn to_index(&self) -> Option { - return self.resolve().to_index(); - } - - fn is_primitive(&self) -> bool { - return match self.typeof_() { - VsType::Undefined => true, - VsType::Null => true, - VsType::Bool => true, - VsType::Number => true, - VsType::BigInt => true, - VsType::Symbol => true, - VsType::String => true, - VsType::Array => false, - VsType::Object => false, - VsType::Function => false, - VsType::Class => false, - }; - } - - fn bind(&self, params: Vec) -> Option { - return self.resolve().bind(params); - } - - fn is_truthy(&self) -> bool { - return self.resolve().is_truthy(); - } - - fn is_nullish(&self) -> bool { - return self.resolve().is_nullish(); - } - - fn as_bigint_data(&self) -> Option { - return self.resolve().as_bigint_data(); - } - - fn as_array_data(&self) -> Option> { - return self.resolve().as_array_data(); - } - - fn as_object_data(&self) -> Option> { - return self.resolve().as_object_data(); - } - - fn as_class_data(&self) -> Option> { - return self.resolve().as_class_data(); - } - - fn load_function(&self) -> LoadFunctionResult { - return self.resolve().load_function(); - } - - fn sub(&self, subscript: Val) -> Result { - self.resolve().sub(subscript) - } - - fn submov(&mut self, _subscript: Val, _value: Val) -> Result<(), Val> { - Err("TODO: Probably an exception, but might be possible".to_error()) - } - - fn next(&mut self) -> LoadFunctionResult { - self.resolve().next() - } - - fn pretty_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.resolve().pretty_fmt(f) - } - - fn codify(&self) -> String { - self.resolve().codify() - } -} - -impl fmt::Display for VsPointer { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.resolve().fmt(f) - } -} - -impl ToVal for VsPointer { - fn to_val(self) -> Val { - Val::Custom(Rc::new(self)) - } -} diff --git a/valuescript_vm/src/vs_value.rs b/valuescript_vm/src/vs_value.rs index f57d38d..8e5e069 100644 --- a/valuescript_vm/src/vs_value.rs +++ b/valuescript_vm/src/vs_value.rs @@ -244,6 +244,7 @@ impl ValTrait for Val { return match self { Function(f) => Some(f.bind(params).to_val()), + Static(val) => val.bind(params), Custom(val) => val.bind(params), _ => None, diff --git a/valuescript_wasm/src/lib.rs b/valuescript_wasm/src/lib.rs index b01fb5d..cc91224 100644 --- a/valuescript_wasm/src/lib.rs +++ b/valuescript_wasm/src/lib.rs @@ -1,4 +1,7 @@ -use std::collections::{BTreeMap, HashMap}; +use std::{ + collections::{BTreeMap, HashMap}, + rc::Rc, +}; use wasm_bindgen::prelude::*; @@ -9,7 +12,7 @@ use valuescript_compiler::{ use valuescript_vm::{ vs_object::VsObject, vs_value::{ToVal, Val}, - LoadFunctionResult, ValTrait, VirtualMachine, + Bytecode, LoadFunctionResult, ValTrait, VirtualMachine, }; // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global @@ -108,9 +111,9 @@ fn run_to_result(entry_point: &str, read_file: &js_sys::Function, args: &str) -> } }; - let bytecode = assemble(&module); + let bytecode = Rc::new(Bytecode::new(assemble(&module))); - match VirtualMachine::read_default_export(&bytecode).load_function() { + match VirtualMachine::read_default_export(bytecode.clone()).load_function() { LoadFunctionResult::NotAFunction => { return RunResult { diagnostics: HashMap::default(), @@ -132,7 +135,7 @@ fn run_to_result(entry_point: &str, read_file: &js_sys::Function, args: &str) -> } }; - let vm_result = vm.run(&bytecode, None, &val_args); + let vm_result = vm.run(bytecode, None, &val_args); RunResult { diagnostics: HashMap::default(), diff --git a/vstc/src/run_command.rs b/vstc/src/run_command.rs index f58509f..f1d40fe 100644 --- a/vstc/src/run_command.rs +++ b/vstc/src/run_command.rs @@ -1,9 +1,10 @@ +use std::fs; use std::rc::Rc; use std::{ffi::OsStr, path::Path, process::exit}; use valuescript_compiler::{assemble, compile, parse_module}; use valuescript_vm::vs_value::Val; -use valuescript_vm::VirtualMachine; +use valuescript_vm::{Bytecode, VirtualMachine}; use crate::resolve_entry_path::resolve_entry_path; @@ -35,7 +36,7 @@ pub fn run_command(args: &Vec) { let file_path = &args[argpos]; argpos += 1; - let bytecode = to_bytecode(format, file_path); + let bytecode = Rc::new(to_bytecode(format, file_path)); let mut vm = VirtualMachine::new(); @@ -44,7 +45,7 @@ pub fn run_command(args: &Vec) { .map(|a| Val::String(Rc::new(a.into()))) .collect(); - match vm.run(&bytecode, None, &val_args) { + match vm.run(bytecode, None, &val_args) { Ok(result) => { println!("{}", result.pretty()); } @@ -87,8 +88,8 @@ fn format_from_path(file_path: &String) -> RunFormat { }; } -fn to_bytecode(format: RunFormat, file_path: &String) -> Rc> { - match format { +fn to_bytecode(format: RunFormat, file_path: &String) -> Bytecode { + Bytecode::new(match format { RunFormat::TypeScript => { let resolved_entry_path = resolve_entry_path(file_path); @@ -115,10 +116,10 @@ fn to_bytecode(format: RunFormat, file_path: &String) -> Rc> { assemble(&module) } - RunFormat::Bytecode => Rc::new( - std::fs::read(file_path).unwrap_or_else(|_| panic!("Failed to read file {}", file_path)), - ), - } + RunFormat::Bytecode => { + fs::read(file_path).unwrap_or_else(|_| panic!("Failed to read file {}", file_path)) + } + }) } fn show_help() { diff --git a/vstc/src/test_inputs.rs b/vstc/src/test_inputs.rs index 72e283f..4e30b54 100644 --- a/vstc/src/test_inputs.rs +++ b/vstc/src/test_inputs.rs @@ -3,11 +3,12 @@ mod tests { use std::collections::HashSet; use std::fs; use std::path::{Path, PathBuf}; + use std::rc::Rc; use valuescript_compiler::compile; use valuescript_compiler::{assemble, parse_module}; - use valuescript_vm::ValTrait; use valuescript_vm::VirtualMachine; + use valuescript_vm::{Bytecode, ValTrait}; use crate::handle_diagnostics_cli::handle_diagnostics_cli; use crate::resolve_entry_path::resolve_entry_path; @@ -70,20 +71,20 @@ mod tests { .module .expect("Should have exited if module is None"); - let bytecode = assemble(&module); + let bytecode = Rc::new(Bytecode::new(assemble(&module))); let assembly = module.to_string(); let parsed_assembly = parse_module(&assembly); let bytecode_via_assembly = assemble(&parsed_assembly); - if bytecode != bytecode_via_assembly { + if bytecode.code != bytecode_via_assembly { println!(" Bytecode mismatch between original and parsed assembly"); failed_paths.insert(file_path.clone()); } let mut vm = VirtualMachine::new(); - let result = vm.run(&bytecode, Some(2_000_000), &[]); + let result = vm.run(bytecode, Some(2_000_000), &[]); let result_str = match result { Ok(val) => val.codify(),