/// <summary> /// Generates an expression to deserialize incoming data to customer method input /// </summary> /// <param name="customerSerializerInstance">Instance of lambda input & output serializer.</param> /// <param name="dataType">Customer input type.</param> /// <param name="inStream">Input expression that defines customer input.</param> /// <returns>Expression that deserializes incoming data to customer method input.</returns> /// <exception cref="LambdaValidationException">Thrown when customer serializer doesn't match with expected serializer definition</exception> private Expression CreateDeserializeExpression(object customerSerializerInstance, Type dataType, Expression inStream) { // generic types, null for String and Stream converters Type[] genericTypes = null; var converter = customerSerializerInstance; if (dataType == Types.StreamType) { converter = StreamSerializer.Instance; } else { if (customerSerializerInstance == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.DeserializeMissingAttribute, _handler.AssemblyName, _handler.MethodName, dataType.FullName); } genericTypes = new[] { dataType }; } // code: serializer var serializer = Expression.Constant(converter); // code: serializer.Deserializer[<T>](inStream) Expression deserializeCall = Expression.Call( serializer, // variable "Deserialize", // method name genericTypes, // generic type inStream); // arg1 - input stream return(deserializeCall); }
/// <summary> /// Throws exception if type is not one we can work with /// </summary> /// <param name="type">Container type of customer method.</param> /// <param name="method">Method information of customer method.</param> /// <exception cref="LambdaValidationException">Throws when customer type is not lambda compatible.</exception> internal static void ValidateCustomerType(Type type, MethodInfo method) { var typeInfo = type.GetTypeInfo(); // generic customer type is not supported if (typeInfo.IsGenericType) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerTypeGeneric, type.FullName); } // abstract customer type is not support if customer method is not static if (!method.IsStatic && typeInfo.IsAbstract) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerTypeAbstract, method.ToString(), type.FullName); } var isClass = typeInfo.IsClass; var isStruct = typeInfo.IsValueType && !typeInfo.IsPrimitive && !typeInfo.IsEnum; // customer type must be class or struct if (!isClass && !isStruct) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerNotClassOrStruct, type.FullName); } }
/// <summary> /// Creates an expression to convert incoming Stream to the customer method inputs. /// If customer method takes no inputs or only takes ILambdaContext, return null. /// </summary> /// <param name="customerSerializerInstance">Instance of lambda input & output serializer.</param> /// <param name="inStreamParameter">Input stream parameter.</param> /// <param name="iLambdaContextType">Type of context passed for the invocation.</param> /// <returns>Expression that deserializes incoming stream to the customer method inputs or null if customer method takes no input.</returns> /// <exception cref="LambdaValidationException">Thrown when customer method inputs don't meet lambda requirements.</exception> private Expression BuildInputExpressionOrNull(object customerSerializerInstance, Expression inStreamParameter, out Type iLambdaContextType) { Type inputType = null; iLambdaContextType = null; // get input types var inputTypes = _customerMethodInfo.GetParameters().Select(pi => pi.ParameterType).ToArray(); // check if there are too many parameters if (inputTypes.Length > 2) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.MethodTooManyParams, _customerMethodInfo.Name, _handler.TypeName); } // if two parameters, check that the second input is ILambdaContext if (inputTypes.Length == 2) { if (!Types.IsILambdaContext(inputTypes[1])) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.MethodSecondParamNotContext, _customerMethodInfo.Name, _handler.TypeName, Types.ILambdaContextTypeName); } iLambdaContextType = inputTypes[1]; inputType = inputTypes[0]; } // if one input, check if input is ILambdaContext else if (inputTypes.Length == 1) { if (Types.IsILambdaContext(inputTypes[0])) { iLambdaContextType = inputTypes[0]; } else { inputType = inputTypes[0]; } } if (inputType != null) { // deserializer.Deserialize(inStream) return(CreateDeserializeExpression(customerSerializerInstance, inputType, inStreamParameter)); } if (iLambdaContextType != null) { _logger.LogDebug($"UCL : Validating iLambdaContextType"); UserCodeValidator.ValidateILambdaContextType(iLambdaContextType); } return(null); }
private Exception GetMultipleMethodsValidationException(TypeInfo typeInfo) { var signatureList = typeInfo.GetMethods(Constants.DefaultFlags) .Where(mi => SignatureMatches(_handler.MethodName, mi) || NameMatches(_handler.MethodName, mi)) .Select(mi => mi.ToString()).ToList(); var signatureListText = string.Join("\n", signatureList); throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.MethodHasOverloads, _handler.MethodName, typeInfo.FullName, signatureListText); }
/// <summary> /// Validates object serializer used for serialization and deserialization of input and output /// Throws exception if the specified ILambdaSerializer is a type we can work with /// </summary> /// <param name="type">Type of the customer's serializer.</param> /// <exception cref="LambdaValidationException">Thrown when customer serializer doesn't match with expected serializer definition</exception> internal static void ValidateILambdaSerializerType(Type type) { var typeInfo = type.GetTypeInfo(); var mismatchReason = CheckILambdaSerializerType(typeInfo); if (!string.IsNullOrEmpty(mismatchReason)) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.TypeNotMatchingShape, type.FullName, Types.ILambdaSerializerTypeName, mismatchReason); } }
/// <summary> /// Constructs an instance of the customer-specified serializer /// </summary> /// <param name="serializerAttribute">Serializer attribute used to define the input/output serializer.</param> /// <returns></returns> /// <exception cref="LambdaValidationException">Thrown when serializer doesn't satisfy serializer type requirements.</exception> /// <exception cref="LambdaUserCodeException">Thrown when failed to instantiate serializer type.</exception> private object ConstructCustomSerializer(Attribute serializerAttribute) { var attributeType = serializerAttribute.GetType(); var serializerTypeProperty = attributeType.GetTypeInfo().GetProperty("SerializerType"); if (serializerTypeProperty == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.InvalidClassNoSerializerType, attributeType.FullName); } if (!Types.TypeType.GetTypeInfo().IsAssignableFrom(serializerTypeProperty.PropertyType)) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.InvalidClassSerializerTypeWrongType, attributeType.FullName, Types.TypeType.FullName); } var serializerType = serializerTypeProperty.GetValue(serializerAttribute) as Type; if (serializerType == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.SerializerTypeNotSet, attributeType.FullName); } var serializerTypeInfo = serializerType.GetTypeInfo(); var constructor = serializerTypeInfo.GetConstructor(Type.EmptyTypes); if (constructor == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.SerializerMissingConstructor, serializerType.FullName); } var iLambdaSerializerType = serializerTypeInfo.GetInterface(Types.ILambdaSerializerTypeName); if (iLambdaSerializerType == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.InvalidClassNoILambdaSerializer, serializerType.FullName); } _logger.LogDebug($"UCL : Validating type '{iLambdaSerializerType.FullName}'"); UserCodeValidator.ValidateILambdaSerializerType(iLambdaSerializerType); object customSerializerInstance; customSerializerInstance = constructor.Invoke(null); return(customSerializerInstance); }
private static PropertyInfo ValidateInterfaceProperty(Type type, string propName, string propTypeName) { var propertyInfo = type.GetTypeInfo().GetProperty(propName, Constants.DefaultFlags); if (propertyInfo == null || !string.Equals(propertyInfo.PropertyType.FullName, propTypeName, StringComparison.Ordinal)) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.TypeMissingExpectedProperty, type.FullName, propName, propTypeName); } if (!propertyInfo.CanRead) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.PropertyNotReadable, propName, type.FullName); } return(propertyInfo); }
/// <summary> /// Attempts to find MethodInfo in given type /// Returns null if no matching method was found /// </summary> /// <param name="type">Type that contains customer method.</param> /// <returns>Method information of customer method.</returns> /// <exception cref="LambdaValidationException">Thrown when failed to find customer method in container type.</exception> private MethodInfo FindCustomerMethod(Type type) { // These are split because finding by name is slightly faster // and it's also the more common case. // RuntimeMethodInfo::ToString() always contains a ' ' character. // So one of the two lookup methods would always return null. var customerMethodInfo = FindCustomerMethodByName(type.GetTypeInfo()) ?? FindCustomerMethodBySignature(type.GetTypeInfo()); if (customerMethodInfo == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.NoMatchingMethod, _handler.MethodName, _handler.TypeName, _handler.AssemblyName, _handler.MethodName); } return(customerMethodInfo); }
/// <summary> /// Generates an expression to serialize customer method result into the output stream /// </summary> /// <param name="customerSerializerInstance">Instance of lambda input & output serializer.</param> /// <param name="dataType">Customer input type.</param> /// <param name="customerObject">Expression that define customer object.</param> /// <param name="outStreamParameter">Expression that defines customer output.</param> /// <returns>Expression that serializes returned object to output stream.</returns> /// <exception cref="LambdaValidationException">Thrown when customer input is serializable & serializer instance is null.</exception> private Expression CreateSerializeExpression(object customerSerializerInstance, Type dataType, Expression customerObject, Expression outStreamParameter) { // generic types, null for String and Stream converters Type[] genericTypes = null; var converter = customerSerializerInstance; Type iLambdaSerializerType = null; // subclasses of Stream are allowed as customer method output if (Types.StreamType.GetTypeInfo().IsAssignableFrom(dataType)) { converter = StreamSerializer.Instance; } else { if (customerSerializerInstance == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.SerializeMissingAttribute, _handler.AssemblyName, _handler.MethodName, dataType.FullName); } iLambdaSerializerType = customerSerializerInstance .GetType() .GetTypeInfo() .GetInterface(Types.ILambdaSerializerTypeName); genericTypes = new[] { dataType }; } // code: serializer Expression converterExpression = Expression.Constant(converter); if (iLambdaSerializerType != null) { // code: (ILambdaSerializer)serializer converterExpression = Expression.Convert(converterExpression, iLambdaSerializerType); } // code: [(ILambdaSerializer)]serializer.Serialize[<T>](handler(...), outStream) Expression serializeCall = Expression.Call( converterExpression, // variable "Serialize", // method name genericTypes, // generic type customerObject, // arg1 - customer object outStreamParameter); // arg2 - out stream return(serializeCall); }
/// <summary> /// Validate customer method signature /// Throws exception if method is not one we can work with /// </summary> /// <param name="method">MethodInfo of customer method</param> /// <exception cref="LambdaValidationException">Thrown when customer method doesn't satisfy method requirements</exception> internal static void ValidateCustomerMethod(MethodInfo method) { if (method.IsAbstract) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerMethodAbstract, method.ToString()); } if (method.IsGenericMethod) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerMethodGeneric, method.ToString()); } var asyncAttribute = method.GetCustomAttribute(Types.AsyncStateMachineAttributeType); var isAsync = asyncAttribute != null; var isVoid = method.ReturnType == Types.VoidType; if (isVoid && isAsync) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerMethodAsyncVoid, method.ToString()); } var inputParameters = method.GetParameters(); if (inputParameters.Length > 0) { var lastParameter = inputParameters[inputParameters.Length - 1]; var paramArrayAttribute = lastParameter.GetCustomAttribute(Types.ParamArrayAttributeType); if (paramArrayAttribute != null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerMethodParams, method.ToString()); } } // detect VarArgs methods if ((method.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.HandlerMethodVararg, method.ToString()); } }
internal static Lazy <CognitoClientContextInternal> GetCognitoClientContextInternalLazy(string text) { return(new Lazy <CognitoClientContextInternal>(() => { CognitoClientContextInternal result = null; if (!string.IsNullOrEmpty(text)) { try { return CognitoClientContextInternal.FromJson(text); } catch (Exception innerException) { throw LambdaExceptions.ValidationException(innerException, "Unable to parse client context JSON string '{0}'.", text); } } return result; })); }
internal static void SetCustomerLoggerLogAction(Assembly coreAssembly, Action <string> customerLoggingAction, InternalLogger internalLogger) { if (coreAssembly == null) { throw new ArgumentNullException(nameof(coreAssembly)); } if (customerLoggingAction == null) { throw new ArgumentNullException(nameof(customerLoggingAction)); } internalLogger.LogDebug($"UCL : Retrieving type '{Types.LambdaLoggerTypeName}'"); var lambdaILoggerType = coreAssembly.GetType(Types.LambdaLoggerTypeName); if (lambdaILoggerType == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.Internal.UnableToLocateType, Types.LambdaLoggerTypeName); } internalLogger.LogDebug($"UCL : Retrieving field '{LambdaLoggingActionFieldName}'"); var loggingActionField = lambdaILoggerType.GetTypeInfo().GetField(LambdaLoggingActionFieldName, BindingFlags.NonPublic | BindingFlags.Static); if (loggingActionField == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.Internal.UnableToRetrieveField, LambdaLoggingActionFieldName, Types.LambdaLoggerTypeName); } internalLogger.LogDebug($"UCL : Setting field '{LambdaLoggingActionFieldName}'"); try { loggingActionField.SetValue(null, customerLoggingAction); } catch (Exception e) { throw LambdaExceptions.ValidationException(e, Errors.UserCodeLoader.Internal.UnableToSetField, Types.LambdaLoggerTypeName, LambdaLoggingActionFieldName); } }
/// <summary> /// Constructs an instance of HandlerInfo for a given handler string. /// </summary> /// <param name="handler"></param> public HandlerInfo(string handler) { if (string.IsNullOrEmpty(handler)) { throw LambdaExceptions.ValidationException(Errors.HandlerInfo.EmptyHandler, HandlerSeparator, HandlerSeparator); } var parts = handler.Split(new[] { HandlerSeparator }, 3, StringSplitOptions.None); if (parts.Length != 3) { throw LambdaExceptions.ValidationException(Errors.HandlerInfo.InvalidHandler, handler, HandlerSeparator, HandlerSeparator); } var assemblyName = parts[0].Trim(); if (string.IsNullOrEmpty(assemblyName)) { throw LambdaExceptions.ValidationException(Errors.HandlerInfo.MissingAssembly, handler, HandlerSeparator, HandlerSeparator); } var typeName = parts[1].Trim(); if (string.IsNullOrEmpty(typeName)) { throw LambdaExceptions.ValidationException(Errors.HandlerInfo.MissingType, handler, HandlerSeparator, HandlerSeparator); } var methodName = parts[2].Trim(); if (string.IsNullOrEmpty(methodName)) { throw LambdaExceptions.ValidationException(Errors.HandlerInfo.MissingMethod, handler, HandlerSeparator, HandlerSeparator); } AssemblyName = new AssemblyName(assemblyName); TypeName = typeName; MethodName = methodName; }
/// <summary> /// Validates ILambdaContext properties and methods /// Throws exception if context type is not one we can work with /// This checks the set of members on the first version of ILambdaContext type. /// DO NOT update this code when new members are added to ILambdaContext type, /// it will break existing Lambda deployment packages which still use older version of ILambdaContext. /// </summary> /// <param name="iLambdaContextType">Type of context passed for the invocation.</param> /// <exception cref="LambdaValidationException">Thrown when context doesn't contain required properties & methods.</exception> internal static void ValidateILambdaContextType(Type iLambdaContextType) { if (iLambdaContextType == null) { return; } ValidateInterfaceStringProperty(iLambdaContextType, "AwsRequestId"); ValidateInterfaceStringProperty(iLambdaContextType, "FunctionName"); ValidateInterfaceStringProperty(iLambdaContextType, "FunctionVersion"); ValidateInterfaceStringProperty(iLambdaContextType, "InvokedFunctionArn"); ValidateInterfaceStringProperty(iLambdaContextType, "LogGroupName"); ValidateInterfaceStringProperty(iLambdaContextType, "LogStreamName"); ValidateInterfaceProperty <int>(iLambdaContextType, "MemoryLimitInMB"); ValidateInterfaceProperty <TimeSpan>(iLambdaContextType, "RemainingTime"); var clientContextProperty = ValidateInterfaceProperty(iLambdaContextType, "ClientContext", Types.IClientContextTypeName); var iClientContextType = clientContextProperty.PropertyType; ValidateInterfaceProperty <IDictionary <string, string> >(iClientContextType, "Environment"); ValidateInterfaceProperty <IDictionary <string, string> >(iClientContextType, "Custom"); ValidateInterfaceProperty(iClientContextType, "Client", Types.IClientApplicationTypeName); var identityProperty = ValidateInterfaceProperty(iLambdaContextType, "Identity", Types.ICognitoIdentityTypeName); var iCognitoIdentityType = identityProperty.PropertyType; ValidateInterfaceStringProperty(iCognitoIdentityType, "IdentityId"); ValidateInterfaceStringProperty(iCognitoIdentityType, "IdentityPoolId"); var loggerProperty = ValidateInterfaceProperty(iLambdaContextType, "Logger", Types.ILambdaLoggerTypeName); var iLambdaLoggerType = loggerProperty.PropertyType; var logMethod = iLambdaLoggerType.GetTypeInfo().GetMethod("Log", new[] { Types.StringType }, null); if (logMethod == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.TypeMissingLogMethod, iLambdaLoggerType.FullName); } }
/// <summary> /// Loads customer assembly, type, and method. /// After this call returns without errors, it is possible to invoke /// the customer method through the Invoke method. /// </summary> public void Init(Action <string> customerLoggingAction) { Assembly customerAssembly = null; try { _logger.LogDebug($"UCL : Parsing handler string '{_handlerString}'"); _handler = new HandlerInfo(_handlerString); // Set the logging action private field on the Amazon.Lambda.Core.LambdaLogger type which is part of the // public Amazon.Lambda.Core package when it is loaded. AppDomain.CurrentDomain.AssemblyLoad += (sender, args) => { _logger.LogInformation($"UCL : Loaded assembly {args.LoadedAssembly.FullName} into default ALC."); if (!_customerLoggerSetUpComplete && string.Equals(LambdaCoreAssemblyName, args.LoadedAssembly.GetName().Name, StringComparison.Ordinal)) { _logger.LogDebug( $"UCL : Load context loading '{LambdaCoreAssemblyName}', attempting to set {Types.LambdaLoggerTypeName}.{LambdaLoggingActionFieldName} to logging action."); SetCustomerLoggerLogAction(args.LoadedAssembly, customerLoggingAction, _logger); _customerLoggerSetUpComplete = true; } }; _logger.LogDebug($"UCL : Attempting to load assembly '{_handler.AssemblyName}'"); customerAssembly = AssemblyLoadContext.Default.LoadFromAssemblyName(_handler.AssemblyName); } catch (FileNotFoundException fex) { _logger.LogError(fex, "An error occured on UCL Init"); throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.CouldNotFindHandlerAssembly, fex.FileName); } catch (LambdaValidationException validationException) { _logger.LogError(validationException, "An error occured on UCL Init"); throw; } catch (Exception exception) { _logger.LogError(exception, "An error occured on UCL Init"); throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.UnableToLoadAssembly, _handler.AssemblyName); } _logger.LogDebug($"UCL : Attempting to load type '{_handler.TypeName}'"); var customerType = customerAssembly.GetType(_handler.TypeName); if (customerType == null) { throw LambdaExceptions.ValidationException(Errors.UserCodeLoader.UnableToLoadType, _handler.TypeName, _handler.AssemblyName); } _logger.LogDebug($"UCL : Attempting to find method '{_handler.MethodName}' in type '{_handler.TypeName}'"); CustomerMethodInfo = FindCustomerMethod(customerType); _logger.LogDebug($"UCL : Located method '{CustomerMethodInfo}'"); _logger.LogDebug($"UCL : Validating method '{CustomerMethodInfo}'"); UserCodeValidator.ValidateCustomerMethod(CustomerMethodInfo); var customerObject = GetCustomerObject(customerType); var customerSerializerInstance = GetSerializerObject(customerAssembly); _logger.LogDebug($"UCL : Constructing invoke delegate"); var isPreJit = UserCodeInit.IsCallPreJit(); var builder = new InvokeDelegateBuilder(_logger, _handler, CustomerMethodInfo); _invokeDelegate = builder.ConstructInvokeDelegate(customerObject, customerSerializerInstance, isPreJit); if (isPreJit) { _logger.LogInformation("PreJit: PrepareDelegate"); RuntimeHelpers.PrepareDelegate(_invokeDelegate); } }
/// <summary> /// Checks that the ILambdaSerializer type is correct, returning null if type is as expected /// or a non-null string with the reason if type is not correct. /// </summary> /// <param name="typeInfo">TypeInfo of the customer serializer.</param> /// <returns>Error string if validation fails else null.</returns> private static string CheckILambdaSerializerType(TypeInfo typeInfo) { if (!typeInfo.IsInterface) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_TypeNotInterface, typeInfo.FullName)); } // check that the Deserialize method exists and is generic var deserializeMethodInfo = typeInfo.GetMethod("Deserialize"); if (deserializeMethodInfo == null) { return(Errors.UserCodeLoader.ILambdaSerializerMismatch_DeserializeMethodNotFound); } if (!deserializeMethodInfo.IsGenericMethod) { return(Errors.UserCodeLoader.ILambdaSerializerMismatch_DeserializeMethodNotGeneric); } // verify that Stream is the only input var deserializeInputs = deserializeMethodInfo.GetParameters(); if (deserializeInputs.Length != 1) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_DeserializeMethodHasTooManyParams, deserializeInputs.Length)); } if (deserializeInputs[0].ParameterType != Types.StreamType) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_DeserializeMethodHasWrongParam, deserializeInputs[0].ParameterType.FullName, Types.StreamType.FullName)); } // verify that T is the return type var deserializeOutputType = deserializeMethodInfo.ReturnType; var deserializeGenericArguments = deserializeMethodInfo.GetGenericArguments(); if (deserializeGenericArguments.Length != 1) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_DeserializeMethodHasWrongNumberGenericArgs, deserializeGenericArguments.Length)); } if (deserializeGenericArguments[0] != deserializeOutputType) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_DeserializeMethodHasWrongReturn, deserializeOutputType.FullName)); } // check that the Serialize method exists, is generic, and returns void var serializeMethodInfo = typeInfo.GetMethod("Serialize"); if (serializeMethodInfo == null) { return(Errors.UserCodeLoader.ILambdaSerializerMismatch_SerializeMethodNotFound); } if (!serializeMethodInfo.IsGenericMethod) { return(Errors.UserCodeLoader.ILambdaSerializerMismatch_SerializeMethodNotGeneric); } if (serializeMethodInfo.ReturnType != Types.VoidType) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_SerializeMethodHasWrongReturn, serializeMethodInfo.ReturnType.FullName)); } // verify that T is the first input and Stream is the second input var serializeInputs = serializeMethodInfo.GetParameters(); var serializeGenericArguments = serializeMethodInfo.GetGenericArguments(); if (serializeInputs.Length != 2) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_SerializeMethodHasWrongNumberOfParameters, serializeInputs.Length)); } if (serializeGenericArguments.Length != 1) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_SerializeMethodHasWrongNumberGenericArgs, serializeGenericArguments.Length)); } if (serializeInputs[0].ParameterType != serializeGenericArguments[0]) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_SerializeMethodHasWrongFirstParam, serializeInputs[0].ParameterType.FullName)); } if (serializeInputs[1].ParameterType != Types.StreamType) { return(LambdaExceptions.FormatMessage(Errors.UserCodeLoader.ILambdaSerializerMismatch_SerializeMethodHasWrongSecondParam, serializeInputs[1].ParameterType.FullName, Types.StreamType.FullName)); } // all good! return(null); }