mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 22:22:25 +00:00
JavaScript: XML namespace declarations and QName strings count against the heap limit
addNamespace and setNamespace now add a declaration's bytes to the heap total when the element gains a new prefix, and QName.prototype.toString adds the bytes of the string it returns. Before, both only checked for room, so a loop of distinct prefixes could grow an element past the heap limit until a collection recounted it.
This commit is contained in:
@@ -299,7 +299,9 @@ fn qname_to_string(interp: &mut Interp, this: Value, _: &[Value], _: bool) -> Js
|
||||
_ => {}
|
||||
}
|
||||
units.extend_from_slice(&name.local);
|
||||
interp.reserve_heap(string_bytes(units.len()))?;
|
||||
let bytes = string_bytes(units.len());
|
||||
interp.reserve_heap(bytes)?;
|
||||
interp.heap.bytes = interp.heap.bytes.saturating_add(bytes);
|
||||
Ok(Value::String(Str::from_units(units)))
|
||||
}
|
||||
|
||||
@@ -816,7 +818,11 @@ fn add_in_scope(interp: &mut Interp, x: ObjId, ns: XmlNs) -> JsResult<()> {
|
||||
if prefix.is_empty() && node.name.as_ref().and_then(|n| n.uri.as_ref()).is_some_and(|u| u.is_empty()) {
|
||||
return Ok(());
|
||||
}
|
||||
interp.reserve_heap(crate::heap::XML_NS_BYTES)?;
|
||||
if !node.ns.iter().any(|m| m.prefix.as_ref() == Some(&prefix)) {
|
||||
let bytes = crate::heap::XML_NS_BYTES;
|
||||
interp.reserve_heap(bytes)?;
|
||||
interp.heap.bytes = interp.heap.bytes.saturating_add(bytes);
|
||||
}
|
||||
let attrs = interp.xml_node(x)?.attrs.clone();
|
||||
let node = interp.xml_node_mut(x)?;
|
||||
node.ns.retain(|m| m.prefix.as_ref() != Some(&prefix));
|
||||
|
||||
@@ -1018,6 +1018,32 @@ fn comparing_xml_values_is_charged_to_the_heap() {
|
||||
assert!(heap_stops_only(setup, "(SPARE, x == y)", "x == y && y.@q == '2'") > 0);
|
||||
}
|
||||
|
||||
/// Runs `setup` with no limit, then `action` in the same realm, and returns how `action` ended.
|
||||
fn after_setup(setup: &str, action: &str) -> Result<JsValue, RunErrorKind> {
|
||||
let mut r = realm();
|
||||
let interp = r.interp();
|
||||
let global = interp.global;
|
||||
assert!(crate::builtins::method(interp, global, "squeeze", squeeze, 1).is_ok());
|
||||
r.run(setup).unwrap();
|
||||
r.run(action).map_err(|e| e.kind)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn namespace_declarations_and_qname_strings_are_charged_to_the_heap() {
|
||||
let heap = Err(RunErrorKind::Limit(LimitKind::Heap));
|
||||
let distinct = "var x = <r/>; var a = []; for (var i = 0; i < 400; i++) a.push(new Namespace('p' + i, 'u' + i));";
|
||||
for method in ["addNamespace", "setNamespace"] {
|
||||
let action = format!("squeeze(4096); for (var i = 0; i < 400; i++) x.{method}(a[i]); 1");
|
||||
assert_eq!(after_setup(distinct, &action), heap, "{method}");
|
||||
}
|
||||
let same = "var x = <r/>; var n = new Namespace('p', 'u');";
|
||||
let action = "squeeze(4096); for (var i = 0; i < 400; i++) x.addNamespace(n); 1";
|
||||
assert_eq!(after_setup(same, action), Ok(JsValue::Number(1.0)));
|
||||
let long = "var s = 'x'; for (var i = 0; i < 12; i++) s += s; var q = new QName('u', s);";
|
||||
let action = "squeeze(12000); var t1 = q.toString(); var t2 = q.toString(); 1";
|
||||
assert_eq!(after_setup(long, action), heap);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_interrupt_stops_serialization() {
|
||||
let mut r = realm();
|
||||
|
||||
Reference in New Issue
Block a user