Tuple<CodeTypeDeclaration, CodeTypeDeclaration> GenerateFunctionType(XPathFunctionInfo[] functions) { XPathFunctionInfo first = functions.First(); XPathFunctionInfo leastParameters = functions.OrderBy(f => f.Parameters.Count).First(); XPathFunctionInfo mostParameters = functions.OrderByDescending(f => f.Parameters.Count).First(); XPathModuleInfo module = first.Module; int minArgs = leastParameters.Parameters.Count; int maxArgs = mostParameters.Parameters.Count; CodeExpression thisRef = new CodeThisReferenceExpression(); var processorField = new CodeMemberField { Name = "_processor", Type = new CodeTypeReference(typeof(SaxonProcessor)) }; var funcNameField = new CodeMemberField { Name = "_FunctionName", Type = new CodeTypeReference(typeof(QName)), InitExpression = new CodeObjectCreateExpression( typeof(QName), new CodePrimitiveExpression(module.Namespace), new CodePrimitiveExpression(first.Name) ) }; var argTypesField = new CodeMemberField { Name = "_ArgumentTypes", Type = new CodeTypeReference(typeof(XdmSequenceType[])), InitExpression = new CodeArrayCreateExpression( typeof(XdmSequenceType), mostParameters.Parameters.Select(p => new CodeObjectCreateExpression( typeof(XdmSequenceType), GetXdmItemTypeExpression(p.Type.ItemType), new CodePrimitiveExpression(GetOcurrenceIndicator(p.Type.Cardinality)) )).ToArray() ) }; var resultTypesField = new CodeMemberField { Name = "_resultTypes", Type = new CodeTypeReference(typeof(XdmSequenceType[])) }; var resultTypesFieldInit = new CodeArrayCreateExpression { CreateType = resultTypesField.Type, Size = functions.Length }; for (int i = 0; i < functions.Length; i++) { XPathFunctionInfo function = functions[i]; resultTypesFieldInit.Initializers.Add( // Using item()? instead of empty-sequence() (function.ReturnType.IsEmptySequence) ? new CodeObjectCreateExpression( typeof(XdmSequenceType), new CodeFieldReferenceExpression( new CodeTypeReferenceExpression(typeof(XdmAnyItemType)), "Instance" ), new CodePrimitiveExpression(GetOcurrenceIndicator(XPathSequenceCardinality.ZeroOrOne)) ) : new CodeObjectCreateExpression( typeof(XdmSequenceType), GetXdmItemTypeExpression(function.ReturnType.ItemType), new CodePrimitiveExpression(GetOcurrenceIndicator(function.ReturnType.Cardinality)) ) ); } resultTypesField.InitExpression = resultTypesFieldInit; var defClass = new CodeTypeDeclaration { Name = first.Method.Name + "Function", Attributes = MemberAttributes.Public, BaseTypes = { typeof(ExtensionFunctionDefinition) }, Members = { processorField, funcNameField, argTypesField, resultTypesField, new CodeConstructor { Attributes = MemberAttributes.Public, Parameters = { new CodeParameterDeclarationExpression(processorField.Type, "processor") }, Statements = { new CodeAssignStatement( new CodeFieldReferenceExpression(thisRef, processorField.Name), new CodeVariableReferenceExpression("processor") ) } }, new CodeMemberProperty { Name = "FunctionName", Type = funcNameField.Type, Attributes = MemberAttributes.Public | MemberAttributes.Override, HasGet = true, GetStatements = { new CodeMethodReturnStatement(new CodeFieldReferenceExpression(thisRef, funcNameField.Name)) } }, new CodeMemberProperty { Name = "ArgumentTypes", Type = argTypesField.Type, Attributes = MemberAttributes.Public | MemberAttributes.Override, HasGet = true, GetStatements = { new CodeMethodReturnStatement(new CodeFieldReferenceExpression(thisRef, argTypesField.Name)) } }, new CodeMemberProperty { Name = "MaximumNumberOfArguments", Type = new CodeTypeReference(typeof(int)), Attributes = MemberAttributes.Public | MemberAttributes.Override, HasGet = true, GetStatements = { new CodeMethodReturnStatement(new CodePrimitiveExpression(maxArgs)) } }, new CodeMemberProperty { Name = "MinimumNumberOfArguments", Type = new CodeTypeReference(typeof(int)), Attributes = MemberAttributes.Public | MemberAttributes.Override, HasGet = true, GetStatements = { new CodeMethodReturnStatement(new CodePrimitiveExpression(minArgs)) } }, GenerateResultTypeMethod(thisRef, resultTypesField) } }; if (first.HasSideEffects) { defClass.Members.Add(new CodeMemberProperty { Name = "HasSideEffects", Type = new CodeTypeReference(typeof(bool)), Attributes = MemberAttributes.Public | MemberAttributes.Override, HasGet = true, GetStatements = { new CodeMethodReturnStatement(new CodePrimitiveExpression(true)) } }); } var callClass = new CodeTypeDeclaration { Name = defClass.Name + "Call", Attributes = MemberAttributes.Assembly, BaseTypes = { new CodeTypeReference(typeof(ExtensionFunctionCall)) }, Members = { processorField, new CodeConstructor { Attributes = MemberAttributes.Public, Parameters = { new CodeParameterDeclarationExpression(processorField.Type, "processor") }, Statements = { new CodeAssignStatement( new CodeFieldReferenceExpression(thisRef, processorField.Name), new CodeVariableReferenceExpression("processor") ) } } } }; CodeMemberMethod initializeMethod = null; if (!module.TypeIsStatic && module.Dependencies.Count > 0) { CodeExpression staticBaseUriExpr = null; if (module.Dependencies.Any(d => d.Type == typeof(XmlResolver))) { var staticBaseUriField = new CodeMemberField { Name = "_staticBaseUri", Type = new CodeTypeReference(typeof(Uri)) }; callClass.Members.Add(staticBaseUriField); var staticContextParam = new CodeParameterDeclarationExpression(typeof(StaticContext), "context"); staticBaseUriExpr = new CodeFieldReferenceExpression(thisRef, staticBaseUriField.Name); callClass.Members.Add(new CodeMemberMethod { Name = "SupplyStaticContext", Attributes = MemberAttributes.Public | MemberAttributes.Override, Parameters = { staticContextParam }, Statements = { new CodeAssignStatement { Left = staticBaseUriExpr, Right = new CodePropertyReferenceExpression { PropertyName = "BaseUri", TargetObject = new CodeVariableReferenceExpression(staticContextParam.Name) } } } }); } initializeMethod = GenerateInitialize(module, new CodeFieldReferenceExpression(thisRef, processorField.Name), staticBaseUriExpr); callClass.Members.Add(initializeMethod); } callClass.Members.Add(GenerateCallMethod(functions, minArgs, maxArgs, new CodeFieldReferenceExpression(thisRef, processorField.Name), initializeMethod)); defClass.Members.Add( new CodeMemberMethod { Name = "MakeFunctionCall", Attributes = MemberAttributes.Public | MemberAttributes.Override, ReturnType = new CodeTypeReference(typeof(ExtensionFunctionCall)), Statements = { new CodeMethodReturnStatement( new CodeObjectCreateExpression( callClass.Name, new CodeFieldReferenceExpression(thisRef, processorField.Name) ) ) } } ); return Tuple.Create(defClass, callClass); }
static CodeMemberMethod GenerateFunction(XPathFunctionInfo function, CodeExpression moduleExpr) { string name = function.Name; MemberAttributes methodAttributes = MemberAttributes.Public; if (name.Contains('-')) { name = name.Replace('-', '_'); methodAttributes = MemberAttributes.Family; } methodAttributes |= MemberAttributes.Final; Type procReturnType = GetReturnType(function.ReturnType); var codeMethod = new CodeMemberMethod { Name = name, Attributes = methodAttributes, ReturnType = new CodeTypeReference(procReturnType), }; var methodInvoke = new CodeMethodInvokeExpression { Method = new CodeMethodReferenceExpression { MethodName = function.Method.Name, TargetObject = moduleExpr } }; for (int i = 0; i < function.Parameters.Count; i++) { XPathSequenceType paramTypeInfo = function.Parameters[i].Type; Type procParamType = GetParameterType(paramTypeInfo); var paramDecl = new CodeParameterDeclarationExpression { Name = "p" + i.ToStringInvariant(), Type = new CodeTypeReference(procParamType) }; codeMethod.Parameters.Add(paramDecl); var paramVarExpr = new CodeVariableReferenceExpression(paramDecl.Name); CodeExpression argExpr = GetArgumentExpression(paramTypeInfo, procParamType, paramVarExpr); methodInvoke.Parameters.Add(argExpr); } CodeVariableReferenceExpression resultVarExpr = null; if (function.ReturnType.ClrType == typeof(void)) { codeMethod.Statements.Add(new CodeExpressionStatement(methodInvoke)); } else { var resultVarDecl = new CodeVariableDeclarationStatement { Name = "result", InitExpression = methodInvoke, Type = new CodeTypeReference(function.Method.ReturnType) }; codeMethod.Statements.Add(resultVarDecl); resultVarExpr = new CodeVariableReferenceExpression(resultVarDecl.Name); } CodeExpression returnExpr = GetReturnExpression(function.ReturnType, resultVarExpr); codeMethod.Statements.Add(new CodeMethodReturnStatement(returnExpr)); return codeMethod; }
CodeMemberMethod GenerateCallMethod(XPathFunctionInfo[] functions, int minArgs, int maxArgs, CodeExpression processorRef, CodeMemberMethod initializeMethod) { XPathFunctionInfo mostParameters = functions.OrderByDescending(f => f.Parameters.Count).First(); XPathModuleInfo module = mostParameters.Module; var argsParam = new CodeParameterDeclarationExpression(typeof(IXdmEnumerator[]), "arguments"); var callMethod = new CodeMemberMethod { Name = "Call", Attributes = MemberAttributes.Public | MemberAttributes.Override, ReturnType = new CodeTypeReference(typeof(IXdmEnumerator)), Parameters = { argsParam, new CodeParameterDeclarationExpression(typeof(DynamicContext), "context") } }; var argumentsRef = new CodeVariableReferenceExpression(callMethod.Parameters[0].Name); CodeExpression moduleRef; if (module.TypeIsStatic) { moduleRef = new CodeTypeReferenceExpression(module.Type); } else { var moduleVar = new CodeVariableDeclarationStatement { Name = "module", Type = new CodeTypeReference(module.Type), InitExpression = new CodeObjectCreateExpression(module.Type) }; callMethod.Statements.Add(moduleVar); moduleRef = new CodeVariableReferenceExpression(moduleVar.Name); if (initializeMethod != null) { callMethod.Statements.Add(new CodeMethodInvokeExpression { Method = new CodeMethodReferenceExpression(new CodeThisReferenceExpression(), initializeMethod.Name), Parameters = { moduleRef } }); } } CodeStatementCollection currentBlock = callMethod.Statements; var paramRef = new List<CodeExpression>(); for (int pos = 0; pos <= maxArgs; pos++) { if (pos > 0) { XPathSequenceType paramInfo = mostParameters.Parameters[pos - 1].Type; var paramVar = new CodeVariableDeclarationStatement { Name = "p" + pos, Type = new CodeTypeReference(paramInfo.ClrType), InitExpression = TransformInput(argumentsRef, pos, paramInfo) }; currentBlock.Add(paramVar); paramRef.Add(new CodeVariableReferenceExpression(paramVar.Name)); } if (pos >= minArgs) { XPathFunctionInfo fn = functions[pos - minArgs]; CodeConditionStatement ifElse = null; if (minArgs != maxArgs && pos < maxArgs) { ifElse = new CodeConditionStatement { Condition = new CodeBinaryOperatorExpression { Left = new CodePropertyReferenceExpression(argumentsRef, "Length"), Operator = CodeBinaryOperatorType.ValueEquality, Right = new CodePrimitiveExpression(pos) } }; currentBlock.Add(ifElse); currentBlock = ifElse.TrueStatements; } var functionInvoke = new CodeMethodInvokeExpression { Method = new CodeMethodReferenceExpression(moduleRef, fn.Method.Name) }; functionInvoke.Parameters.AddRange(paramRef.ToArray()); CodeExpression returnExpr; if (!fn.ReturnType.IsEmptySequence) { returnExpr = TransformOutput(functionInvoke, fn.ReturnType, processorRef); } else { currentBlock.Add(functionInvoke); returnExpr = new CodePropertyReferenceExpression { PropertyName = "INSTANCE", TargetObject = new CodeTypeReferenceExpression(typeof(EmptyEnumerator)) }; } currentBlock.Add(new CodeMethodReturnStatement(returnExpr)); if (ifElse != null) { currentBlock = ifElse.FalseStatements; } } } return callMethod; }