private bool MatchesNodeKinds(QilTargetType ndIsType, XmlQueryType typDerived, XmlQueryType typBase) {
XmlNodeKindFlags kinds;
bool allowKinds = true;
XPathNodeType kindsRuntime;
int kindsUnion;
// If not checking whether typDerived is some kind of singleton node, then fallback to MatchesXmlType
if (!typBase.IsNode || !typBase.IsSingleton)
return false;
// If typDerived is not statically guaranteed to be a singleton node (and not an rtf), then fallback to MatchesXmlType
if (!typDerived.IsNode || !typDerived.IsSingleton || !typDerived.IsNotRtf)
return false;
// Now we are guaranteed that typDerived is a node, and typBase is a node, so check node kinds
// Ensure that typBase is only composed of kind-test prime types (no name-test, no schema-test, etc.)
kinds = XmlNodeKindFlags.None;
foreach (XmlQueryType typItem in typBase) {
if (Ref.Equals(typItem, TypeFactory.Element)) kinds |= XmlNodeKindFlags.Element;
else if (Ref.Equals(typItem, TypeFactory.Attribute)) kinds |= XmlNodeKindFlags.Attribute;
else if (Ref.Equals(typItem, TypeFactory.Text)) kinds |= XmlNodeKindFlags.Text;
else if (Ref.Equals(typItem, TypeFactory.Document)) kinds |= XmlNodeKindFlags.Document;
else if (Ref.Equals(typItem, TypeFactory.Comment)) kinds |= XmlNodeKindFlags.Comment;
else if (Ref.Equals(typItem, TypeFactory.PI)) kinds |= XmlNodeKindFlags.PI;
else if (Ref.Equals(typItem, TypeFactory.Namespace)) kinds |= XmlNodeKindFlags.Namespace;
else return false;
}
Debug.Assert((typDerived.NodeKinds & kinds) != XmlNodeKindFlags.None, "Normalizer should have taken care of case where node kinds are disjoint.");
kinds = typDerived.NodeKinds & kinds;
// Attempt to allow or disallow exactly one kind
if (!Bits.ExactlyOne((uint) kinds)) {
// Not possible to allow one kind, so try to disallow one kind
kinds = ~kinds & XmlNodeKindFlags.Any;
allowKinds = !allowKinds;
}
switch (kinds) {
case XmlNodeKindFlags.Element: kindsRuntime = XPathNodeType.Element; break;
case XmlNodeKindFlags.Attribute: kindsRuntime = XPathNodeType.Attribute; break;
case XmlNodeKindFlags.Namespace: kindsRuntime = XPathNodeType.Namespace; break;
case XmlNodeKindFlags.PI: kindsRuntime = XPathNodeType.ProcessingInstruction; break;
case XmlNodeKindFlags.Comment: kindsRuntime = XPathNodeType.Comment; break;
case XmlNodeKindFlags.Document: kindsRuntime = XPathNodeType.Root; break;
default:
// Union of several types (when testing for Text, we need to test for Whitespace as well)
// if (((1 << navigator.NodeType) & nodesDisallow) op 0) goto LabelBranch;
this.helper.Emit(OpCodes.Ldc_I4_1);
kindsRuntime = XPathNodeType.All;
break;
}
// Push navigator.NodeType onto the stack
NestedVisitEnsureStack(ndIsType.Source);
this.helper.Call(XmlILMethods.NavType);
if (kindsRuntime == XPathNodeType.All) {
// if (((1 << navigator.NodeType) & kindsUnion) op 0) goto LabelBranch;
this.helper.Emit(OpCodes.Shl);
kindsUnion = 0;
if ((kinds & XmlNodeKindFlags.Document) != 0) kindsUnion |= (1 << (int) XPathNodeType.Root);
if ((kinds & XmlNodeKindFlags.Element) != 0) kindsUnion |= (1 << (int) XPathNodeType.Element);
if ((kinds & XmlNodeKindFlags.Attribute) != 0) kindsUnion |= (1 << (int) XPathNodeType.Attribute);
if ((kinds & XmlNodeKindFlags.Text) != 0) kindsUnion |= (1 << (int) (int) XPathNodeType.Text) |
(1 << (int) (int) XPathNodeType.SignificantWhitespace) |
(1 << (int) (int) XPathNodeType.Whitespace);
if ((kinds & XmlNodeKindFlags.Comment) != 0) kindsUnion |= (1 << (int) XPathNodeType.Comment);
if ((kinds & XmlNodeKindFlags.PI) != 0) kindsUnion |= (1 << (int) XPathNodeType.ProcessingInstruction);
if ((kinds & XmlNodeKindFlags.Namespace) != 0) kindsUnion |= (1 << (int) XPathNodeType.Namespace);
this.helper.LoadInteger(kindsUnion);
this.helper.Emit(OpCodes.And);
ZeroCompare(allowKinds ? QilNodeType.Ne : QilNodeType.Eq, false);
}
else {
// if (navigator.NodeType op runtimeItem) goto LabelBranch;
this.helper.LoadInteger((int) kindsRuntime);
ClrCompare(allowKinds ? QilNodeType.Eq : QilNodeType.Ne, XmlTypeCode.Int);
}
return true;
}