protected override QilNode VisitXsltInvokeEarlyBound(QilInvokeEarlyBound ndInvoke) {
QilName ndName = ndInvoke.Name;
XmlExtensionFunction extFunc;
Type clrTypeRetSrc, clrTypeRetDst;
// Retrieve metadata from the extension function
extFunc = new XmlExtensionFunction(ndName.LocalName, ndName.NamespaceUri, ndInvoke.ClrMethod);
clrTypeRetSrc = extFunc.ClrReturnType;
clrTypeRetDst = GetStorageType(ndInvoke);
// Prepare to call runtime.ChangeTypeXsltResult
if (clrTypeRetSrc != clrTypeRetDst && !ndInvoke.XmlType.IsEmpty) {
this.helper.LoadQueryRuntime();
this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(ndInvoke.XmlType));
}
// If this is not a static method, then get the instance object
if (!extFunc.Method.IsStatic) {
// Special-case the XsltLibrary object
if (ndName.NamespaceUri.Length == 0)
this.helper.LoadXsltLibrary();
else
this.helper.CallGetEarlyBoundObject(this.helper.StaticData.DeclareEarlyBound(ndName.NamespaceUri, extFunc.Method), extFunc.Method.DeclaringType);
}
// Generate code to push each Invoke argument onto the stack
for (int iArg = 0; iArg < ndInvoke.Arguments.Count; iArg++) {
QilNode ndActualArg;
XmlQueryType xmlTypeFormalArg;
Type clrTypeActualArg, clrTypeFormalArg;
ndActualArg = ndInvoke.Arguments[iArg];
// Infer Xml type and Clr type of formal argument
xmlTypeFormalArg = extFunc.GetXmlArgumentType(iArg);
clrTypeFormalArg = extFunc.GetClrArgumentType(iArg);
Debug.Assert(ndActualArg.XmlType.IsSubtypeOf(xmlTypeFormalArg), "Xml type of actual arg must be a subtype of the Xml type of the formal arg");
// Use different conversion rules for internal Xslt libraries. If the actual argument is
// stored using Clr type T, then library must use type T, XPathItem, IList<T>, or IList<XPathItem>.
// If the actual argument is stored using Clr type IList<T>, then library must use type
// IList<T> or IList<XPathItem>. This is to ensure that there will not be unnecessary
// conversions that take place when calling into an internal library.
if (ndName.NamespaceUri.Length == 0) {
Type itemType = GetItemStorageType(ndActualArg);
if (clrTypeFormalArg == XmlILMethods.StorageMethods[itemType].IListType) {
// Formal type is IList<T>
NestedVisitEnsureStack(ndActualArg, itemType, true);
}
else if (clrTypeFormalArg == XmlILMethods.StorageMethods[typeof(XPathItem)].IListType) {
// Formal type is IList<XPathItem>
NestedVisitEnsureStack(ndActualArg, typeof(XPathItem), true);
}
else if ((ndActualArg.XmlType.IsSingleton && clrTypeFormalArg == itemType) || ndActualArg.XmlType.TypeCode == XmlTypeCode.None) {
// Formal type is T
NestedVisitEnsureStack(ndActualArg, itemType, false);
}
else if (ndActualArg.XmlType.IsSingleton && clrTypeFormalArg == typeof(XPathItem)) {
// Formal type is XPathItem
NestedVisitEnsureStack(ndActualArg, typeof(XPathItem), false);
}
else
Debug.Fail("Internal Xslt library may not use parameters of type " + clrTypeFormalArg);
}
else {
// There is an implicit upcast to the Xml type of the formal argument. This can change the Clr storage type.
clrTypeActualArg = GetStorageType(xmlTypeFormalArg);
// If the formal Clr type is typeof(object) or if it is not a supertype of the actual Clr type, then call ChangeTypeXsltArgument
if (xmlTypeFormalArg.TypeCode == XmlTypeCode.Item || !clrTypeFormalArg.IsAssignableFrom(clrTypeActualArg)) {
// (clrTypeFormalArg) runtime.ChangeTypeXsltArgument(xmlTypeFormalArg, (object) value, clrTypeFormalArg);
this.helper.LoadQueryRuntime();
this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(xmlTypeFormalArg));
NestedVisitEnsureStack(ndActualArg, GetItemStorageType(xmlTypeFormalArg), !xmlTypeFormalArg.IsSingleton);
this.helper.TreatAs(clrTypeActualArg, typeof(object));
this.helper.LoadType(clrTypeFormalArg);
this.helper.Call(XmlILMethods.ChangeTypeXsltArg);
this.helper.TreatAs(typeof(object), clrTypeFormalArg);
}
else {
NestedVisitEnsureStack(ndActualArg, GetItemStorageType(xmlTypeFormalArg), !xmlTypeFormalArg.IsSingleton);
}
}
}
// Invoke the target method
this.helper.Call(extFunc.Method);
// Return value is on the stack; convert it to canonical ILGen storage type
if (ndInvoke.XmlType.IsEmpty) {
this.helper.Emit(OpCodes.Ldsfld, XmlILMethods.StorageMethods[typeof(XPathItem)].SeqEmpty);
}
else if (clrTypeRetSrc != clrTypeRetDst) {
// (T) runtime.ChangeTypeXsltResult(idxType, (object) value);
this.helper.TreatAs(clrTypeRetSrc, typeof(object));
this.helper.Call(XmlILMethods.ChangeTypeXsltResult);
this.helper.TreatAs(typeof(object), clrTypeRetDst);
}
else if (ndName.NamespaceUri.Length != 0 && !clrTypeRetSrc.IsValueType){
// Check for null if a user-defined extension function returns a reference type
Label lblSkip = this.helper.DefineLabel();
this.helper.Emit(OpCodes.Dup);
this.helper.Emit(OpCodes.Brtrue, lblSkip);
this.helper.LoadQueryRuntime();
this.helper.Emit(OpCodes.Ldstr, Res.GetString(Res.Xslt_ItemNull));
this.helper.Call(XmlILMethods.ThrowException);
this.helper.MarkLabel(lblSkip);
}
this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndInvoke), !ndInvoke.XmlType.IsSingleton);
return ndInvoke;
}