revert: reject marginal numeric VM slot reuse
This commit is contained in:
@@ -168,40 +168,36 @@ impl Program {
|
||||
Instruction::Push(value) => scratch.stack.push(value),
|
||||
Instruction::LoadVariable(index) => {
|
||||
let index = usize::from(index);
|
||||
let value = if scratch.variable_generations[index] == scratch.generation {
|
||||
scratch.variables[index]
|
||||
} else {
|
||||
let expected_type = self.variable_types[index];
|
||||
let value = resolve(index, &self.variables[index], expected_type)?;
|
||||
if value.value_type() != expected_type {
|
||||
return Err(EvalError::new(format!(
|
||||
"variable {} expected {:?}, got {:?}",
|
||||
self.variables[index],
|
||||
expected_type,
|
||||
value.value_type()
|
||||
)));
|
||||
let cached = scratch.variables[index];
|
||||
let value = match cached {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
let expected_type = self.variable_types[index];
|
||||
let value = resolve(index, &self.variables[index], expected_type)?;
|
||||
if value.value_type() != expected_type {
|
||||
return Err(EvalError::new(format!(
|
||||
"variable {} expected {:?}, got {:?}",
|
||||
self.variables[index],
|
||||
expected_type,
|
||||
value.value_type()
|
||||
)));
|
||||
}
|
||||
scratch.variables[index] = Some(value);
|
||||
value
|
||||
}
|
||||
scratch.variables[index] = value;
|
||||
scratch.variable_generations[index] = scratch.generation;
|
||||
value
|
||||
};
|
||||
scratch.stack.push(value);
|
||||
}
|
||||
Instruction::LoadLocal(index) => {
|
||||
let index = usize::from(index);
|
||||
if scratch.local_generations[index] != scratch.generation {
|
||||
return Err(EvalError::new(format!(
|
||||
"local slot {index} was not initialized"
|
||||
)));
|
||||
}
|
||||
let value = scratch.locals[index];
|
||||
let value = scratch.locals[index].ok_or_else(|| {
|
||||
EvalError::new(format!("local slot {index} was not initialized"))
|
||||
})?;
|
||||
scratch.stack.push(value);
|
||||
}
|
||||
Instruction::StoreLocal(index) => {
|
||||
let index = usize::from(index);
|
||||
let value = pop(&mut scratch.stack)?;
|
||||
scratch.locals[index] = value;
|
||||
scratch.local_generations[index] = scratch.generation;
|
||||
scratch.locals[usize::from(index)] = Some(value);
|
||||
}
|
||||
Instruction::Unary(operator) => {
|
||||
let value = pop(&mut scratch.stack)?;
|
||||
@@ -264,11 +260,8 @@ impl Program {
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Scratch {
|
||||
stack: Vec<Value>,
|
||||
variables: Vec<Value>,
|
||||
variable_generations: Vec<u32>,
|
||||
locals: Vec<Value>,
|
||||
local_generations: Vec<u32>,
|
||||
generation: u32,
|
||||
variables: Vec<Option<Value>>,
|
||||
locals: Vec<Option<Value>>,
|
||||
}
|
||||
|
||||
impl Scratch {
|
||||
@@ -280,21 +273,10 @@ impl Scratch {
|
||||
.len()
|
||||
.saturating_sub(self.stack.capacity()),
|
||||
);
|
||||
self.generation = self.generation.wrapping_add(1);
|
||||
if self.generation == 0 {
|
||||
self.variable_generations.fill(0);
|
||||
self.local_generations.fill(0);
|
||||
self.generation = 1;
|
||||
}
|
||||
if self.variables.len() < program.variables.len() {
|
||||
self.variables
|
||||
.resize(program.variables.len(), Value::Number(0.0));
|
||||
self.variable_generations.resize(program.variables.len(), 0);
|
||||
}
|
||||
if self.locals.len() < program.local_count {
|
||||
self.locals.resize(program.local_count, Value::Number(0.0));
|
||||
self.local_generations.resize(program.local_count, 0);
|
||||
}
|
||||
self.variables.clear();
|
||||
self.variables.resize(program.variables.len(), None);
|
||||
self.locals.clear();
|
||||
self.locals.resize(program.local_count, None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1380,27 +1362,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scratch_generation_rollover_invalidates_cached_slots() {
|
||||
let program = compile("left + right", |_| Some(ValueType::Number)).expect("compile");
|
||||
let mut scratch = Scratch::default();
|
||||
let first = program
|
||||
.evaluate(&mut scratch, |_index, name, _expected| {
|
||||
Ok(Value::Number(if name == "left" { 1.0 } else { 2.0 }))
|
||||
})
|
||||
.expect("first evaluation");
|
||||
assert_eq!(first, Value::Number(3.0));
|
||||
|
||||
scratch.generation = u32::MAX;
|
||||
let second = program
|
||||
.evaluate(&mut scratch, |_index, name, _expected| {
|
||||
Ok(Value::Number(if name == "left" { 10.0 } else { 20.0 }))
|
||||
})
|
||||
.expect("evaluation after generation rollover");
|
||||
assert_eq!(second, Value::Number(30.0));
|
||||
assert_eq!(scratch.generation, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matches_rhai_for_representative_numeric_boolean_corpus() {
|
||||
let source = r#"
|
||||
|
||||
Reference in New Issue
Block a user