Merge pull request #26 from Ryex/ensure_no_borrow_errors

move mutability within IC struct to limit borrow panics
This commit is contained in:
Rachel Powers
2024-04-22 13:25:17 -07:00
committed by GitHub
5 changed files with 287 additions and 248 deletions

View File

@@ -628,14 +628,14 @@ impl Device {
LogicType::LineNumber,
LogicField {
field_type: FieldType::ReadWrite,
value: ic.ip as f64,
value: ic.ip() as f64,
},
);
copy.insert(
LogicType::Error,
LogicField {
field_type: FieldType::Read,
value: match ic.state {
value: match *ic.state.borrow() {
ICState::Error(_) => 1.0,
_ => 0.0,
},
@@ -736,19 +736,12 @@ impl Device {
pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result<f64, ICError> {
if typ == LogicType::LineNumber && self.ic.is_some() {
if let Ok(ic) = vm
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.try_borrow()
{
Ok(ic.ip as f64)
} else {
// HACK: the game succeeds in getting the correct line number
// when reading it's own IC, but we'll panic trying to do it here
// this is worked around in internal_step so just return 0 here
Ok(0.0)
}
.borrow();
Ok(ic.ip() as f64)
} else if let Some(field) = self.get_fields(vm).get(&typ) {
if field.field_type == FieldType::Read || field.field_type == FieldType::ReadWrite {
Ok(field.value)
@@ -773,16 +766,12 @@ impl Device {
{
Err(ICError::ReadOnlyField(typ.to_string()))
} else if typ == LogicType::LineNumber && self.ic.is_some() {
// try borrow to set ip, we should only fail if the ic is in use aka is is *our* ic
// in game trying to set your own ic's LineNumber appears to be a Nop so this is fine.
if let Ok(mut ic) = vm
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.try_borrow_mut()
{
ic.ip = val as u32;
}
.borrow();
ic.set_ip(val as u32);
Ok(())
} else if let Some(field) = self.fields.get_mut(&typ) {
if field.field_type == FieldType::Write
@@ -818,20 +807,12 @@ impl Device {
&& self.ic.is_some()
&& typ == SlotLogicType::LineNumber
{
// try borrow to get ip, we should only fail if the ic is in us aka is is *our* ic
if let Ok(ic) = vm
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.try_borrow()
{
Ok(ic.ip as f64)
} else {
// HACK: the game succeeds in getting the correct line number
// when reading it's own IC, but we'll panic trying to do it here
// this is worked around in internal_step so just return 0 here
Ok(0.0)
}
.borrow();
Ok(ic.ip() as f64)
} else {
Ok(slot.get_field(typ))
}
@@ -848,32 +829,18 @@ impl Device {
.ok_or(ICError::SlotIndexOutOfRange(index))?;
let mut fields = slot.get_fields();
if slot.typ == SlotType::ProgrammableChip && slot.occupant.is_some() && self.ic.is_some() {
// try borrow to get ip, we should only fail if the ic is in us aka is is *our* ic
if let Ok(ic) = vm
let ic = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.try_borrow()
{
fields.insert(
SlotLogicType::LineNumber,
LogicField {
field_type: FieldType::ReadWrite,
value: ic.ip as f64,
},
);
} else {
// HACK: the game succeeds in getting the correct line number
// when reading it's own IC, but we'll panic trying to do it here
// this is worked around in internal_step so just return 0 here
fields.insert(
SlotLogicType::LineNumber,
LogicField {
field_type: FieldType::ReadWrite,
value: 0.0,
},
);
}
.borrow();
fields.insert(
SlotLogicType::LineNumber,
LogicField {
field_type: FieldType::ReadWrite,
value: ic.ip() as f64,
},
);
}
Ok(fields)
}
@@ -883,32 +850,14 @@ impl Device {
index: f64,
typ: SlotLogicType,
val: f64,
vm: &VM,
_vm: &VM,
force: bool,
) -> Result<(), ICError> {
let slot = self
.slots
.get_mut(index as usize)
.ok_or(ICError::SlotIndexOutOfRange(index))?;
if slot.typ == SlotType::ProgrammableChip
&& slot.occupant.is_some()
&& self.ic.is_some()
&& typ == SlotLogicType::LineNumber
{
// try borrow to set ip, we shoudl only fail if the ic is in us aka is is *our* ic
// in game trying to set your own ic's LineNumber appears to be a Nop so this is fine.
if let Ok(mut ic) = vm
.ics
.get(&self.ic.unwrap())
.ok_or_else(|| ICError::UnknownDeviceID(self.ic.unwrap() as f64))?
.try_borrow_mut()
{
ic.ip = val as u32;
}
Ok(())
} else {
slot.set_field(typ, val, force)
}
slot.set_field(typ, val, force)
}
pub fn get_slot(&self, index: f64) -> Result<&Slot, ICError> {

View File

@@ -431,6 +431,7 @@ impl Operand {
Device::Numbered(p) => {
let dp = ic
.pins
.borrow()
.get(p as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(p as f64))
.copied()?;
@@ -443,6 +444,7 @@ impl Operand {
let val = ic.get_register(indirection, target)?;
let dp = ic
.pins
.borrow()
.get(val as usize)
.ok_or(interpreter::ICError::DeviceIndexOutOfRange(val))
.copied()?;
@@ -522,11 +524,11 @@ impl Operand {
pub fn translate_alias(&self, ic: &interpreter::IC) -> Self {
match &self {
Operand::Identifier(id) => {
if let Some(alias) = ic.aliases.get(&id.name) {
if let Some(alias) = ic.aliases.borrow().get(&id.name) {
alias.clone()
} else if let Some(define) = ic.defines.get(&id.name) {
} else if let Some(define) = ic.defines.borrow().get(&id.name) {
Operand::Number(Number::Float(*define))
} else if let Some(label) = ic.program.labels.get(&id.name) {
} else if let Some(label) = ic.program.borrow().labels.get(&id.name) {
Operand::Number(Number::Float(*label as f64))
} else {
self.clone()

View File

@@ -1,6 +1,6 @@
use core::f64;
use serde::{Deserialize, Serialize};
use std::{ops::Deref, string::ToString};
use std::{cell::{Cell, RefCell}, ops::Deref, string::ToString};
use std::{
collections::{HashMap, HashSet},
error::Error,
@@ -180,21 +180,20 @@ impl Display for ICState {
pub struct IC {
pub device: u32,
pub id: u32,
pub registers: [f64; 18],
pub registers: RefCell<[f64; 18]>,
/// Instruction Pointer
pub ip: u32,
pub ip: Cell<u32>,
/// Instruction Count since last yield
pub ic: u16,
pub stack: [f64; 512],
pub aliases: HashMap<String, grammar::Operand>,
pub defines: HashMap<String, f64>,
pub pins: [Option<u32>; 6],
pub code: String,
pub program: Program,
pub state: ICState,
pub ic: Cell<u16>,
pub stack: RefCell<[f64; 512]>,
pub aliases: RefCell<HashMap<String, grammar::Operand>>,
pub defines: RefCell<HashMap<String, f64>>,
pub pins: RefCell<[Option<u32>; 6]>,
pub code: RefCell<String>,
pub program: RefCell<Program>,
pub state: RefCell<ICState>,
}
#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrozenIC {
@@ -215,21 +214,22 @@ pub struct FrozenIC {
}
impl<T> From<T> for FrozenIC
where T: Deref<Target = IC>
where
T: Deref<Target = IC>,
{
fn from(ic: T) -> Self {
FrozenIC {
device: ic.device,
id: ic.id,
registers: ic.registers,
ip: ic.ip,
ic: ic.ic,
stack: ic.stack,
aliases: ic.aliases.clone(),
defines: ic.defines.clone(),
pins: ic.pins,
state: ic.state.clone(),
code: ic.code.clone(),
registers: *ic.registers.borrow(),
ip: ic.ip.get(),
ic: ic.ic.get(),
stack: *ic.stack.borrow(),
aliases: ic.aliases.borrow().clone(),
defines: ic.defines.borrow().clone(),
pins: *ic.pins.borrow(),
state: ic.state.borrow().clone(),
code: ic.code.borrow().clone(),
}
}
}
@@ -239,16 +239,16 @@ impl From<FrozenIC> for IC {
IC {
device: value.device,
id: value.id,
registers: value.registers,
ip: value.ip,
ic: value.ic,
stack: value.stack,
aliases: value.aliases,
defines: value.defines,
pins: value.pins,
state: value.state,
code: value.code.clone(),
program: Program::from_code_with_invalid(&value.code),
registers: RefCell::new(value.registers),
ip: Cell::new(value.ip),
ic: Cell::new(value.ic),
stack: RefCell::new(value.stack),
aliases: RefCell::new(value.aliases),
defines: RefCell::new(value.defines),
pins: RefCell::new(value.pins),
state: RefCell::new(value.state),
code: RefCell::new(value.code.clone()),
program: RefCell::new(Program::from_code_with_invalid(&value.code)),
}
}
}
@@ -369,57 +369,49 @@ impl IC {
IC {
device,
id,
ip: 0,
ic: 0,
registers: [0.0; 18],
stack: [0.0; 512],
pins: [None; 6],
program: Program::new(),
code: String::new(),
aliases: HashMap::new(),
defines: HashMap::new(),
state: ICState::Start,
ip: Cell::new(0),
ic: Cell::new(0),
registers: RefCell::new([0.0; 18]),
stack: RefCell::new([0.0; 512]),
pins: RefCell::new([None; 6]),
program: RefCell::new(Program::new()),
code: RefCell::new(String::new()),
aliases: RefCell::new(HashMap::new()),
defines: RefCell::new(HashMap::new()),
state: RefCell::new(ICState::Start),
}
}
pub fn reset(&mut self) {
self.ip = 0;
self.ic = 0;
self.registers = [0.0; 18];
self.stack = [0.0; 512];
self.aliases = HashMap::new();
self.defines = HashMap::new();
self.state = ICState::Start;
pub fn reset(&self) {
self.ip.replace(0);
self.ic.replace(0);
self.registers.replace([0.0; 18]);
self.stack.replace([0.0; 512]);
self.aliases.replace(HashMap::new());
self.defines.replace(HashMap::new());
self.state.replace(ICState::Start);
}
/// Set program code if it's valid
pub fn set_code(&mut self, code: &str) -> Result<(), ICError> {
pub fn set_code(&self, code: &str) -> Result<(), ICError> {
let prog = Program::try_from_code(code)?;
self.ip = 0;
self.ic = 0;
self.aliases = HashMap::new();
self.defines = HashMap::new();
self.program = prog;
self.code = code.to_string();
self.program.replace(prog);
self.code.replace(code.to_string());
Ok(())
}
/// Set program code and translate invalid lines to Nop, collecting errors
pub fn set_code_invalid(&mut self, code: &str) {
let prog = Program::from_code_with_invalid(code);
self.ip = 0;
self.ic = 0;
self.aliases = HashMap::new();
self.defines = HashMap::new();
self.program = prog;
self.code = code.to_string();
self.program.replace(prog);
self.code.replace(code.to_string());
}
pub fn get_real_target(&self, indirection: u32, target: u32) -> Result<f64, ICError> {
let mut i = indirection;
let mut t = target as f64;
while i > 0 {
if let Some(new_t) = self.registers.get(t as usize) {
if let Some(new_t) = self.registers.borrow().get(t as usize) {
t = *new_t;
} else {
return Err(ICError::RegisterIndexOutOfRange(t));
@@ -432,6 +424,7 @@ impl IC {
pub fn get_register(&self, indirection: u32, target: u32) -> Result<f64, ICError> {
let t = self.get_real_target(indirection, target)?;
self.registers
.borrow()
.get(t as usize)
.ok_or(ICError::RegisterIndexOutOfRange(t))
.copied()
@@ -439,72 +432,76 @@ impl IC {
/// sets a register thorough, recursing through provided indirection, returns value previously
pub fn set_register(
&mut self,
&self,
indirection: u32,
target: u32,
val: f64,
) -> Result<f64, ICError> {
let t = self.get_real_target(indirection, target)?;
let old_val = self
.registers
let mut registers = self.registers.borrow_mut();
let old_val = registers
.get(t as usize)
.ok_or(ICError::RegisterIndexOutOfRange(t))
.copied()?;
self.registers[t as usize] = val;
registers[t as usize] = val;
Ok(old_val)
}
/// save ip to 'ra' or register 18
fn al(&mut self) {
self.registers[17] = self.ip as f64 + 1.0;
fn al(&self) {
self.registers.borrow_mut()[17] = self.ip() as f64 + 1.0;
}
pub fn push(&mut self, val: f64) -> Result<f64, ICError> {
let sp = (self.registers[16].round()) as i32;
pub fn push(&self, val: f64) -> Result<f64, ICError> {
let mut registers = self.registers.borrow_mut();
let mut stack = self.stack.borrow_mut();
let sp = (registers[16].round()) as i32;
if sp < 0 {
Err(ICError::StackUnderflow)
} else if sp >= 512 {
Err(ICError::StackOverflow)
} else {
let last = self.stack[sp as usize];
self.stack[sp as usize] = val;
self.registers[16] += 1.0;
let last = stack[sp as usize];
stack[sp as usize] = val;
registers[16] += 1.0;
Ok(last)
}
}
pub fn pop(&mut self) -> Result<f64, ICError> {
self.registers[16] -= 1.0;
let sp = (self.registers[16].round()) as i32;
pub fn pop(&self) -> Result<f64, ICError> {
let mut registers = self.registers.borrow_mut();
registers[16] -= 1.0;
let sp = (registers[16].round()) as i32;
if sp < 0 {
Err(ICError::StackUnderflow)
} else if sp >= 512 {
Err(ICError::StackOverflow)
} else {
let last = self.stack[sp as usize];
let last = self.stack.borrow()[sp as usize];
Ok(last)
}
}
pub fn poke(&mut self, address: f64, val: f64) -> Result<f64, ICError> {
pub fn poke(&self, address: f64, val: f64) -> Result<f64, ICError> {
let sp = address.round() as i32;
if !(0..512).contains(&sp) {
Err(ICError::StackIndexOutOfRange(address))
} else {
let last = self.stack[sp as usize];
self.stack[sp as usize] = val;
let mut stack = self.stack.borrow_mut();
let last = stack[sp as usize];
stack[sp as usize] = val;
Ok(last)
}
}
pub fn peek(&self) -> Result<f64, ICError> {
let sp = (self.registers[16] - 1.0).round() as i32;
let sp = (self.registers.borrow()[16] - 1.0).round() as i32;
if sp < 0 {
Err(ICError::StackUnderflow)
} else if sp >= 512 {
Err(ICError::StackOverflow)
} else {
let last = self.stack[sp as usize];
let last = self.stack.borrow()[sp as usize];
Ok(last)
}
}
@@ -516,37 +513,49 @@ impl IC {
} else if sp >= 512 {
Err(ICError::StackOverflow)
} else {
let last = self.stack[sp as usize];
let last = self.stack.borrow()[sp as usize];
Ok(last)
}
}
/// processes one line of the contained program
pub fn step(&mut self, vm: &VM, advance_ip_on_err: bool) -> Result<bool, LineError> {
pub fn step(&self, vm: &VM, advance_ip_on_err: bool) -> Result<bool, LineError> {
// TODO: handle sleep
self.state = ICState::Running;
let line = self.ip;
self.state.replace(ICState::Running);
let line = self.ip();
let result = self.internal_step(vm, advance_ip_on_err);
if let Err(error) = result {
let error = LineError { error, line };
self.state = ICState::Error(error.clone());
self.state.replace(ICState::Error(error.clone()));
Err(error)
} else {
Ok(true)
}
}
fn internal_step(&mut self, vm: &VM, advance_ip_on_err: bool) -> Result<(), ICError> {
pub fn ip(&self) -> u32 {
self.ip.get()
}
pub fn set_ip(&self, val: u32) {
self.ip.replace(val);
}
fn internal_step(&self, vm: &VM, advance_ip_on_err: bool) -> Result<(), ICError> {
use grammar::*;
use ICError::*;
let mut next_ip = self.ip + 1;
let mut next_ip = self.ip() + 1;
// XXX: This closure should be replaced with a try block
// https://github.com/rust-lang/rust/issues/31436
let mut process_op = |this: &mut Self| -> Result<(), ICError> {
let mut process_op = |this: &Self| -> Result<(), ICError> {
use grammar::InstructionOp::*;
let line = this.program.get_line(this.ip)?;
// force the program borrow to drop
let line = {
let prog = this.program.borrow();
prog.get_line(this.ip())?.clone()
};
let operands = &line.operands;
let inst = line.instruction;
match inst {
@@ -557,14 +566,14 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let now = time::OffsetDateTime::now_local()
.unwrap_or_else(|_| time::OffsetDateTime::now_utc());
this.state = ICState::Sleep(now, a);
this.state.replace(ICState::Sleep(now, a));
Ok(())
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 1)),
}, // TODO
Yield => match &operands[..] {
[] => {
this.state = ICState::Yield;
this.state.replace(ICState::Yield);
Ok(())
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 0)),
@@ -585,10 +594,11 @@ impl IC {
desired: "Number".to_owned(),
});
};
if this.defines.contains_key(&ident.name) {
let mut defines = this.defines.borrow_mut();
if defines.contains_key(&ident.name) {
Err(DuplicateDefine(ident.name.clone()))
} else {
this.defines.insert(ident.name.clone(), num.value());
defines.insert(ident.name.clone(), num.value());
Ok(())
}
}
@@ -625,7 +635,7 @@ impl IC {
})
}
};
this.aliases.insert(ident.name.clone(), alias);
this.aliases.borrow_mut().insert(ident.name.clone(), alias);
Ok(())
}
oprs => Err(ICError::mismatch_operands(oprs.len(), 2)),
@@ -671,7 +681,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a == b {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -703,7 +713,7 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let b = b.as_value(this, inst, 2)?;
next_ip = if a == 0.0 {
(this.ip as f64 + b) as u32
(this.ip() as f64 + b) as u32
} else {
next_ip
};
@@ -738,7 +748,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a != b {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -770,7 +780,7 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let b = b.as_value(this, inst, 2)?;
next_ip = if a != 0.0 {
(this.ip as f64 + b) as u32
(this.ip() as f64 + b) as u32
} else {
next_ip
};
@@ -805,7 +815,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a < b {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -840,7 +850,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a <= b {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -872,7 +882,7 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let b = b.as_value(this, inst, 2)?;
next_ip = if a <= 0.0 {
(this.ip as f64 + b) as u32
(this.ip() as f64 + b) as u32
} else {
next_ip
};
@@ -904,7 +914,7 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let b = b.as_value(this, inst, 2)?;
next_ip = if a < 0.0 {
(this.ip as f64 + b) as u32
(this.ip() as f64 + b) as u32
} else {
next_ip
};
@@ -939,7 +949,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a > b {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -971,7 +981,7 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let b = b.as_value(this, inst, 2)?;
next_ip = if a > 0.0 {
(this.ip as f64 + b) as u32
(this.ip() as f64 + b) as u32
} else {
next_ip
};
@@ -1006,7 +1016,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a >= b {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -1038,7 +1048,7 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let b = b.as_value(this, inst, 2)?;
next_ip = if a >= 0.0 {
(this.ip as f64 + b) as u32
(this.ip() as f64 + b) as u32
} else {
next_ip
};
@@ -1090,7 +1100,7 @@ impl IC {
next_ip = if f64::abs(a - b)
<= f64::max(c * f64::max(a.abs(), b.abs()), f64::EPSILON * 8.0)
{
(this.ip as f64 + d) as u32
(this.ip() as f64 + d) as u32
} else {
next_ip
};
@@ -1134,7 +1144,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a.abs() <= f64::max(b * a.abs(), f64::EPSILON * 8.0) {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -1186,7 +1196,7 @@ impl IC {
next_ip = if f64::abs(a - b)
> f64::max(c * f64::max(a.abs(), b.abs()), f64::EPSILON * 8.0)
{
(this.ip as f64 + d) as u32
(this.ip() as f64 + d) as u32
} else {
next_ip
};
@@ -1229,7 +1239,7 @@ impl IC {
let b = b.as_value(this, inst, 2)?;
let c = c.as_value(this, inst, 3)?;
next_ip = if a.abs() > f64::max(b * a.abs(), f64::EPSILON * 8.0) {
(this.ip as f64 + c) as u32
(this.ip() as f64 + c) as u32
} else {
next_ip
};
@@ -1261,7 +1271,7 @@ impl IC {
let (device, _connection) = d.as_device(this, inst, 1)?;
let a = a.as_value(this, inst, 2)?;
next_ip = if device.is_some() {
(this.ip as f64 + a) as u32
(this.ip() as f64 + a) as u32
} else {
next_ip
};
@@ -1293,7 +1303,7 @@ impl IC {
let (device, _connection) = d.as_device(this, inst, 1)?;
let a = a.as_value(this, inst, 2)?;
next_ip = if device.is_none() {
(this.ip as f64 + a) as u32
(this.ip() as f64 + a) as u32
} else {
next_ip
};
@@ -1315,7 +1325,7 @@ impl IC {
let a = a.as_value(this, inst, 1)?;
let b = b.as_value(this, inst, 2)?;
next_ip = if a.is_nan() {
(this.ip as f64 + b) as u32
(this.ip() as f64 + b) as u32
} else {
next_ip
};
@@ -1344,7 +1354,7 @@ impl IC {
Jr => match &operands[..] {
[a] => {
let a = a.as_value(this, inst, 1)?;
next_ip = (this.ip as f64 + a) as u32;
next_ip = (this.ip() as f64 + a) as u32;
Ok(())
}
oprs => Err(ICError::too_many_operands(oprs.len(), 1)),
@@ -2178,7 +2188,7 @@ impl IC {
if ic_id == &this.id {
this.poke(addr, val)?;
} else {
let mut ic = vm.ics.get(ic_id).unwrap().borrow_mut();
let ic = vm.ics.get(ic_id).unwrap().borrow();
ic.poke(addr, val)?;
}
vm.set_modified(device_id);
@@ -2206,7 +2216,7 @@ impl IC {
if ic_id == &this.id {
this.poke(addr, val)?;
} else {
let mut ic = vm.ics.get(ic_id).unwrap().borrow_mut();
let ic = vm.ics.get(ic_id).unwrap().borrow();
ic.poke(addr, val)?;
}
vm.set_modified(device_id as u32);
@@ -2360,7 +2370,7 @@ impl IC {
if lt == LogicType::LineNumber && this.device == device_id {
// HACK: we can't use device.get_field as that will try to reborrow our
// ic which will panic
this.set_register(indirection, target, this.ip as f64)?;
this.set_register(indirection, target, this.ip() as f64)?;
Ok(())
} else {
let device = vm.get_device_same_network(this.device, device_id);
@@ -2390,7 +2400,7 @@ impl IC {
if lt == LogicType::LineNumber && this.device == device_id as u32 {
// HACK: we can't use device.get_field as that will try to reborrow our
// ic which will panic
this.set_register(indirection, target, this.ip as f64)?;
this.set_register(indirection, target, this.ip() as f64)?;
Ok(())
} else {
let device = vm.get_device_same_network(this.device, device_id as u32);
@@ -2419,7 +2429,7 @@ impl IC {
if slt == SlotLogicType::LineNumber && this.device == device_id {
// HACK: we can't use device.get_slot_field as that will try to reborrow our
// ic which will panic
this.set_register(indirection, target, this.ip as f64)?;
this.set_register(indirection, target, this.ip() as f64)?;
Ok(())
} else {
let device = vm.get_device_same_network(this.device, device_id);
@@ -2536,8 +2546,8 @@ impl IC {
};
let result = process_op(self);
if result.is_ok() || advance_ip_on_err {
self.ic += 1;
self.ip = next_ip;
self.ic.set(self.ic.get() + 1);
self.set_ip(next_ip);
}
result
}
@@ -2590,3 +2600,83 @@ pub fn i64_to_f64(i: i64) -> f64 {
}
i as f64
}
#[cfg(test)]
mod tests {
use crate::vm::VMError;
use super::*;
#[test]
fn batch_modes() -> Result<(), VMError> {
let mut vm = VM::new();
let ic = vm.add_ic(None).unwrap();
let ic_id = {
let device = vm.devices.get(&ic).unwrap();
let device_ref = device.borrow();
device_ref.ic.unwrap()
};
let ic_chip = vm.ics.get(&ic_id).unwrap().borrow();
vm.set_code(
ic,
r#"lb r0 HASH("ItemActiveVent") On Sum
#lb r1 HASH("ItemActiveVent") On Maximum
lb r2 HASH("ItemActiveVent") On Minimum"#,
)?;
vm.step_ic(ic, false)?;
let r0 = ic_chip.get_register(0, 0).unwrap();
assert_eq!(r0, 0.0);
vm.step_ic(ic, false)?;
// let r1 = ic_chip.get_register(0, 1).unwrap();
// assert_eq!(r1, f64::NEG_INFINITY);
vm.step_ic(ic, false)?;
let r2 = ic_chip.get_register(0, 2).unwrap();
assert_eq!(r2, f64::INFINITY);
Ok(())
}
#[test]
fn stack() -> Result<(), VMError> {
let mut vm = VM::new();
let ic = vm.add_ic(None).unwrap();
let ic_id = {
let device = vm.devices.get(&ic).unwrap();
let device_ref = device.borrow();
device_ref.ic.unwrap()
};
let ic_chip = vm.ics.get(&ic_id).unwrap().borrow();
vm.set_code(
ic,
r#"push 100
push 10
pop r0
push 1000
peek r1
poke 1 20
pop r2
"#,
)?;
vm.step_ic(ic, false)?;
let stack0 = ic_chip.peek_addr(0.0)?;
assert_eq!(stack0, 100.0);
vm.step_ic(ic, false)?;
let stack1 = ic_chip.peek_addr(1.0)?;
assert_eq!(stack1, 10.0);
vm.step_ic(ic, false)?;
let r0 = ic_chip.get_register(0, 0).unwrap();
assert_eq!(r0, 10.0);
vm.step_ic(ic, false)?;
let stack1 = ic_chip.peek_addr(1.0)?;
assert_eq!(stack1, 1000.0);
vm.step_ic(ic, false)?;
let r1 = ic_chip.get_register(0, 1).unwrap();
assert_eq!(r1, 1000.0);
vm.step_ic(ic, false)?;
let stack1 = ic_chip.peek_addr(1.0)?;
assert_eq!(stack1, 20.0);
vm.step_ic(ic, false)?;
let r2 = ic_chip.get_register(0, 2).unwrap();
assert_eq!(r2, 20.0);
Ok(())
}
}

View File

@@ -285,13 +285,11 @@ impl VM {
device.borrow_mut().id = new_id;
self.devices.insert(new_id, device);
self.ics.iter().for_each(|(_id, ic)| {
if let Ok(mut ic_ref) = ic.try_borrow_mut() {
ic_ref.pins.iter_mut().for_each(|pin| {
if pin.is_some_and(|d| d == old_id) {
pin.replace(new_id);
}
})
}
ic.borrow().pins.borrow_mut().iter_mut().for_each(|pin| {
if pin.is_some_and(|d| d == old_id) {
pin.replace(new_id);
}
})
});
self.networks.iter().for_each(|(_net_id, net)| {
if let Ok(mut net_ref) = net.try_borrow_mut() {
@@ -312,15 +310,14 @@ impl VM {
.ok_or(VMError::UnknownId(id))?
.borrow();
let ic_id = *device.ic.as_ref().ok_or(VMError::NoIC(id))?;
let mut ic = self
let ic = self
.ics
.get(&ic_id)
.ok_or(VMError::UnknownIcId(ic_id))?
.borrow_mut();
.borrow();
let new_prog = interpreter::Program::try_from_code(code)?;
ic.program = new_prog;
ic.ip = 0;
ic.code = code.to_string();
ic.program.replace(new_prog);
ic.code.replace(code.to_string());
Ok(true)
}
@@ -332,15 +329,14 @@ impl VM {
.ok_or(VMError::UnknownId(id))?
.borrow();
let ic_id = *device.ic.as_ref().ok_or(VMError::NoIC(id))?;
let mut ic = self
let ic = self
.ics
.get(&ic_id)
.ok_or(VMError::UnknownIcId(ic_id))?
.borrow_mut();
let new_prog = interpreter::Program::from_code_with_invalid(code);
ic.program = new_prog;
ic.ip = 0;
ic.code = code.to_string();
ic.program.replace(new_prog);
ic.code.replace(code.to_string());
Ok(true)
}
@@ -363,8 +359,8 @@ impl VM {
.get(&ic_id)
.ok_or(VMError::UnknownIcId(ic_id))?
.clone();
ic.borrow_mut().ic = 0;
let result = ic.borrow_mut().step(self, advance_ip_on_err)?;
ic.borrow().ic.replace(0);
let result = ic.borrow().step(self, advance_ip_on_err)?;
Ok(result)
}
@@ -378,7 +374,7 @@ impl VM {
.get(&ic_id)
.ok_or(VMError::UnknownIcId(ic_id))?
.clone();
ic.borrow_mut().ic = 0;
ic.borrow().ic.replace(0);
self.set_modified(id);
for _i in 0..128 {
if let Err(err) = ic.borrow_mut().step(self, ignore_errors) {
@@ -386,13 +382,15 @@ impl VM {
return Err(err.into());
}
}
if let interpreter::ICState::Yield = ic.borrow().state {
if let interpreter::ICState::Yield = *ic.borrow().state.borrow() {
return Ok(false);
} else if let interpreter::ICState::Sleep(_then, _sleep_for) = ic.borrow().state {
} else if let interpreter::ICState::Sleep(_then, _sleep_for) =
*ic.borrow().state.borrow()
{
return Ok(false);
}
}
ic.borrow_mut().state = interpreter::ICState::Yield;
ic.borrow().state.replace(interpreter::ICState::Yield);
Ok(true)
}
@@ -408,8 +406,8 @@ impl VM {
.get(&ic_id)
.ok_or(VMError::UnknownIcId(ic_id))?
.clone();
ic.borrow_mut().ic = 0;
ic.borrow_mut().reset();
ic.borrow().ic.replace(0);
ic.borrow().reset();
Ok(true)
}
@@ -506,7 +504,7 @@ impl VM {
let Some(ic_id) = device.borrow().ic else {
return Err(VMError::NoIC(id));
};
self.ics.get(&ic_id).unwrap().borrow_mut().pins[pin] = val;
self.ics.get(&ic_id).unwrap().borrow().pins.borrow_mut()[pin] = val;
Ok(true)
}
}

View File

@@ -116,7 +116,7 @@ impl DeviceRef {
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().ip)
.map(|ic| ic.as_ref().borrow().ip())
})
}
@@ -127,7 +127,7 @@ impl DeviceRef {
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().ic)
.map(|ic| ic.as_ref().borrow().ic.get())
})
}
@@ -138,7 +138,7 @@ impl DeviceRef {
.borrow()
.ics
.get(ic)
.map(|ic| Stack(ic.as_ref().borrow().stack))
.map(|ic| Stack(*ic.as_ref().borrow().stack.borrow()))
})
}
@@ -149,44 +149,44 @@ impl DeviceRef {
.borrow()
.ics
.get(ic)
.map(|ic| Registers(ic.as_ref().borrow().registers))
.map(|ic| Registers(*ic.as_ref().borrow().registers.borrow()))
})
}
#[wasm_bindgen(getter, js_name = "aliases", skip_typescript)]
pub fn ic_aliases(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| {
let aliases = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().aliases.clone())
}))
.unwrap()
.map(|ic| ic.as_ref().borrow().aliases.borrow().clone())
});
serde_wasm_bindgen::to_value(aliases).unwrap()
}
#[wasm_bindgen(getter, js_name = "defines", skip_typescript)]
pub fn ic_defines(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| {
let defines = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().defines.clone())
}))
.unwrap()
.map(|ic| ic.as_ref().borrow().defines.borrow().clone())
});
serde_wasm_bindgen::to_value(defines).unwrap()
}
#[wasm_bindgen(getter, js_name = "pins", skip_typescript)]
pub fn ic_pins(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| {
let pins = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.as_ref().borrow().pins)
}))
.unwrap()
.map(|ic| *ic.as_ref().borrow().pins.borrow())
});
serde_wasm_bindgen::to_value(pins).unwrap()
}
#[wasm_bindgen(getter, js_name = "state")]
@@ -202,19 +202,19 @@ impl DeviceRef {
.get(ic)
.map(|ic| ic.borrow().state.clone())
})
.map(|state| state.to_string())
.map(|state| state.borrow().to_string())
}
#[wasm_bindgen(getter, js_name = "program", skip_typescript)]
pub fn ic_program(&self) -> JsValue {
serde_wasm_bindgen::to_value(&self.device.borrow().ic.as_ref().and_then(|ic| {
let prog = &self.device.borrow().ic.as_ref().and_then(|ic| {
self.vm
.borrow()
.ics
.get(ic)
.map(|ic| ic.borrow().program.clone())
}))
.unwrap()
.map(|ic| ic.borrow().program.borrow().clone())
});
serde_wasm_bindgen::to_value(prog).unwrap()
}
#[wasm_bindgen(getter, js_name = "code")]
@@ -224,7 +224,7 @@ impl DeviceRef {
.borrow()
.ics
.get(ic)
.map(|ic| ic.borrow().code.clone())
.map(|ic| ic.borrow().code.borrow().clone())
})
}