JavaScript: apply takes fuel for each argument it reads

Function.prototype.apply reads up to 65,536 elements from an array-like
object. Each element read now takes a unit of fuel, so a long list ends
the run with the fuel limit as other loops in the interpreter do.
This commit is contained in:
dotCooCoo
2026-11-07 19:02:53 -08:00
parent aa3bcc4c77
commit c7baa679d9
2 changed files with 9 additions and 1 deletions
+2 -1
View File
@@ -1550,7 +1550,8 @@ impl Interp {
let saved = self.temp_roots.len();
let mut out = Vec::with_capacity(n);
for i in 0..n {
match self.get_from(*o, &index_key(u32::try_from(i).unwrap_or(u32::MAX)), list) {
// Each element read takes fuel, so a long array-like list stays within the run's limits.
match self.use_fuel().and_then(|()| self.get_from(*o, &index_key(u32::try_from(i).unwrap_or(u32::MAX)), list)) {
Ok(v) => {
self.temp_roots.push(v.clone());
out.push(v);
+7
View File
@@ -391,6 +391,13 @@ fn shrinking_an_array_takes_fuel_for_each_property() {
assert_eq!(realm.run("a.length = 1; a.length + a[0] + (99999 in a ? 1 : 0)"), Ok(JsValue::Number(1.0)));
}
#[test]
fn apply_takes_fuel_for_each_argument_it_reads() {
let mut realm = small(Limits { fuel: 1000, ..Limits::default() });
assert_eq!(limit(&mut realm, "(function () {}).apply(null, {length: 65536});"), LimitKind::Fuel);
assert_eq!(realm.run("(function () { return arguments.length; }).apply(null, {length: 100})"), Ok(JsValue::Number(100.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