mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 02:19:56 -06:00
JavaScript: shrinking an array takes fuel for each property
Assigning a smaller length to an array visits every property in one operation. It now takes fuel for each one before anything is removed, so a run with a small fuel limit or a set interrupt flag stops there and leaves the array unchanged.
This commit is contained in:
+11
-4
@@ -1061,12 +1061,19 @@ impl Interp {
|
||||
if f64::from(new) != n {
|
||||
return Err(self.throw(ErrorType::Range, "invalid array length"));
|
||||
}
|
||||
let o = self.object_mut(id)?;
|
||||
let Class::Array { length, length_writable } = &mut o.class else { return internal("not an array") };
|
||||
if !*length_writable {
|
||||
let o = self.object(id)?;
|
||||
let Class::Array { length: old, length_writable } = o.class else { return internal("not an array") };
|
||||
if !length_writable {
|
||||
return self.reject(throw, &key("length"), "it is read-only");
|
||||
}
|
||||
let old = *length;
|
||||
if new < old {
|
||||
// Shrinking visits every property: each takes fuel before anything is removed.
|
||||
for _ in 0..o.props.len() {
|
||||
self.use_fuel()?;
|
||||
}
|
||||
}
|
||||
let o = self.object_mut(id)?;
|
||||
let Class::Array { length, .. } = &mut o.class else { return internal("not an array") };
|
||||
*length = new;
|
||||
if new < old {
|
||||
let doomed: Vec<Key> = o.props.keys().into_iter().filter(|k| array_index(k).is_some_and(|i| i >= new)).collect();
|
||||
|
||||
@@ -374,6 +374,18 @@ fn for_in_names_take_fuel_and_heap_under_later_limits() {
|
||||
assert!(realm.interp().heap.bytes <= heap_bytes, "{} > {heap_bytes}", realm.interp().heap.bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shrinking_an_array_takes_fuel_for_each_property() {
|
||||
let mut realm = small(Limits::default());
|
||||
assert_eq!(realm.run("var a = []; for (var i = 0; i < 100000; i++) a[i] = i; a.length"), Ok(JsValue::Number(100_000.0)));
|
||||
realm.set_limits(Limits { fuel: 1000, ..Limits::default() });
|
||||
assert_eq!(limit(&mut realm, "a.length = 0;"), LimitKind::Fuel);
|
||||
realm.set_limits(Limits::default());
|
||||
// The run stopped before anything was removed.
|
||||
assert_eq!(realm.run("a.length + a[99999]"), Ok(JsValue::Number(199_999.0)));
|
||||
assert_eq!(realm.run("a.length = 1; a.length + a[0] + (99999 in a ? 1 : 0)"), Ok(JsValue::Number(1.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn declarations_check_the_heap_before_they_are_bound() {
|
||||
// Each script declares more than fits in the heap. The run ends with the heap limit before
|
||||
|
||||
Reference in New Issue
Block a user