mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 04:14:26 +00:00
JavaScript: new XML copies a one-item list's node, and insertChildAfter and insertChildBefore take fuel per child scanned
new XML(list) now returns a deep copy of the list's single item, as ECMA-357 section 13.4.2 specifies for an XMLList argument; before, it returned the item itself, so changes to the new value changed the list. insertChildAfter and insertChildBefore take one unit of fuel for each child they compare with the reference, as childIndex does. The test for the missing xpath method now checks the XML and XMLList prototypes; reading .xpath on a value selects child elements and returned an empty list in every case.
This commit is contained in:
@@ -330,7 +330,7 @@ fn xml_ctor(interp: &mut Interp, _: Value, args: &[Value], construct: bool) -> J
|
||||
let v = arg(args, 0);
|
||||
interp.rooted(std::slice::from_ref(&v), |i| {
|
||||
let x = to_xml(i, &v)?;
|
||||
if construct && matches!(i.classify(&v), Xv::Node(_)) {
|
||||
if construct && matches!(i.classify(&v), Xv::Node(_) | Xv::List(_)) {
|
||||
return Ok(Value::Object(i.node_copy(x)?));
|
||||
}
|
||||
Ok(Value::Object(x))
|
||||
@@ -883,8 +883,15 @@ fn insert_relative(interp: &mut Interp, this: Value, args: &[Value], after: bool
|
||||
if after { 0 } else { len }
|
||||
} else {
|
||||
let Xv::Node(r) = interp.classify(&reference) else { return Ok(Value::Undefined) };
|
||||
interp.use_fuel()?;
|
||||
let Some(i) = interp.xml_node(x)?.children.iter().position(|&c| c == r) else { return Ok(Value::Undefined) };
|
||||
let mut found = None;
|
||||
for i in 0..len {
|
||||
interp.use_fuel()?;
|
||||
if interp.xml_node(x)?.children.get(i) == Some(&r) {
|
||||
found = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let Some(i) = found else { return Ok(Value::Undefined) };
|
||||
if after { i + 1 } else { i }
|
||||
};
|
||||
interp.rooted(std::slice::from_ref(&v), |i| i.xml_insert(x, at, &c))?;
|
||||
|
||||
@@ -290,6 +290,8 @@ fn the_constructors_keep_identity_or_copy_as_specified() {
|
||||
truth("XML().nodeKind() === 'text' && XML(null) == '' && XML(undefined) == ''");
|
||||
truth("var x = <a/>; XML(x) === x && new XML(x) !== x && new XML(x) == x");
|
||||
truth("var l = <><a/></>; XML(l) === l[0]");
|
||||
truth("var l = <><a/></>; var y = new XML(l); y !== l[0] && y == l[0] && y.parent() === null");
|
||||
truth("var l = <><a/></>; var y = new XML(l); y.@p = '1'; y.appendChild(<b/>); l[0].@p.length() === 0 && l[0].*.length() === 0");
|
||||
assert_eq!(thrown_name("XML(<><a/><b/></>)"), "TypeError");
|
||||
assert_eq!(thrown_name("XML('<a><b></a>')"), "SyntaxError");
|
||||
assert_eq!(thrown_name("XML('<a/><b/>')"), "SyntaxError");
|
||||
@@ -368,7 +370,7 @@ fn the_xml_methods_behave_as_specified() {
|
||||
assert_eq!(string("(<a>1</a>).toString()"), "1");
|
||||
assert_eq!(string("(<a><b/></a>).toString()"), "<a>\n <b/>\n</a>");
|
||||
truth("var x = <a/>; x.valueOf() === x");
|
||||
truth("(<a/>).xpath === undefined || (<a/>).xpath.length() === 0");
|
||||
truth("!Object.prototype.hasOwnProperty.call(XML.prototype, 'xpath') && !Object.prototype.hasOwnProperty.call(XMLList.prototype, 'xpath')");
|
||||
truth("var x = <a><b/></a>; x.b.attributes !== undefined");
|
||||
}
|
||||
|
||||
@@ -716,6 +718,26 @@ fn catastrophic_queries_end_with_the_fuel_limit() {
|
||||
assert_eq!(kind_of(limits, &serialize), RunErrorKind::Limit(LimitKind::Fuel));
|
||||
}
|
||||
|
||||
/// The fuel that `action` takes when it runs after `setup`.
|
||||
fn fuel_of(setup: &str, action: &str) -> u64 {
|
||||
let mut r = realm();
|
||||
r.run(setup).unwrap();
|
||||
r.run(action).unwrap();
|
||||
Limits::default().fuel - r.fuel_left()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_child_before_and_after_take_fuel_for_each_child_they_scan() {
|
||||
let narrow = "var x = <a><b/></a>; var last = x.b[0];";
|
||||
let wide = "var x = <a/>; for (var i = 0; i < 2000; i++) x.appendChild(<b/>); var last = x.b[1999];";
|
||||
for action in
|
||||
["x.insertChildAfter(<z/>, <c/>)", "x.insertChildBefore(<z/>, <c/>)", "x.insertChildAfter(last, <c/>)", "x.insertChildBefore(last, <c/>)"]
|
||||
{
|
||||
let (n, w) = (fuel_of(narrow, action), fuel_of(wide, action));
|
||||
assert!(w >= n + 1999, "{action}: {n} with one child, {w} with 2000");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limits_cannot_be_caught_by_the_script() {
|
||||
assert_eq!(kind_of(TIGHT, "try { for (;;) (<a/>).toXMLString(); } catch (e) { 1 }"), RunErrorKind::Limit(LimitKind::Fuel));
|
||||
|
||||
Reference in New Issue
Block a user