public void Write(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer) { foreach (var frame in this) { frame.GenerateCode(variables, method, writer); } }
/// <summary> /// Generates the code required by this <see cref="Frame"/>, delegating the responsibility to the /// abstract method <see cref="Generate" />. /// </summary> /// <remarks> /// This method will store the parameters and the current <see cref="ISourceWriter.IndentationLevel" /> inside /// of this <see cref="Frame" /> for later referencing. /// </remarks> /// <param name="variables">A source of variable for a method, from which to grab any <see cref="Variable"/>s that this /// frame needs but does not create.</param> /// <param name="method">The method this <see cref="Frame"/> belongs to.</param> /// <param name="writer">Where to write the code to.</param> public void GenerateCode(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer) { this.BlockLevel = writer.IndentationLevel; this._variables = variables; this._method = method; this._writer = writer; method.RegisterFrame(this); this.Generate(new MethodVariableUsageRecorder(variables, this._uses), method, writer, this.Next); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.WriteLine("return Task.FromResult(5);"); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var context = variables.FindVariable(typeof(ApiOperationContext)); var currentSpan = context.GetProperty(nameof(ApiOperationContext.ApmSpan)); writer.WriteLine($"{currentSpan}?.{nameof(IApmSpan.RecordException)}({this._exceptionVariable});"); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var stopwatchVariable = variables.FindVariable(typeof(Stopwatch)); writer.WriteLine($"{stopwatchVariable}.Stop();"); writer.Write( LogFrame.Information( _operationFinishedLogEvent, "Operation {OperationName} finished in {TotalMilliseconds}ms", ReflectionUtilities.PrettyTypeName(this._operationType), stopwatchVariable.GetProperty(nameof(Stopwatch.Elapsed)).GetProperty(nameof(TimeSpan.TotalMilliseconds)))); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var parameters = this.Ctor.GetParameters(); for (var i = 0; i < parameters.Length; i++) { if (this.Parameters[i] == null) { var parameter = parameters[i]; this.Parameters[i] = variables.FindVariable(parameter.ParameterType); } } foreach (var setter in this.Setters) { setter.FindVariable(variables); } switch (this.Mode) { case ConstructorCallMode.Variable: writer.WriteLine(this.Declaration() + ";"); this.ActivatorFrames.Write(variables, method, writer); next(); break; case ConstructorCallMode.ReturnValue: if (this.ActivatorFrames.Any()) { writer.WriteLine(this.Declaration() + ";"); this.ActivatorFrames.Write(variables, method, writer); writer.WriteLine($"return {this.Variable};"); next(); } else { writer.WriteLine($"return {this.Invocation()};"); next(); } break; case ConstructorCallMode.UsingNestedVariable: writer.Using(this.Declaration(), w => { this.ActivatorFrames.Write(variables, method, writer); next(); }); break; } }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { if (this._inner.Count > 1) { for (var i = 1; i < this._inner.Count; i++) { this._inner[i - 1].NextFrame = this._inner[i]; } } this.GenerateCode(variables, method, writer, this._inner[0]); next(); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { // Note that we cannot just get a variable of type IApmSpan as _we_ create one, which causes a loop and failed compilation var context = variables.FindVariable(typeof(ApiOperationContext)); var currentSpan = context.GetProperty(nameof(ApiOperationContext.ApmSpan)); writer.WriteLine($"using var {this._spanVariable} = {currentSpan}.{nameof(IApmSpan.StartSpan)}(\"{this._spanKind}\", \"{this._operationName}\", \"{this._type}\");"); next(); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var serviceProviderVariable = variables.FindVariable(typeof(IServiceProvider)); // We import this namespace to allow using extension method invocation, which cleans up the // generated code quite a bit for this use case. method.GeneratedType.Namespaces.Add(typeof(ServiceProviderServiceExtensions).Namespace); writer.WriteLine( $"var {this.InstanceVariable} = {serviceProviderVariable}.{nameof(ServiceProviderServiceExtensions.GetRequiredService)}<{this._constructedType.FullNameInCode()}>();"); next(); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.WriteLine(this.ToString()); next(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var number = variables.FindVariable(typeof(int)); writer.WriteLine($"return {number.Usage} + 2;"); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var context = variables.FindVariable(typeof(ApiOperationContext)); var activityVariable = context.GetProperty(nameof(ApiOperationContext.Activity)); writer.If($"{this._exceptionVariable} is {typeof(ApiException).FullNameInCode()} apiException && apiException.{nameof(ApiException.HttpStatus)} < 500"); writer.Write(LogFrame.Information(this._exceptionVariable, "A non-critical ApiException has occurred with message {ExceptionMessage}", this._exceptionVariable.GetProperty(nameof(Exception.Message)))); writer.Write(new ActivityStatusFrame(activityVariable, Status.Ok)); writer.FinishBlock(); writer.Else(); writer.WriteLine($"{typeof(BlueprintActivitySource).FullNameInCode()}.{nameof(BlueprintActivitySource.RecordException)}({activityVariable}, {this._exceptionVariable}, {this._escaped.ToString().ToLowerInvariant()});"); writer.Write(LogFrame.Error(this._exceptionVariable, "An unhandled exception has occurred with message {ExceptionMessage}", this._exceptionVariable.GetProperty(nameof(Exception.Message)))); writer.Write(new ActivityStatusFrame(activityVariable, Status.Error)); writer.FinishBlock(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.Comment(nameof(SourceFrame)); next(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var handlerFromSource = variables.FindVariable(typeof(IHandler)); writer.Comment(nameof(CustomFrame)); next(); }
/// <inheritdoc/> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { if (this._predicate(variables, method)) { foreach (var f in this._childFrames) { writer.Write(f); } } next(); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.If($"{this._activityVariable} != null"); writer.WriteLine($"{typeof(ActivityExtensions).FullNameInCode()}.{nameof(ActivityExtensions.SetStatus)}({this._activityVariable}, {this._statusVariableVariable});"); writer.FinishBlock(); next(); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.BlankLine(); next(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.WriteLine( $"var {this._operationVariable} = ({this._operationVariable.VariableType.FullNameInCode()}) {this._operationContextVariable.GetProperty(nameof(ApiOperationContext.Operation))};"); next(); }
/// <inheritdoc /> protected override void GenerateCode(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Frame inner) { writer.Block($"if ({this._condition})"); inner.GenerateCode(variables, method, writer); writer.FinishBlock(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { if (this.ReturnedVariable == null && this._returnType != null) { this.ReturnedVariable = variables.TryFindVariable(this._returnType); } if (this.ReturnedVariable == null) { writer.WriteLine("return;"); } else { var variableIsTask = this.ReturnedVariable.VariableType.IsGenericType && this.ReturnedVariable.VariableType.GetGenericTypeDefinition() == typeof(Task <>); var methodReturnsTask = method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task <>); // This method does not use async/await but _does_ return Task, but the variable to return is _not_ a Task<>, therefore we // need to use Task.FromResult to get the correct return type if (method.AsyncMode == AsyncMode.None && methodReturnsTask && !variableIsTask) { // What type are we expecting to return? var taskValueType = method.ReturnType.GenericTypeArguments[0]; writer.WriteLine( $"return {typeof(Task).FullNameInCode()}.{nameof(Task.FromResult)}(({taskValueType.FullNameInCode()}){this.ReturnedVariable});"); } else { writer.WriteLine($"return {this.ReturnedVariable};"); } } }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.WriteLine($"{this._spanVariable}.{nameof(IApmSpan.Dispose)}();"); next(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var operationResultName = typeof(OperationResult).FullNameInCode(); if (typeof(OperationResult).IsAssignableFrom(this._resultVariable.VariableType)) { // If the declared type is already a type of OperationResult we can eliminate the ternary operator check and // immediately assign to the operationResult variable writer.WriteLine($"{operationResultName} {this._operationResultVariable} = {this._resultVariable};"); } else if (this._resultVariable.VariableType.IsAssignableFrom(typeof(OperationResult))) { // If the variable type _could_ be an OperationResult then we use a ternary operator to check whether it // actually is, and either use it directly or wrap in an OkResult var okResultName = typeof(OkResult).FullNameInCode(); writer.WriteLine($"{operationResultName} {this._operationResultVariable} = {this._resultVariable} is {operationResultName} r ? " + "r : " + $"new {okResultName}({this._resultVariable});"); } else { // The type is NOT related to OperationResult at all, so we always create a wrapping OkResult var okResultName = typeof(OkResult).FullNameInCode(); writer.WriteLine($"{operationResultName} {this._operationResultVariable} = new {okResultName}({this._resultVariable});"); } next(); }
protected abstract void GenerateCode(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Frame inner);
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var apiOperationContextVariable = variables.FindVariable(typeof(ApiOperationContext)); var spanVariable = apiOperationContextVariable.GetProperty(nameof(ApiOperationContext.ApmSpan)); // It is possible that no span exists writer.If($"{spanVariable} != null"); // We set user data as tags. Individual APM tools may wish to react accordingly to these tags if there is some specific // location user details need to be stored. writer.WriteLine($"var userContext = {apiOperationContextVariable}.{nameof(ApiOperationContext.UserAuthorisationContext)};"); writer.If($"userContext != null && userContext.{nameof(IUserAuthorisationContext.IsAnonymous)} == false"); writer.WriteLine($"{spanVariable}.SetTag(\"user.id\", userContext.{nameof(IUserAuthorisationContext.Id)});"); writer.WriteLine($"{spanVariable}.SetTag(\"user.account_id\", userContext.{nameof(IUserAuthorisationContext.AccountId)});"); writer.FinishBlock(); writer.FinishBlock(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.WriteLine($"var {this._stopwatchVariable} = {typeof(Stopwatch).FullNameInCode()}.{nameof(Stopwatch.StartNew)}();"); next(); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { writer.Block("try"); next(); writer.FinishBlock(); foreach (var handler in this._context.ExceptionHandlers.OrderBy(k => k.Key, new CatchClauseComparer())) { writer.Block($"catch ({handler.Key.FullNameInCode()} e)"); // Key == exception type being caught var exceptionVariable = new Variable(handler.Key, "e"); var allFrames = handler.Value.SelectMany(v => v(exceptionVariable)).ToList(); foreach (var frame in allFrames) { frame.GenerateCode(variables, method, writer); } writer.FinishBlock(); } if (this._context.FinallyFrames.Any()) { writer.Block("finally"); foreach (var frame in this._context.FinallyFrames) { frame.GenerateCode(variables, method, writer); } writer.FinishBlock(); } }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var one = variables.FindVariable(typeof(int)); writer.WriteLine($"return {one} * {one};"); }
/// <inheritdoc /> protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var apiOperationContextVariable = variables.FindVariable(typeof(ApiOperationContext)); var activityVariable = apiOperationContextVariable.GetProperty(nameof(ApiOperationContext.Activity)); var operationTypeKey = ReflectionUtilities.PrettyTypeName(this._context.Descriptor.OperationType); writer.BlankLine(); // 2. For every property of the operation output a value to the exception.Data dictionary. All properties that are // not considered sensitive foreach (var prop in this._context.Descriptor.Properties) { if (SensitiveProperties.IsSensitive(prop)) { continue; } // Only support, for now, primitive values, strings and GUIDs to avoid pushing complex types as tags if (!prop.PropertyType.IsPrimitive && !prop.PropertyType.IsEnum && prop.PropertyType.GetNonNullableType() != typeof(string) && prop.PropertyType.GetNonNullableType() != typeof(Guid)) { continue; } writer.WriteLine( $"{activityVariable}?.{nameof(Activity.SetTag)}(\"{operationTypeKey}.{prop.Name}\", {variables.FindVariable(this._context.Descriptor.OperationType)}.{prop.Name});"); } next(); }
protected override void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next) { var contextVariable = variables.FindVariable(typeof(ApiOperationContext)); this.LoopAttributes( variables, writer, $"{nameof(BlueprintValidationAttribute.GetValidationResultAsync)}({this.Property.PropertyValueVariable}, \"{this.Property.PropertyInfoVariable.Property.Name}\", {contextVariable})"); next(); }
/// <summary> /// Implements the actual generation of the code for this <see cref="Frame" />, writing to /// the given <see cref="ISourceWriter" />. /// </summary> /// <remarks> /// <para> /// The <see cref="GeneratedMethod" /> given is the same as <see cref="_method" />, but is provided as /// a convenience parameter to make it more obvious it's available. /// </para> /// <para> /// The <see cref="ISourceWriter" /> given is the same as <see cref="_writer" />, but is provided as /// a convenience parameter to make it more obvious it's available. /// </para> /// <para> /// Frames <em>should</em> typically call <paramref name="next" /> to insert code from the next frame. If they /// do not then no further code is executed and this <see cref="Frame" /> therefore becomes the last frame /// of the method. /// </para> /// </remarks> /// <param name="variables">A source of variables, used to grab from other frames / <see cref="IVariableSource"/>s the /// variables needed to generate the code.</param> /// <param name="method">The method to which this <see cref="Frame" /> belongs.</param> /// <param name="writer">The writer to write code to.</param> /// <param name="next">The action to call to write the next frame (equivalent to calling <see cref="Next"/> directly).</param> protected abstract void Generate(IMethodVariables variables, GeneratedMethod method, IMethodSourceWriter writer, Action next);