mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 07:16:52 +00:00
JavaScript: join with an empty separator does not walk the holes of a sparse array
Array.prototype.join wrote the separator once per index between the
elements it found, so new Array(4294967295).join("") ran four billion
empty writes without taking fuel. Separators are now written in one step
per gap, and an empty separator writes nothing.
This commit is contained in:
@@ -227,6 +227,20 @@ impl Builder {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Appends `units` `count` times. Nothing is written for an empty `units`, however large
|
||||
/// `count` is.
|
||||
fn push_repeated(&mut self, interp: &mut Interp, units: &[u16], count: u64) -> JsResult<()> {
|
||||
if units.is_empty() || count == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let more = usize::try_from(count).unwrap_or(usize::MAX).saturating_mul(units.len());
|
||||
self.make_room(interp, more)?;
|
||||
for _ in 0..count {
|
||||
self.units.extend_from_slice(units);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_str(&mut self, interp: &mut Interp, s: &str) -> JsResult<()> {
|
||||
let units: Vec<u16> = s.encode_utf16().collect();
|
||||
self.push_units(interp, &units)
|
||||
|
||||
@@ -291,15 +291,11 @@ fn join_elements(interp: &mut Interp, o: ObjId, len: u32, sep: &[u16], item: fn(
|
||||
while let Some(k) = it.next(interp)? {
|
||||
let v = get_at(interp, o, k as f64)?;
|
||||
let Some(s) = item(interp, v)? else { continue };
|
||||
for _ in seps..k {
|
||||
out.push_units(interp, sep)?;
|
||||
}
|
||||
out.push_repeated(interp, sep, k.saturating_sub(seps))?;
|
||||
seps = k;
|
||||
out.push_units(interp, &s.flat())?;
|
||||
}
|
||||
for _ in seps..u64::from(len - 1) {
|
||||
out.push_units(interp, sep)?;
|
||||
}
|
||||
out.push_repeated(interp, sep, u64::from(len - 1).saturating_sub(seps))?;
|
||||
Ok(out.finish())
|
||||
}
|
||||
|
||||
|
||||
@@ -231,6 +231,8 @@ fn sparse_arrays_of_any_length_cost_only_their_elements() {
|
||||
let cases = [
|
||||
("var a = new Array(4294967295); a.join('')", JsValue::String(String::new())),
|
||||
("var a = []; a[4294967294] = 'z'; a[3] = 'y'; a.join('').length", JsValue::Number(2.0)),
|
||||
// An empty separator is not written once per hole, so a thousand such joins finish.
|
||||
("var b = []; b[4294967294] = 'z'; for (var i = 0; i < 1000; i++) new Array(4294967295).join('') + b.join(''); i", JsValue::Number(1000.0)),
|
||||
("var a = []; a[4294967294] = 2; a[7] = 1; a.sort(); a[0] * 10 + a[1] + (a.length === 4294967295 ? 0 : 100)", JsValue::Number(12.0)),
|
||||
("var a = []; a[4294967294] = 1; a.indexOf(1)", JsValue::Number(4_294_967_294.0)),
|
||||
("var a = []; a[4294967294] = 1; a.lastIndexOf(1, -1) + a.lastIndexOf(2)", JsValue::Number(4_294_967_293.0)),
|
||||
|
||||
Reference in New Issue
Block a user