diff --git a/crates/js/src/builtins/json.rs b/crates/js/src/builtins/json.rs index d32344946..364ec9969 100644 --- a/crates/js/src/builtins/json.rs +++ b/crates/js/src/builtins/json.rs @@ -474,8 +474,7 @@ fn settings(interp: &mut Interp, replacer: &Value, space: &Value) -> JsResult s.gap = interp.rooted(&[Value::String(t.clone())], |i| flat_charged(i, &t))?.iter().take(10).copied().collect(), + Value::String(t) => s.gap = flat_charged(interp, &t)?.iter().take(10).copied().collect(), _ => {} } Ok(s) diff --git a/crates/js/src/interp.rs b/crates/js/src/interp.rs index 696df4778..ff821b217 100644 --- a/crates/js/src/interp.rs +++ b/crates/js/src/interp.rs @@ -2626,8 +2626,7 @@ impl Interp { Value::String(s) => string_to_number(&flat_charged(self, s)?), Value::Object(_) => { let p = self.to_primitive(v, Hint::Number)?; - // Rooted, so what a string holds is still counted while its copy is reserved. - return self.rooted(std::slice::from_ref(&p), |i| i.to_number(&p)); + return self.to_number(&p); } }) } @@ -2662,19 +2661,19 @@ impl Interp { Value::Number(n) if *n >= 0.0 && *n < 4_294_967_295.0 && n.fract() == 0.0 => Ok(index_key(*n as u32)), _ => { let s = self.to_str(v)?; - // Rooted, so what the string holds is still counted while its copy is reserved. - self.rooted(&[Value::String(s.clone())], |i| i.flat_reserved(&s)) + self.flat_reserved(&s) } } } /// The code units of `s`. Flattening a rope makes a copy of its units, so for a rope that - /// copy's bytes are reserved and added to the heap first, without taking fuel. Everything - /// the caller still needs must be reachable from the roots. + /// copy's bytes are reserved and added to the heap first, without taking fuel. The rope stays + /// rooted while the copy is reserved; everything else the caller still needs must be + /// reachable from the roots. fn flat_reserved(&mut self, s: &Str) -> JsResult { if s.is_rope() { let bytes = string_bytes(s.len()); - self.reserve_heap(bytes)?; + self.rooted(&[Value::String(s.clone())], |i| i.reserve_heap(bytes))?; self.heap.bytes = self.heap.bytes.saturating_add(bytes); } Ok(s.flat()) @@ -2832,9 +2831,7 @@ impl Interp { | (Value::Bool(_), Value::Bool(_)) | (Value::Object(_), Value::Object(_)) => return Ok(strict_eq(&x, &y)), (Value::Number(n), Value::String(s)) | (Value::String(s), Value::Number(n)) => { - // Rooted, so what the string holds is still counted while its copy is reserved. - let units = self.rooted(&[x.clone(), y.clone()], |i| flat_charged(i, s))?; - return Ok(*n == string_to_number(&units)); + return Ok(*n == string_to_number(&flat_charged(self, s)?)); } (Value::Bool(v), _) => x = Value::Number(f64::from(u8::from(*v))), (_, Value::Bool(v)) => y = Value::Number(f64::from(u8::from(*v))), diff --git a/crates/js/src/string_builtins.rs b/crates/js/src/string_builtins.rs index b3758bdf5..e1baed6c2 100644 --- a/crates/js/src/string_builtins.rs +++ b/crates/js/src/string_builtins.rs @@ -109,10 +109,11 @@ fn push_held(interp: &mut Interp, held: &mut HeldBytes, list: &mut Vec, it } /// The code units of `s`. Flattening a rope makes a copy of its units, so for a rope that copy's -/// bytes are reserved and added to the heap, and its units charged as fuel, first. +/// bytes are reserved and added to the heap, and its units charged as fuel, first. The rope stays +/// rooted while the copy is reserved, so what it holds is still counted. pub(crate) fn flat_charged(interp: &mut Interp, s: &Str) -> JsResult> { if s.is_rope() { - reserve_units(interp, s.len())?; + interp.rooted(&[Value::String(s.clone())], |interp| reserve_units(interp, s.len()))?; } Ok(s.flat()) } diff --git a/crates/js/src/text_tests.rs b/crates/js/src/text_tests.rs index d2627cfb5..01fc613cc 100644 --- a/crates/js/src/text_tests.rs +++ b/crates/js/src/text_tests.rs @@ -357,6 +357,7 @@ fn a_converted_rope_counts_what_it_holds_while_its_copy_is_made() { "+({ valueOf: mk })", "({})[{ toString: mk }]", "({ valueOf: mk }) == 1", + "++({ get x() { return mk(); } }).x", "JSON.stringify(0, null, (function () { var o = new String(''); o.toString = mk; return o; })())", ] .into_iter()