The namespace list that inScopeNamespaces(), namespace(),
namespaceDeclarations(), the XPath namespace axis, XPath prefixes and
XPath name() collect from a node and its ancestors was built in Rust
buffers the heap limit did not see, so a node with many namespaces in
scope let a script grow past its limit.
The scope now holds the bytes of its list and prefix table until it is
dropped, and namespaceDeclarations() also holds its ancestor table and
the declarations it returns. The queried node stays rooted while the
scope is built, and each declaration is read in place instead of
copying every node's namespace list.
Other copies in the same files are held against the limit too:
- addNamespace(), setNamespace(), setName() and removeNamespace() read
an element's attributes one at a time instead of copying the list,
and removeNamespace() holds its walk stack.
- child(index), comments(), text(), elements(), processingInstructions()
and normalize() on an XMLList hold the copy of its items, and
replace() holds the copy of the children it matched.
- XPath name() holds the units of a prefixed name it builds.
- The XML, XMLList, Namespace and QName constructors, isXMLName(),
attribute(), namespace(), propertyIsEnumerable(), setName(),
setLocalName(), addNamespace(), removeNamespace() and setNamespace()
reserve the bytes of the flat copy of a long (rope) string argument
before they make it, and attribute() reserves the copy of a name
without its leading @.
- QName.prototype.toString() reserves its result before it builds it.
xml.xpath(expression[, variables]) evaluates an XPath 1.0 expression
(https://www.w3.org/TR/1999/REC-xpath-19991116/) with the XML value as
the context node and returns the selected nodes as an XMLList, in
document order and without repeats. On an XMLList the expression runs
on each element in turn, and the results are joined.
- All 13 axes and the 27 core functions. id() selects nothing, since
E4X trees carry no DTD data.
- Each property of `variables` binds the variable of its name. A
prefix in a name test resolves through the context element's
in-scope namespaces; `xml` is always bound.
- An expression that breaks the grammar gives a SyntaxError. An
unknown function, a wrong argument count, an unbound prefix or
variable, a result that is not a node-set, and a result that holds
the root or a namespace node give a TypeError.
- The parser and the evaluator keep their state on their own stacks,
so expressions of any depth use no native stack; nesting past
Options::max_depth gives "nesting too deep". Traversal stacks, node
lists, strings and the values that string functions build count
against the heap limit, and every node visited and string read takes
fuel.
Docs: docs/target-app-parity.md (E4X line), crate doc in lib.rs.