/// <summary> /// Constructs the invoke delegate using Expressions /// /// Serialize & Deserialize calls are only made when a serializer is provided. /// Context is only passed when customer method has context parameter. /// Lambda return type can be void. /// /// (inStream, context, outStream) => /// { /// var input = serializer.Deserialize(inStream); /// var output = handler(input, context); /// return serializer.Serialize(output); /// } /// /// </summary> /// <param name="customerObject">Wrapped customer object.</param> /// <param name="customerSerializerInstance">Instance of lambda input & output serializer.</param> /// <returns>Action delegate pointing to customer's handler.</returns> public Action <Stream, ILambdaContext, Stream> ConstructInvokeDelegate(object customerObject, object customerSerializerInstance) { var inStreamParameter = Expression.Parameter(Types.StreamType, "inStream"); var outStreamParameter = Expression.Parameter(Types.StreamType, "outStream"); var contextParameter = Expression.Parameter(typeof(ILambdaContext), "context"); _logger.LogDebug($"UCL : Constructing input expression"); var inputExpression = BuildInputExpressionOrNull(customerSerializerInstance, inStreamParameter, out var iLambdaContextType); _logger.LogDebug($"UCL : Constructing context expression"); var contextExpression = BuildContextExpressionOrNull(iLambdaContextType, contextParameter); _logger.LogDebug($"UCL : Constructing handler expression"); var handlerExpression = CreateHandlerCallExpression(customerObject, inputExpression, contextExpression); _logger.LogDebug($"UCL : Constructing output expression"); var outputExpression = CreateOutputExpression(customerSerializerInstance, outStreamParameter, handlerExpression); _logger.LogDebug($"UCL : Constructing final expression"); var finalExpression = Expression.Lambda <Action <Stream, ILambdaContext, Stream> >(outputExpression, inStreamParameter, contextParameter, outStreamParameter); #if DEBUG var finalExpressionDebugView = typeof(Expression) .GetTypeInfo() .GetProperty("DebugView", BindingFlags.Instance | BindingFlags.NonPublic) .GetValue(finalExpression); _logger.LogDebug($"UCL : Constructed final expression:\n'{finalExpressionDebugView}'"); #endif _logger.LogDebug($"UCL : Compiling final expression"); return(finalExpression.Compile()); }
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> /// 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); } }