/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "avmplus.h" using namespace MMgc; namespace avmplus { extern wchar *stripPrefix (const wchar *str, const char *pre); E4XNodeAux::E4XNodeAux(Stringp s, Namespace *ns, FunctionObject* notify) : m_name(s), m_ns(ns), m_notification(notify) { } #ifdef DEBUGGER uint64_t E4XNodeAux::bytesUsed() const { uint64_t size = GC::Size(this); if (m_name) size += m_name->bytesUsed(); if (m_ns) size += m_ns->bytesUsedDeep(); if (m_notification) size += m_notification->bytesUsed(); return size; } #endif AttributeE4XNode::AttributeE4XNode (E4XNode *parent, String *value) : E4XNode(parent), m_value(value) { #if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER) IMemorySampler *memSampler = (IMemorySampler *)gc()->GetAttachedSampler(); if (memSampler) { memSampler->recordNewObjectAllocation(this, ".mem.xml.attribute", (size_t) bytesUsedShallow()); } #endif } CDATAE4XNode::CDATAE4XNode (E4XNode *parent, String *value) : E4XNode(parent), m_value(value) { #if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER) IMemorySampler *memSampler = (IMemorySampler *)gc()->GetAttachedSampler(); if (memSampler) { memSampler->recordNewObjectAllocation(this, ".mem.xml.cdata", (size_t) bytesUsedShallow()); } #endif } CommentE4XNode::CommentE4XNode (E4XNode *parent, String *value) : E4XNode(parent), m_value(value) { #if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER) IMemorySampler *memSampler = (IMemorySampler *)gc()->GetAttachedSampler(); if (memSampler) { memSampler->recordNewObjectAllocation(this, ".mem.xml.comment", (size_t) bytesUsedShallow()); } #endif } PIE4XNode::PIE4XNode (E4XNode *parent, String *value) : E4XNode(parent), m_value(value) { #if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER) IMemorySampler *memSampler = (IMemorySampler *)gc()->GetAttachedSampler(); if (memSampler) { memSampler->recordNewObjectAllocation(this, ".mem.xml.processinginstruction", (size_t) bytesUsedShallow()); } #endif } TextE4XNode::TextE4XNode (E4XNode *parent, String *value) : E4XNode(parent), m_value(value) { #if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER) IMemorySampler *memSampler = (IMemorySampler *)gc()->GetAttachedSampler(); if (memSampler) { memSampler->recordNewObjectAllocation(this, ".mem.xml.text", (size_t) bytesUsedShallow()); } #endif } ElementE4XNode::ElementE4XNode (E4XNode *parent) : E4XNode(parent) { #if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER) IMemorySampler *memSampler = (IMemorySampler *)gc()->GetAttachedSampler(); if (memSampler) { memSampler->recordNewObjectAllocation(this, ".mem.xml.element", (size_t) bytesUsedShallow()); } #endif } // Fast append with no checks for type, etc. void ElementE4XNode::_append (E4XNode *childNode) { childNode->setParent (this); if (!m_children) { WB(gc(), this, &m_children, uintptr_t(childNode) | SINGLECHILDBIT); return; } if (m_children & SINGLECHILDBIT) { convertToE4XNodeList(); } HeapE4XNodeList* aa = ((HeapE4XNodeList*)(uintptr_t)m_children); aa->list.add(childNode); } uint32_t ElementE4XNode::numChildren() const { if (!m_children) return 0; if (m_children & SINGLECHILDBIT) return 1; else { HeapE4XNodeList* aa = ((HeapE4XNodeList*)(uintptr_t)m_children); return aa->list.length(); } } void ElementE4XNode::clearChildren() { if (m_children & ~SINGLECHILDBIT) { // !!@ delete our AtomList } WB(gc(), this, &m_children, 0); } void ElementE4XNode::convertToE4XNodeList() { if (m_children & SINGLECHILDBIT) { E4XNode *firstChild = (E4XNode *) (m_children & ~SINGLECHILDBIT); HeapE4XNodeList* aa = HeapE4XNodeList::create(gc(), 2); aa->list.add(firstChild); WB(gc(), this, &m_children, uintptr_t(aa)); } else if (!m_children) { WB(gc(), this, &m_children, uintptr_t(HeapE4XNodeList::create(gc(), 1))); } } void ElementE4XNode::insertChild (uint32_t i, E4XNode *x) { // m_children->insert (i, a) convertToE4XNodeList(); HeapE4XNodeList* aa = ((HeapE4XNodeList*)(uintptr_t)m_children); aa->list.insert(i, x); } void ElementE4XNode::removeChild (uint32_t i) { // m_children->removeAt (i) convertToE4XNodeList(); HeapE4XNodeList *aa = ((HeapE4XNodeList *)(uintptr_t)m_children); aa->list.removeAt (i); } void ElementE4XNode::setChildAt (uint32_t i, E4XNode *x) { if ((i == 0) && (m_children & SINGLECHILDBIT)) { WB(gc(), this, &m_children, uintptr_t(x) | SINGLECHILDBIT); } else { convertToE4XNodeList(); HeapE4XNodeList* aa = ((HeapE4XNodeList *)(uintptr_t)m_children); aa->list.set(i, x); } } E4XNode *ElementE4XNode::_getAt(uint32_t i) const { if (i >= _length()) return 0; if (int(this->m_children) & SINGLECHILDBIT) { return (E4XNode *)(this->m_children & ~SINGLECHILDBIT); } else { HeapE4XNodeList* aa = (HeapE4XNodeList*)(uintptr_t)this->m_children; return aa->list.get(i); } } bool E4XNode::getQName(Multiname *mn, Namespacep publicNS) const { if (!m_nameOrAux) return false; uintptr_t nameOrAux = m_nameOrAux; if (AUXBIT & nameOrAux) { E4XNodeAux *aux = (E4XNodeAux *)(nameOrAux & ~AUXBIT); // We can have a notification only aux which won't have a real name if (aux->m_name) { mn->setName (aux->m_name); mn->setNamespace (aux->m_ns); mn->setQName(); } else { return false; } } else { Stringp str = (String *)(nameOrAux); mn->setName (str); mn->setNamespace(publicNS); } if (getClass() == kAttribute) mn->setAttr(); return true; } // FIXME avoid code duplication with getQName bool E4XNode::getQNameForeign(AvmCore* core, Multiname *mn, Namespacep publicNS) const { if (!m_nameOrAux) return false; uintptr_t nameOrAux = m_nameOrAux; if (AUXBIT & nameOrAux) { E4XNodeAux *aux = (E4XNodeAux *)(nameOrAux & ~AUXBIT); // We can have a notification only aux which won't have a real name if (aux->m_name) { mn->setName (core->internForeignString(aux->m_name)); mn->setNamespace (core->internNamespace(core->cloneNamespace(aux->m_ns))); mn->setQName(); } else { return false; } } else { Stringp str = (String *)(nameOrAux); mn->setName (core->internForeignString(str)); mn->setNamespace(core->internNamespace(core->cloneNamespace(publicNS))); } if (getClass() == kAttribute) mn->setAttr(); return true; } void E4XNode::setQName (AvmCore *core, Stringp name, Namespace *ns) { // name can be null! if (name && !name->isInterned()) name = core->internString(name); // If we already have an aux, use it. (It may have notification atom set) uintptr_t nameOrAux = m_nameOrAux; if (AUXBIT & nameOrAux) { E4XNodeAux *aux = (E4XNodeAux *)(nameOrAux & ~AUXBIT); aux->m_name = name; aux->m_ns = ns; return; } if (!name && !ns) { m_nameOrAux = 0; return; } if (!ns || ns->isPublic() || (ns->getPrefix() == core->kEmptyString->atom() && ns->getURI()->isEmpty())) { //m_nameOrAux = int (name); WBRC(core->GetGC(), this, &m_nameOrAux, uintptr_t(name)); return; } E4XNodeAux *aux = E4XNodeAux::create(core->GetGC(), name, ns); //m_nameOrAux = AUXBIT | int(aux); // Clear it out in case there's already a name there. WBRC_NULL(&m_nameOrAux); WB(core->GetGC(), this, &m_nameOrAux, AUXBIT | uintptr_t(aux)); } void E4XNode::setQName (AvmCore *core, const Multiname *mn) { if (!mn) { m_nameOrAux = 0; } else { setQName (core, mn->getName(), mn->getNamespace()); } } // E4X 9.1.1.3, pg 20 void E4XNode::_addInScopeNamespace (AvmCore* /*core*/, Namespace* /*ns*/, Namespacep /*publicNS*/) { // do nothing for non-element nodes } void ElementE4XNode::_addInScopeNamespace (AvmCore *core, Namespace *ns, Namespacep publicNS) { // if (getClass() & (kText | kCDATA | kComment | kProcessingInstruction | kAttribute)) // return; if (!ns || (ns->getPrefix() == undefinedAtom)) return; Multiname m; getQName(&m, publicNS); if ((ns->getPrefix() == core->kEmptyString->atom()) && (!m.isAnyNamespace()) && m.getNamespace()->getURI()->isEmpty()) return; // step 2b + 2c int index = -1; for (unsigned int i = 0, nn = numNamespaces(); i < nn; i++) { Namespace *ns2 = getNamespaces()->list.get(i); if (ns2->getPrefix() == ns->getPrefix()) index = i; } // step 2d if (index != -1) { Namespace *ns2 = getNamespaces()->list.get(index); if (ns2->getURI() != ns->getURI()) { // remove match from inscopenamespaces m_namespaces->list.removeAt(index); } } // step 2e - add namespace to inscopenamespaces if (!m_namespaces) m_namespaces = HeapNamespaceList::create(core->GetGC(), 1); m_namespaces->list.add(ns); // step 2f // If this nodes prefix == n prefix // set this nodes prefix to undefined if (!m.isAnyNamespace() && (m.getNamespace()->getPrefix() == ns->getPrefix())) { setQName(core, m.getName(), core->newNamespace(m.getNamespace()->getURI(), Namespace::NS_Public, kApiVersion_VM_ALLVERSIONS)); } // step 2g // for all attributes // if their nodes prefix == n.prefix // set the node prefix to undefined for (unsigned int i = 0, na = numAttributes(); i < na; i++) { E4XNode *curAttr = m_attributes->list.get(i); Multiname ma; curAttr->getQName(&ma, publicNS); if (!ma.isAnyNamespace() && ma.getNamespace()->getPrefix() == ns->getPrefix()) { curAttr->setQName(core, ma.getName(), core->newNamespace(ma.getNamespace()->getURI(), Namespace::NS_Public, kApiVersion_VM_ALLVERSIONS)); } } return; } int E4XNode::FindMatchingNamespace (AvmCore *core, Namespace *ns) { for (unsigned int i = 0, nn = numNamespaces(); i < nn; i++) { Namespace *ns2 = getNamespaces()->list.get(i); if (ns2->getURI() == ns->getURI()) { if (ns->getPrefix() == undefinedAtom) return i; if (ns2->getPrefix() == core->kEmptyString->atom()) return -1; if (ns2->getPrefix() == ns->getPrefix()) return i; } } return -1; } Namespace *E4XNode::FindNamespace(AvmCore *core, Toplevel *toplevel, Stringp& tagName, bool bAttribute) { int32_t pos = tagName->indexOfLatin1(":", 1, 0); // handle case of ":name" if (pos == 0) toplevel->throwTypeError(kXMLBadQName, tagName); Stringp prefix = core->kEmptyString; if (pos > 0) { prefix = core->internSubstring(tagName, 0, pos); tagName = core->internSubstring(tagName, pos + 1, tagName->length()); } // An attribute without a prefix is unqualified and does not inherit a namespace // from its parent. if (bAttribute && prefix == core->kEmptyString) return 0; // search all in scope namespaces for a matching prefix. If we find one // return that prefix, otherwise we need to throw an error. E4XNode *y = this; while (y) { for (unsigned int i = 0, nn = y->numNamespaces(); i < nn; i++) { Namespace *ns = y->getNamespaces()->list.get(i); if (((prefix == core->kEmptyString) && !ns->hasPrefix()) || (prefix->atom() == ns->getPrefix())) { return ns; } } y = y->m_parent; } if (prefix == core->kxml) { Namespacep nsXML = core->newNamespace(core->kEmptyString->atom(), core->kXML1998NS->atom()); return nsXML; } // throw error because we didn't match this prefix if (prefix != core->kEmptyString) { toplevel->throwTypeError(kXMLPrefixNotBound, prefix, tagName); } return 0; } void E4XNode::BuildInScopeNamespaceList(AvmCore* /*core*/, NamespaceList& inScopeNS) const { const E4XNode *y = this; while (y) { for (unsigned int i = 0, nn = y->numNamespaces(); i < nn; i++) { Namespace *ns1 = y->getNamespaces()->list.get(i); uint32_t j; for (j = 0; j < inScopeNS.length(); j++) { Namespace *ns2 = inScopeNS.get(j); if (ns1->getPrefix() == undefinedAtom) { if (ns1->getURI() == ns2->getURI()) break; } else { if (ns1->getPrefix() == ns2->getPrefix()) break; } } if (j == inScopeNS.length()) // no match { #ifdef STRING_DEBUG Stringp u = ns1->getURI(); Stringp p = core->string(ns1->getPrefix()); #endif inScopeNS.add(ns1); } } y = y->m_parent; } } void E4XNode::addAttribute (E4XNode* /*x*/) { AvmAssert(0); } void ElementE4XNode::addAttribute (E4XNode *x) { if (!m_attributes) m_attributes = HeapE4XNodeList::create(gc(), 1); m_attributes->list.add(x); } void ElementE4XNode::CopyAttributesAndNamespaces(AvmCore *core, Toplevel *toplevel, XMLTag& tag, Namespacep publicNS) { m_attributes = 0; m_namespaces = 0; uint32_t numAttr = 0; int32_t len; // We first handle namespaces because the a attribute tag can reference a namespace // defined farther on in the same node... // ... uint32_t index = 0; Stringp attributeName, attributeValue; while (tag.nextAttribute(index, attributeName, attributeValue)) { Namespace *ns = NULL; len = attributeName->length(); if (len >= 5) { // caseless match if (attributeName->matchesLatin1_caseless("xmlns", 5, 0)) { // a namespace xnlns:prefix="URI" or xmlns="URI" if ((len > 5) && attributeName->charAt(5) == ':') { if (len == 6) // xmlns:=uri -- throw exception because of badly formed XML??? toplevel->throwTypeError(kXMLBadQName, attributeName); Stringp prefix = attributeName->substring (6, len); ns = core->newNamespace(prefix->atom(), attributeValue->atom()); } else if (len == 5) { // xmlns=uri ns = core->newNamespace(core->kEmptyString->atom(), attributeValue->atom()); } // !!@ Don't intern these namespaces since the intern table ignores // the prefix value of the namespace. if (ns) // ns can be null if prefix is defined and attributeValue = "" this->_addInScopeNamespace(core, ns, publicNS); } } if (!ns) numAttr++; } if (!numAttr) return; m_attributes = HeapE4XNodeList::create(core->GetGC(), numAttr); // Now we read the attributes index = 0; while (tag.nextAttribute(index, attributeName, attributeValue)) { len = attributeName->length(); // check for namespace declarations and ignore them if (len >= 5) { // caseless match if (attributeName->matchesLatin1_caseless("xmlns", 5, 0)) { // a namespace xnlns:prefix="URI" or xmlns="URI" if ((len == 5) || ((len > 5) && attributeName->charAt(5) == ':')) continue; } } // !!@ intern our attributeValue?? E4XNode *attrObj = AttributeE4XNode::create(core->GetGC(), this, attributeValue); Namespace *ns = this->FindNamespace(core, toplevel, attributeName, true); if (!ns) ns = publicNS; attrObj->setQName(core, attributeName, ns); // check for a duplicate attribute here and throw a kXMLDuplicateAttribute if found Multiname m2; attrObj->getQName(&m2, publicNS); for (unsigned int i = 0, na = numAttributes(); i < na; i++) { E4XNode *curAttr = m_attributes->list.get(i); Multiname m; curAttr->getQName(&m, publicNS); if (m.matches(&m2)) { toplevel->typeErrorClass()->throwError(kXMLDuplicateAttribute, attributeName, tag.text, core->toErrorString(tag.text->length())); } } m_attributes->list.add(attrObj); } } ////////////////////////////////////////////////////////////////////// // E4X Section 9.1.1 ////////////////////////////////////////////////////////////////////// // E4X 9.1.1.4, pg 15 void E4XNode::_deleteByIndex (uint32_t i) { if (i < numChildren()) { E4XNode *x = _getAt(i); if (x) { x->m_parent = NULL; } removeChild (i); AvmAssert(numChildren() ^ 0x80000000); // check for underflow } } // E4X 9.1.1.7, page 16 E4XNode *E4XNode::_deepCopy (AvmCore *core, Toplevel *toplevel, Namespacep publicNS) const { core->stackCheck(toplevel); E4XNode *x = 0; switch (this->getClass()) { case kAttribute: x = AttributeE4XNode::create(core->GetGC(), 0, getValue()); break; case kText: x = TextE4XNode::create(core->GetGC(), 0, getValue()); break; case kCDATA: x = CDATAE4XNode::create(core->GetGC(), 0, getValue()); break; case kComment: x = CommentE4XNode::create(core->GetGC(), 0, getValue()); break; case kProcessingInstruction: x = PIE4XNode::create(core->GetGC(), 0, getValue()); break; case kElement: x = ElementE4XNode::create(core->GetGC(), 0); break; } Multiname m; if (this->getQName(&m, publicNS)) { x->setQName (core, &m); } if (x->getClass() == kElement) { ElementE4XNode *y = (ElementE4XNode *) x; // step 2 - for each ns in inScopeNamespaces unsigned int nn = numNamespaces(); if (nn) { y->m_namespaces = HeapNamespaceList::create(core->GetGC(), nn); uint32_t i; for (i = 0; i < nn; i++) { y->m_namespaces->list.add(getNamespaces()->list.get(i)); } } // step 3 - duplicate attribute nodes unsigned int na = numAttributes(); if (na) { y->m_attributes = HeapE4XNodeList::create(core->GetGC(), na); uint32_t i; for (i = 0; i < na; i++) { E4XNode *ax = getAttribute (i); E4XNode *bx = ax->_deepCopy(core, toplevel, publicNS); bx->setParent(y); y->addAttribute(bx); } } // step 4 - duplicate children if (numChildren()) { AvmAssert(y->m_children == 0); WB(gc(), y, &y->m_children, uintptr_t(HeapE4XNodeList::create(core->GetGC(), numChildren()))); for (uint32_t k = 0; k < _length(); k++) { E4XNode *child = _getAt(k); if (((child->getClass() == E4XNode::kComment) && toplevel->xmlClass()->get_ignoreComments()) || ((child->getClass() == E4XNode::kProcessingInstruction) && toplevel->xmlClass()->get_ignoreProcessingInstructions())) { continue; } E4XNode *cx = child->_deepCopy (core, toplevel, publicNS); cx->setParent (y); //y->m_children->push (c); y->_append (cx); } } } return x; } // FIXME: copy and paste E4XNode *E4XNode::_deepCopyForeign (AvmCore *core, Toplevel *toplevel, Namespacep publicNS) const { core->stackCheck(toplevel); E4XNode *x = 0; switch (this->getClass()) { case kAttribute: x = AttributeE4XNode::create(core->GetGC(), 0, getValue()->clone(core)); break; case kText: x = TextE4XNode::create(core->GetGC(), 0, getValue()->clone(core)); break; case kCDATA: x = CDATAE4XNode::create(core->GetGC(), 0, getValue()->clone(core)); break; case kComment: x = CommentE4XNode::create(core->GetGC(), 0, getValue()->clone(core)); break; case kProcessingInstruction: x = PIE4XNode::create(core->GetGC(), 0, getValue()->clone(core)); break; case kElement: x = ElementE4XNode::create(core->GetGC(), 0); break; } Multiname m; if (this->getQNameForeign(core, &m, publicNS)) { x->setQName (core, &m); } if (x->getClass() == kElement) { ElementE4XNode *y = (ElementE4XNode *) x; // step 2 - for each ns in inScopeNamespaces if (numNamespaces()) { y->m_namespaces = HeapNamespaceList::create(core->GetGC(), numNamespaces()); uint32_t i; for (i = 0; i < numNamespaces(); i++) { y->m_namespaces->list.add(core->cloneNamespace(getNamespaces()->list.get(i))); } } // step 3 - duplicate attribute nodes if (numAttributes()) { y->m_attributes = HeapE4XNodeList::create(core->GetGC(), numAttributes()); uint32_t i; for (i = 0; i < numAttributes(); i++) { E4XNode *ax = getAttribute (i); E4XNode *bx = ax->_deepCopyForeign(core, toplevel, publicNS); bx->setParent(y); y->addAttribute(bx); } } // step 4 - duplicate children if (numChildren()) { AvmAssert(y->m_children == 0); y->m_children = uintptr_t(HeapE4XNodeList::create(core->GetGC(), numChildren())); for (uint32_t k = 0; k < _length(); k++) { E4XNode *child = _getAt(k); if (((child->getClass() == E4XNode::kComment) && toplevel->xmlClass()->get_ignoreComments()) || ((child->getClass() == E4XNode::kProcessingInstruction) && toplevel->xmlClass()->get_ignoreProcessingInstructions())) { continue; } E4XNode *cx = child->_deepCopyForeign (core, toplevel, publicNS); cx->setParent (y); //y->m_children->push (c); y->_append (cx); } } } return x; } #if 0 // E4X 9.1.1.8, page 17 Atom E4XNode::descendants(Atom P) const { Multiname m; toplevel->ToXMLName (P, m); return getDescendants (&m); } #endif // E4X 9.1.1.9, page 17 bool E4XNode::_equals(Toplevel* toplevel, AvmCore *core, E4XNode *v) const { core->stackCheck(toplevel); if (this == v) return true; if (this->getClass() != v->getClass()) return false; Multiname m; Multiname m2; Namespacep publicNS = core->findPublicNamespace(); if (this->getQName(&m, publicNS)) { if (v->getQName(&m2, publicNS) == 0) return false; // QName/AttributeName comparision here if (!m.matches(&m2)) return false; } else if (v->getQName(&m2, publicNS) != 0) { return false; } // Not enabled after discussion with JeffD. If the namespaces are important, they're // used in the node names themselves. #if 0 // NOT part of the original spec. Added in later (bug 144429) if (this->numNamespaces() != v->numNamespaces()) return false; // Order of namespaces does not matter NamespaceList *ns1 = getNamespaces(); NamespaceList *ns2 = v->getNamespaces(); for (uint32_t n1 = 0; n1 < numNamespaces(); n1++) { Namespace *namespace1 = ns1->get(n1); for (uint32_t n2 = 0; n2 < numNamespaces(); n2++) { Namespace *namespace2 = ns2->get(n2); if (namespace1->EqualTo (namespace2)) break; } // A match was not found if (n2 == numNamespaces()) return false; } #endif if (this->numAttributes() != v->numAttributes()) return false; if (this->numChildren() != v->numChildren()) return false; if (this->getValue() != v->getValue() && (this->getValue()==NULL || v->getValue()==NULL || *getValue() != *v->getValue())) return false; // step 8 // for each a in x.attributes // if v does not containing matching attribute, return failure for (uint32_t k1 = 0, na1 = numAttributes(); k1 < na1; k1++) { E4XNode *x1 = getAttribute(k1); bool bFoundMatch = false; for (uint32_t k2 = 0, na2 = v->numAttributes(); k2 < na2; k2++) { if (x1->_equals(toplevel, core, v->getAttribute(k2))) { bFoundMatch = true; break; } } if (!bFoundMatch) return false; } // step 9 for (uint32_t i = 0; i < _length(); i++) { E4XNode *x1 = _getAt(i); E4XNode *x2 = v->_getAt(i); if (!x1->_equals(toplevel, core, x2)) return false; } return true; } // E4X 9.1.1.11, page 18 void E4XNode::_insert (AvmCore* /*core*/, Toplevel* /*toplevel*/, uint32_t /*entry*/, Atom /*value*/) { return; } void ElementE4XNode::_insert (AvmCore *core, Toplevel *toplevel, uint32_t entry, Atom value) { // //step 1 // if (m_class & (kText | kCDATA | kComment | kProcessingInstruction | kAttribute)) // return; // Spec says to throw a typeError if entry is not a number // We handle that in callingn functions uint32_t n = 1; XMLListObject *xl = AvmCore::atomToXMLList(value); if (xl) { n = xl->_length(); } else { E4XNode *x = AvmCore::atomToXML(value); if (x) { E4XNode *n = this; while (n) { if (x == n) toplevel->throwTypeError(kXMLIllegalCyclicalLoop); n = n->getParent(); } } } if (n == 0) return; if (!m_children) { WB(gc(), this, &m_children, uintptr_t(HeapE4XNodeList::create(core->GetGC(), n))); } if (xl) { // insert each element of our XMLList into our array for (uint32_t j = 0; j < xl->_length(); j++) { E4XNode *child = AvmCore::atomToXML(xl->_getAt(j)->atom()); // !!@ Not in spec but seems like a good idea E4XNode *n = this; while (n) { if (child == n) toplevel->throwTypeError(kXMLIllegalCyclicalLoop); n = n->getParent(); } child->setParent(this); insertChild (entry + j, child); } } else { insertChild (entry, 0); // make room for our replace this->_replace (core, toplevel, entry, value); } return; } // E4X 9.1.1.12, page 19 // Autoconverts V into an XML object E4XNode* E4XNode::_replace (AvmCore* /*core*/, Toplevel* /*toplevel*/, uint32_t /*i*/, Atom /*V*/, Atom /*pastValue*/) { return 0; } E4XNode* ElementE4XNode::_replace (AvmCore *core, Toplevel *toplevel, uint32_t i, Atom V, Atom pastValue) { //step 1 //if (getClass() & (kText | kCDATA | kComment | kProcessingInstruction | kAttribute)) // return; // step 2 + 3 // API throws a typeError if entry is not a number // This is always handled back in the caller. // step 4 if (i >= _length()) { i = _length(); // add a blank spot for this child if (!m_children) WB(gc(), this, &m_children, uintptr_t(HeapE4XNodeList::create(core->GetGC(), 1))); convertToE4XNodeList(); HeapE4XNodeList* aa = ((HeapE4XNodeList*)(uintptr_t)m_children); aa->list.add(NULL); } E4XNode *prior = _getAt(i); // step 5 E4XNode *xml = AvmCore::atomToXML(V); if (xml && (xml->getClass() & (kElement | kComment | kProcessingInstruction | kText | kCDATA))) { //a. If V.[[Class]] is "element" and (V is x or an ancestor of x) throw an Error exception if (xml->getClass() == kElement) { E4XNode *n = this; while (n) { if (xml == n) toplevel->throwTypeError(kXMLIllegalCyclicalLoop); n = n->getParent(); } } xml->setParent (this); if (i < this->numChildren()) { if (prior) { prior->setParent (NULL); } } this->setChildAt (i, xml); } else if (AvmCore::atomToXMLList(V)) { _deleteByIndex (i); _insert (core, toplevel, i, V); } else { Stringp s = core->string(V); E4XNode *newXML = TextE4XNode::create(core->GetGC(), this, s); // if this[i] is going away, clear its parent if (prior) { prior->setParent (NULL); } setChildAt (i, newXML); if (XMLObject::notifyNeeded(newXML)) { Atom detail = prior ? prior->getValue()->atom() : 0; if (!detail) detail = pastValue; XMLObject* target = XMLObject::create(core->GetGC(), toplevel->xmlClass(), newXML); target->nonChildChanges(core->ktextSet, newXML->getValue()->atom(), detail); } } return prior; } void ElementE4XNode::setNotification(AvmCore *core, FunctionObject* f, Namespacep publicNS) { uintptr_t nameOrAux = m_nameOrAux; // We already have an aux structure if (AUXBIT & nameOrAux) { E4XNodeAux *aux = (E4XNodeAux *)(nameOrAux & ~AUXBIT); aux->m_notification = f; } // allocate one to hold our name and notification atom else { Stringp str = (String *)(nameOrAux); E4XNodeAux *aux = E4XNodeAux::create(core->GetGC(), str, publicNS, f); //m_nameOrAux = AUXBIT | int(aux); // Clear it out in case there's already a name there. WBRC_NULL(&m_nameOrAux); WB(core->GetGC(), this, &m_nameOrAux, AUXBIT | uintptr_t(aux)); } } FunctionObject* ElementE4XNode::getNotification() const { uintptr_t nameOrAux = m_nameOrAux; if (AUXBIT & m_nameOrAux) { E4XNodeAux *aux = (E4XNodeAux *)(nameOrAux & ~AUXBIT); return aux->m_notification; } return 0; } bool E4XNode::hasSimpleContent() const { if (getClass() & (E4XNode::kComment | E4XNode::kProcessingInstruction)) return false; // for each prop in x, if x.class == element, return false for (uint32_t i = 0; i < _length(); i++) { E4XNode *child = _getAt(i); if (child->getClass() == E4XNode::kElement) { return false; } } return true; } bool E4XNode::hasComplexContent() const { if (getClass() & (E4XNode::kText | E4XNode::kComment | E4XNode::kProcessingInstruction | E4XNode::kAttribute | E4XNode::kCDATA)) return false; for (uint32_t i = 0; i < _length(); i++) { E4XNode *child = _getAt(i); if (child->getClass() == E4XNode::kElement) { return true; } } return false; } int E4XNode::childIndex() const { if ((m_parent == NULL) || (getClass() == E4XNode::kAttribute)) return -1; // find this child in parent's children list - return ordinal AvmAssert(m_parent->_length()); // this child's parent does not contain itself??? for (uint32_t i = 0; i < m_parent->_length(); i++) { E4XNode *x = m_parent->_getAt(i); if (x == this) { return i; } } // this child's parent does not contain itself??? AvmAssert(0); return -1; } String *E4XNode::nodeKind(Toplevel* toplevel) const { AvmCore* core = toplevel->core(); switch (getClass()) { case E4XNode::kAttribute: return core->kattribute; case E4XNode::kText: case E4XNode::kCDATA: return core->ktext; case E4XNode::kComment: return core->kcomment; case E4XNode::kProcessingInstruction: return core->kprocessing_instruction; case E4XNode::kElement: return core->kelement; case E4XNode::kUnknown: default: AvmAssert(0); return 0; } } void E4XNode::dispose() { for(uint32_t i=0,n=numChildren();i < n; i++) { E4XNode *node = _getAt(i); node->dispose(); node->setParent(NULL); node->clearChildren(); } #if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER) IMemorySampler *memSampler = (IMemorySampler *)gc()->GetAttachedSampler(); if (memSampler) { memSampler->recordDeallocation(this, GC::Size(this)); } #endif } void ElementE4XNode::dispose() { for (uint32_t i=0, na=numAttributes(); idispose(); E4XNode::dispose(); } #ifdef DEBUGGER uint64_t E4XNode::bytesUsed() const { // find the root node of the tree const E4XNode* root = this; while (root->m_parent) root = root->m_parent; // now find the size going down from there return root->bytesUsedDown(); } uint64_t E4XNode::bytesUsedDown() const { uint64_t size = GC::Size(this); if (m_nameOrAux) { uintptr_t nameOrAux = m_nameOrAux; if (AUXBIT & nameOrAux) { E4XNodeAux *aux = (E4XNodeAux *)(nameOrAux & ~AUXBIT); size += aux->bytesUsed(); } else { Stringp str = (String *)(nameOrAux); size += str->bytesUsed(); } } return size; } uint64_t E4XNode::bytesUsedShallow() const { uint64_t size = GC::Size(this); HeapE4XNodeList* attr = getAttributes(); if (attr) size += GC::Size(attr); HeapNamespaceList* nslist = getNamespaces(); if (nslist) size += GC::Size(nslist); return size; } uint64_t ElementE4XNode::bytesUsedDown() const { // Add up all the memory taken by this element plus all the namespaces, // attributes, and elements under it. uint64_t size = E4XNode::bytesUsedDown(); for (uint32_t i=0, nn=numNamespaces(); ilist.get(i); size += ns1->bytesUsedDeep(); } for (uint32_t i=0, na=numAttributes(); ibytesUsedDown(); for (uint32_t i=0, n=_length(); ibytesUsedDown(); return size; } #endif }