Пример #1
0
        public void write_method()
        {
            // SAMPLE: write-new-method
            // Configures the code generation rules
            // and policies
            var rules = new GenerationRules("GeneratedNamespace");

            // Adds the "now : DateTime" variable rule to
            // our generated code
            rules.Sources.Add(new NowTimeVariableSource());

            // Start the definition for a new generated assembly
            var assembly = new GeneratedAssembly(rules);

            // Add a new generated type called "WhatTimeIsIt" that will
            // implement the
            var type = assembly.AddType("WhatTimeIsIt", typeof(ISaySomething));

            // Getting the definition for the method named "Speak"
            var method = type.MethodFor(nameof(ISaySomething.Speak));

            // Adding a frame that calls the NowSpeaker.Speak() method and
            // adding it to the generated method
            var @call = new MethodCall(typeof(NowSpeaker), nameof(NowSpeaker.Speak));

            method.Frames.Add(@call);

            // Compile the new code!
            assembly.CompileAll();
            // ENDSAMPLE


            // Write the generated code to the console here
            _output.WriteLine(type.SourceCode);
        }
Пример #2
0
        public void generate_dynamic_types_with_no_fields()
        {
            var rules    = new GenerationRules("Lamar.Compilation");
            var assembly = new GeneratedAssembly(rules);



            var adder = assembly.AddType("Adder", typeof(INumberGenerator));

            adder.MethodFor("Generate").Frames.Append <AddFrame>();

            var multiplier = assembly.AddType("Multiplier", typeof(INumberGenerator));

            multiplier.MethodFor(nameof(INumberGenerator.Generate))
            .Frames.Append <MultiplyFrame>();

            assembly.CompileAll();

            Activator.CreateInstance(adder.CompiledType)
            .As <INumberGenerator>()
            .Generate(3, 4).ShouldBe(7);

            Activator.CreateInstance(multiplier.CompiledType)
            .As <INumberGenerator>()
            .Generate(3, 4).ShouldBe(12);

            adder.SourceCode.ShouldContain("public class Adder");
            multiplier.SourceCode.ShouldContain("public class Multiplier");
        }
Пример #3
0
        public void generate_code_with_base_type_that_is_generic()
        {
            var assembly = new GeneratedAssembly(new GenerationRules());
            var type     = assembly.AddType("SomeClass", typeof(ClassWithGenericParameter <string>));

            assembly.CompileAll();
        }
Пример #4
0
        /// <summary>
        /// Generate a new method for the basic base class. The base class "TObject" should
        /// only have a single declared method
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="rules"></param>
        /// <typeparam name="TObject"></typeparam>
        /// <returns></returns>
        public static CodegenResult <TObject> ForBaseOf <TObject>(Action <GeneratedType, GeneratedMethod> configuration, GenerationRules rules = null)
        {
            if (typeof(TObject).GetMethods().Length != 1)
            {
                throw new ArgumentOutOfRangeException(nameof(TObject), "The supplied base type or interface can only have exactly one declared method");
            }

            rules = rules ?? new GenerationRules("LamarCodegenScenario");
            var assembly = new GeneratedAssembly(rules);

            if (typeof(TObject).IsGenericType)
            {
                foreach (var genericTypeArgument in typeof(TObject).GenericTypeArguments)
                {
                    rules.Assemblies.Fill(genericTypeArgument.Assembly);
                }
            }


            var generatedType = assembly.AddType("GeneratedType", typeof(TObject));

            var method = generatedType.Methods.Single();

            configuration(generatedType, method);

            assembly.CompileAll();

            return(new CodegenResult <TObject>(generatedType.CreateInstance <TObject>(), generatedType.SourceCode));
        }
Пример #5
0
        /// <summary>
        /// Generate a new method for the basic base class. The base class "TObject" should
        /// only have a single declared method
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="rules"></param>
        /// <typeparam name="TObject"></typeparam>
        /// <returns></returns>
        private static CodegenResult <TObject> ForBaseOf <TObject>(Action <GeneratedType, GeneratedMethod> configuration)
        {
            if (typeof(TObject).GetMethods().Length != 1)
            {
                throw new ArgumentOutOfRangeException(nameof(TObject), "The supplied base type or interface can only have exactly one declared method");
            }

            var rules    = Builder.Rules();
            var assembly = new GeneratedAssembly(rules);

            var generatedType = assembly.AddType("Tests", "GeneratedType", typeof(TObject));

            var method = generatedType.Methods.Single();

            configuration(generatedType, method);

            var generator = new AssemblyGenerator(new NullLogger <AssemblyGenerator>(), new InMemoryOnlyCompileStrategy(new NullLogger <InMemoryOnlyCompileStrategy>()));

            if (typeof(TObject).IsGenericType)
            {
                foreach (var genericTypeArgument in typeof(TObject).GenericTypeArguments)
                {
                    generator.ReferenceAssembly(genericTypeArgument.Assembly);
                }
            }

            assembly.CompileAll(generator);

            return(new CodegenResult <TObject>(generatedType.CreateInstance <TObject>(), generatedType.SourceCode));
        }
