Commit Graph
2 Commits
Author SHA1 Message Date
dotCooCoo 3ca5a75d87 Scripts: namespace scopes and edits count against the heap limit
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.
2026-11-13 05:55:22 -08:00
dotCooCoo f526c5310c JS: XML and XMLList xpath evaluates XPath 1.0 expressions
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.
2026-11-11 21:01:32 -08:00