/// <summary> /// Writes the declaration of a new class to the source writer. /// </summary> /// <param name="writer">Where to write to.</param> /// <param name="className">The name of the class.</param> /// <param name="inheritsOrImplements">The set of <see cref="Type" />s that the class either inherits from, or implements.</param> public static void StartClass(this ISourceWriter writer, string className, params Type[] inheritsOrImplements) { if (inheritsOrImplements.Length == 0) { writer.Block($"public class {className}"); } else { var tempQualifier = inheritsOrImplements.Select(x => x.FullNameInCode()); writer.Block($"public class {className} : {string.Join(", ", tempQualifier)}"); } }
protected void LoopAttributes(IMethodVariables variables, ISourceWriter writer, string methodCall) { var resultsVariable = variables.FindVariable(typeof(ValidationFailures)); var attributeType = typeof(T).FullNameInCode(); var awaitMethod = this.IsAsync ? "await" : string.Empty; writer.Comment($"{this._property.PropertyInfoVariable} == {this._property.PropertyInfoVariable.Property.DeclaringType.Name}.{this._property.PropertyInfoVariable.Property.Name}"); writer.Block($"foreach (var attribute in {this._property.PropertyAttributesVariable})"); writer.Block($"if (attribute is {attributeType} x)"); writer.WriteLine($"var result = {awaitMethod} x.{methodCall};"); writer.Block($"if (result != {Variable.StaticFrom<ValidationResult>(nameof(ValidationResult.Success))})"); writer.WriteLine($"{resultsVariable}.{nameof(ValidationFailures.AddFailure)}(result);"); writer.FinishBlock(); writer.FinishBlock(); writer.FinishBlock(); }
/// <summary> /// Writes "try ([declaration])" into the code and starts a new code /// block with a leading '{' character. /// </summary> /// <param name="writer">Where to write to.</param> /// <param name="inner">The action that writes the body of the try block, passed the same writer to avoid closure allocation.</param> public static void Try( this ISourceWriter writer, Action <ISourceWriter> inner) { writer.Block("try"); inner(writer); writer.FinishBlock(); }
/// <summary> /// Writes "catch ([declaration])" into the code and starts a new code /// block with a leading '{' character. /// </summary> /// <param name="writer">Where to write to.</param> /// <param name="declaration">The code that goes within the parenthesis of this catch block (i.e. at a minimum the exception type).</param> /// <param name="inner">The action that writes the body of the catch block, passed the same writer to avoid closure allocation.</param> public static void Catch( this ISourceWriter writer, string declaration, Action <ISourceWriter> inner) { writer.Block("catch (" + declaration + ")"); inner(writer); writer.FinishBlock(); }
private void WriteConstructorMethod(ISourceWriter writer, HashSet <InjectedField> args) { var tempQualifier = args.Select(x => x.CtorArgDeclaration); var ctorArgs = string.Join(", ", tempQualifier); var declaration = $"public {this.TypeName}({ctorArgs})"; if (this.BaseConstructorArguments.Any()) { var tempQualifier1 = this.BaseConstructorArguments.Select(x => x.ArgumentName); declaration = $"{declaration} : base({string.Join(", ", tempQualifier1)})"; } writer.Block(declaration); foreach (var field in args) { field.WriteAssignment(writer); } writer.FinishBlock(); }
/// <summary> /// Writes the code for this method to the specified <see cref="ISourceWriter" />. /// </summary> /// <remarks> /// <para> /// This method will perform the necessary work to collect variables, both created in this method /// and by external <see cref="IVariableSource"/>s. /// </para> /// <para> /// This method does a lot of bookkeeping to keep track of <see cref="Frame"/>s and <see cref="Variable"/>s that /// are created and used by any added frames. The "top" <see cref="Frame"/> will have it's <see cref="Frame.Generate"/> /// method called twice, which in turn is expected to flow through all frames (using the assigned <see cref="Frame.NextFrame" /> /// property. /// </para> /// <para> /// The <see cref="Frame.Generate" /> method is called twice to allow the first run-through to create the required variables /// and to declare the dependencies. During this process we collect this information and, potentially, add in extra <see cref="Frame"/>s /// as required by the <see cref="IVariableSource"/>s that declare on-the-fly variables. The second time we will already have /// determined the variables and will actually write the code to the supplied <see cref="ISourceWriter" />. /// </para> /// </remarks> /// <param name="writer">The writer to output the code to.</param> public void WriteMethod(ISourceWriter writer) { // 1. Chain all existing frames together (setting their NextFrame property). var topFrame = ChainFrames(this.Frames); // 2. Clear out "all registered frames" to enable this method to be called multiple times. this._allRegisteredFrames.Clear(); // 3. The first time around is used for discovering the variables, ensuring frames // are fully created etc. No actual writing will occur var trackingWriter = new TrackingVariableWriter(this); topFrame.GenerateCode(trackingWriter, this, new MethodSourceWriter(trackingWriter, this, trackingWriter)); // 4. Determine the async mode of this method, which determines the result type and how // the actual return value is generated. Only do this is asyncMode is not set to // something else to allow overriding externally if (this.AsyncMode == AsyncMode.None) { this.AsyncMode = AsyncMode.AsyncTask; if (this._allRegisteredFrames.All(x => !x.IsAsync)) { this.AsyncMode = AsyncMode.None; } else { var lastFrame = this._allRegisteredFrames.Last(); if (this._allRegisteredFrames.Count(x => x.IsAsync) == 1 && lastFrame.IsAsync && lastFrame.CanReturnTask()) { this.AsyncMode = AsyncMode.ReturnFromLastNode; } } } // 5. Now find various types of variables to push to the GeneratedType, in addition to also // adding the creation frames to this method's collection if they do not already exist foreach (var variable in this._variables.Values.TopologicalSort(v => v.Dependencies)) { if (variable.Creator != null && !this._allRegisteredFrames.Contains(variable.Creator)) { // Find the first usage of this variable and place the frame before that. var firstUsage = this._allRegisteredFrames.FirstOrDefault(f => f.Uses.Contains(variable)); if (firstUsage == null) { this.Frames.Insert(0, variable.Creator); // throw new InvalidOperationException( // $"The variable '{variable}' has a creator Frame that has not been appended to this GeneratedMethod, " + // "nor does any Frame exist that Uses it, therefore we cannot determine where to place the creator Frame."); } else { this.Frames.Insert(this.Frames.IndexOf(firstUsage), variable.Creator); } this._allRegisteredFrames.Add(variable.Creator); } switch (variable) { case InjectedField field: this.GeneratedType.AllInjectedFields.Add(field); break; case Setter setter: this.GeneratedType.Setters.Add(setter); break; case StaticField staticField: this.GeneratedType.AllStaticFields.Add(staticField); break; } } // 6. Re-chain all existing frames as we may have pushed new ones topFrame = ChainFrames(this.Frames); // 7. We now have all frames & variables collected, lets do the final generation of code var returnValue = this.DetermineReturnExpression(); if (this.Overrides) { returnValue = "override " + returnValue; } var tempQualifier = this.Arguments.Select(x => x.Declaration); var arguments = string.Join(", ", tempQualifier); writer.Block($"public {returnValue} {this.MethodName}({arguments})"); // 7.1. Clear out "all registered frames" so we do not end up with large duplicated List this._allRegisteredFrames.Clear(); topFrame.GenerateCode(trackingWriter, this, new MethodSourceWriter(trackingWriter, this, writer)); this.WriteReturnStatement(writer); writer.FinishBlock(); }
private void WriteDeclaration(ISourceWriter writer) { writer.Block($"{this.GetDeclaration()}"); }
/// <summary> /// Writes "using ([declaration])" into the code and starts a new code /// block with a leading '{' character. /// </summary> /// <param name="writer">Where to write to.</param> /// <param name="declaration">The code that goes within the parenthesis of this using block (i.e. the expression that generates and optionally sets, the disposable object).</param> /// <param name="inner">The action that writes the body of the using block, passed the same writer to avoid closure allocation.</param> public static void Using(this ISourceWriter writer, string declaration, Action <ISourceWriter> inner) { writer.Block($"using ({declaration})"); inner(writer); writer.FinishBlock(); }
/// <summary> /// Adds a "namespace [@namespace]" declaration into the code, and starts a new /// code block with a leading '{' character. /// </summary> /// <param name="writer">Where to write to.</param> /// <param name="namespace">The namespace.</param> public static void Namespace(this ISourceWriter writer, string @namespace) { writer.Block($"namespace {@namespace}"); }
/// <summary> /// Starts a finally block in code with the opening brace and indention for following lines. /// </summary> /// <param name="writer">Where to write to.</param> public static void Finally(this ISourceWriter writer) { writer.FinishBlock(); writer.Block("finally"); }
/// <summary> /// Starts a try block in code with the opening brace and indention for following lines. /// </summary> /// <param name="writer">Where to write to.</param> public static void Try(this ISourceWriter writer) { writer.Block("try"); }
/// <summary> /// Starts an else block in code with the opening brace and indention for following lines. /// </summary> /// <param name="writer">Where to write to.</param> public static void Else(this ISourceWriter writer) { writer.Block("else"); }
/// <summary> /// Starts an if block in code with the opening brace and indention for following lines. /// </summary> /// <param name="writer">Where to write to.</param> /// <param name="statement">The statement to put inside the if block.</param> public static void If(this ISourceWriter writer, string statement) { writer.Block($"if ({statement})"); }