Пример #6
0
        public void generate_code_with_base_type_that_is_generic_using_an_inner_type_as_the_parameter()
        {
            var assembly = new GeneratedAssembly(new GenerationRules());
            var type     = assembly.AddType("SomeClass", typeof(ClassWithGenericParameter <SomeInnerClass>));

            assembly.CompileAll();

            _output.WriteLine(type.SourceCode);
        }
Пример #7
0
        public void can_use_Task_as_the_class_name()
        {
            var assembly = new GeneratedAssembly(new GenerationRules());
            var type     = assembly.AddType("Task", typeof(Thing));
            var method   = type.MethodFor("Do").Frames.Code("// stuff");

            assembly.CompileAll();

            _output.WriteLine(type.SourceCode);
        }
        public void do_not_do_a_stackoverflow_here()
        {
            var assembly = new GeneratedAssembly(new GenerationRules("Jasper.Generated"));
            var type     = assembly.AddType("MyGuy", typeof(IHandler));
            var method   = type.MethodFor("Go");

            method.Frames.Add(new CustomFrame());
            method.Frames.Add(new CustomFrame());
            method.Frames.Add(new CustomFrame());

            assembly.CompileAll();
        }
Пример #9
0
        public void write_footer_into_source_code()
        {
            var assembly = new GeneratedAssembly(new GenerationRules());
            var type     = assembly.AddType("SomeClass", typeof(ClassWithGenericParameter <SomeInnerClass>));

            type.Footer = new OneLineComment("Hey, look at this!");

            assembly.CompileAll();

            type.SourceCode.ReadLines()
            .ShouldContain("    // Hey, look at this!");
            _output.WriteLine(type.SourceCode);
        }
        public async Task can_generate_method()
        {
            var assembly = new GeneratedAssembly(new GenerationRules("Jasper.Generated"));


            var generatedType = assembly.AddType("NumberGetter", typeof(INumberGetter));

            generatedType.MethodFor("GetNumber").Add(new ReturnFive());

            assembly.CompileAll();

            var getter = generatedType.CreateInstance <INumberGetter>();

            var number = await getter.GetNumber();

            number.ShouldBe(5);
        }
Пример #11
0
        public static Task Warmup()
        {
            if (_warmup == null)
            {
                _warmup = Task.Factory.StartNew(() =>
                {
                    var generatedAssembly = new GeneratedAssembly(new GenerationRules("Lamar.Generated"));
                    generatedAssembly.AddType("Tracer", typeof(IStub));


                    generatedAssembly.CompileAll();

                    _warmup = Task.CompletedTask;
                });
            }

            return(_warmup);
        }
Пример #12
0
 public void CompileWithInlineServices(GeneratedAssembly assembly)
 {
     assembly.CompileAll(new ServiceVariableSource(ServiceGraph));
 }
Пример #13
0
 public void CompileWithInlineServices(GeneratedAssembly assembly)
 {
     assembly.CompileAll(ServiceGraph);
 }
Пример #14
0
 public static void CompileAll(this GeneratedAssembly assembly)
 {
     assembly.CompileAll(new AssemblyGenerator(new NullLogger <AssemblyGenerator>(), new InMemoryOnlyCompileStrategy(new NullLogger <InMemoryOnlyCompileStrategy>())));
 }
Пример #15
0
        /// <summary>
        /// Given the specified <see cref="BlueprintApiOptions" /> will generate and compile an
        /// <see cref="IApiOperationExecutor" /> that can be used to execute any operation that
        /// has been identified by the model of the options passed.
        /// </summary>
        /// <param name="options">The configured options.</param>
        /// <param name="serviceProvider">The configured service provider.</param>
        /// <returns>An executor built from the given options and data model.</returns>
        public CodeGennedExecutor Build(BlueprintApiOptions options, IServiceProvider serviceProvider)
        {
            var model = options.Model;

            // We have multiple ways in which we work with generated assemblies, depending on context:
            //
            //  - We are writing unit tests which create many pipelines (within Blueprint itself). Here we
            //    would want to use in-memory compilation and assembly loading only
            //
            //  - We have deployed an app using generated code. We want to use pre-compiled DLLs loaded as
            //    part of the usual loading process. This is done by creating an assembly and PDB that is
            //    deployed with the application and loaded below (see step 1)
            //
            //  - We are in development. Here we wish to generate and load a new DLL on application startup and
            //    store in the temp folder of the machine. This means the DLL is _not_ loaded as normal part
            //    of .NET process and therefore we can (re)create at will on startup without worrying about
            //    the existence of an existing DLL

            // 1. Try and find an already loaded assembly
            foreach (var loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (loadedAssembly.GetName().Name == options.GenerationRules.AssemblyName)
                {
                    // The assembly exists in the current domain, therefore it has either already been generated in this
                    // process OR it has previously been compiled and loaded as part of normal assembly loading (pre-compiled
                    // as part of dotnet publish)

                    this._logger.LogInformation("Assembly {AssemblyName} already exists, using to create executor.", options.GenerationRules.AssemblyName);

                    return(CreateFromAssembly(options, serviceProvider, model, loadedAssembly));
                }
            }

            // 2. Do we have the DLL stored alongside this application but NOT loaded?
            var directory    = Path.GetDirectoryName(typeof(ApiOperationExecutorBuilder).Assembly.Location);
            var assemblyPath = Path.Combine(directory, options.GenerationRules.AssemblyName) + ".dll";

            if (File.Exists(assemblyPath))
            {
                this._logger.LogInformation(
                    "Assembly {AssemblyName} found at {AssemblyLocation}. Loading and using to create executor.",
                    options.GenerationRules.AssemblyName,
                    assemblyPath);

                var loadedPipelineDll = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);

                return(CreateFromAssembly(options, serviceProvider, model, loadedPipelineDll));
            }

            // 2. We DO NOT have any existing DLLs. In that case we are going to generate the source code using our configured
            // middlewares and then hand off to AssemblyGenerator to compile and load the assembly (which may be in-memory, stored
            // to a temp folder or stored to the project output folder)
            this._logger.LogInformation("Building Blueprint API operation executor for {0} operations", options.Model.Operations.Count());

            using var serviceScope = serviceProvider.CreateScope();

            foreach (var middleware in options.MiddlewareBuilders)
            {
                this.Use(middleware);
            }

            var typeToCreationMappings = new Dictionary <Type, Func <Type> >();

            // Start the definition for a new generated assembly
            var assembly = new GeneratedAssembly(options.GenerationRules);

            foreach (var operation in model.Operations)
            {
                this._references.Add(operation.OperationType.Assembly);
            }

            foreach (var a in this._references)
            {
                this._logger.LogDebug("Referencing assembly {0}", a.FullName);

                assembly.ReferenceAssembly(a);
            }

            foreach (var operation in model.Operations)
            {
                this._logger.LogDebug("Generating executor for {0}", operation.OperationType.FullName);

                var typeName = NormaliseTypeName(operation);

                var pipelineExecutorType = assembly.AddType(
                    operation.OperationType.Namespace,
                    typeName,
                    typeof(IOperationExecutorPipeline));

                // We need to set up a LoggerVariable once, to be shared between methods
                pipelineExecutorType.AllInjectedFields.Add(new LoggerVariable(typeName));

                var executeMethod       = pipelineExecutorType.MethodFor(nameof(IOperationExecutorPipeline.ExecuteAsync));
                var executeNestedMethod = pipelineExecutorType.MethodFor(nameof(IOperationExecutorPipeline.ExecuteNestedAsync));

                this.Generate(options, serviceProvider, executeMethod, operation, model, serviceScope, false);
                this.Generate(options, serviceProvider, executeNestedMethod, operation, model, serviceScope, true);

                typeToCreationMappings.Add(
                    operation.OperationType,
                    () => pipelineExecutorType.CompiledType);
            }

            this._logger.LogInformation("Compiling {0} pipeline executors", typeToCreationMappings.Count);
            assembly.CompileAll(serviceProvider.GetRequiredService <ICompileStrategy>());
            this._logger.LogInformation("Done compiling {0} pipeline executors", typeToCreationMappings.Count);

            return(new CodeGennedExecutor(
                       serviceProvider,
                       model,
                       assembly,
                       typeToCreationMappings));
        }