Exemplo n.º 1
0
 public dependency_inlining()
 {
     theAssembly = new GeneratedAssembly(new GenerationRules("Lamar.Generated"));
     theType     = theAssembly.AddType("GeneratedClass", typeof(Message1Handler));
     theMethod   = theType.MethodFor("Handle");
 }
Exemplo n.º 2
0
 public void CompileWithInlineServices(GeneratedAssembly assembly)
 {
     assembly.CompileAll(ServiceGraph);
 }
Exemplo n.º 3
0
 public string GenerateCodeWithInlineServices(GeneratedAssembly assembly)
 {
     return(assembly.GenerateCode(ServiceGraph));
 }
Exemplo n.º 4
0
 public static void CompileAll(this GeneratedAssembly assembly)
 {
     new AssemblyGenerator().Compile(assembly);
 }
Exemplo n.º 5
0
        public override Type Generate(Type type)
        {
            var entityTypeDetail = TypeAnalyzer.GetTypeDetail(entityType);

            if (!entityTypeDetail.Attributes.Select(x => x.GetType()).Contains(entityAttributeType))
            {
                throw new Exception($"{nameof(TransactStoreEntityAttribute)} {nameof(entityType)} argument {type.Name} does not inherit {entityAttributeType.Name}");
            }

            var typeDetail = TypeAnalyzer.GetTypeDetail(type);

            if (!typeDetail.BaseTypes.Contains(dataContextType))
            {
                throw new Exception($"{nameof(TransactStoreEntityAttribute)} is not placed on a {dataContextType.Name}");
            }

            var baseType = TypeAnalyzer.GetGenericType(providerType, type, entityType);

            var typeSignature = $"{entityType.Name}_{type.Name}_Provider";

            var moduleBuilder = GeneratedAssembly.GetModuleBuilder();
            var typeBuilder   = moduleBuilder.DefineType(typeSignature, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, baseType);

            _ = typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

            var properties = new HashSet <Tuple <PropertyInfo, bool> >();
            var transactProviderTypeDetails = TypeAnalyzer.GetTypeDetail(baseType);

            var methods = transactProviderTypeDetails.MethodDetails.Select(x => x.MethodInfo).ToArray();

            if (eventLinking.HasValue)
            {
                var eventLinkingProperty = transactProviderTypeDetails.GetMember("EventLinking");
                properties.Add(new Tuple <PropertyInfo, bool>((PropertyInfo)eventLinkingProperty.MemberInfo, eventLinking.Value));
            }

            if (queryLinking.HasValue)
            {
                var queryLinkingProperty = transactProviderTypeDetails.GetMember("QueryLinking");
                properties.Add(new Tuple <PropertyInfo, bool>((PropertyInfo)queryLinkingProperty.MemberInfo, queryLinking.Value));
            }

            if (persistLinking.HasValue)
            {
                var eventLinkingProperty = transactProviderTypeDetails.GetMember("PersistLinking");
                properties.Add(new Tuple <PropertyInfo, bool>((PropertyInfo)eventLinkingProperty.MemberInfo, persistLinking.Value));
            }

            foreach (var prop in properties)
            {
                var property = prop.Item1;
                var value    = prop.Item2;

                var propertyBuilder = typeBuilder.DefineProperty(
                    property.Name,
                    PropertyAttributes.HasDefault,
                    property.PropertyType,
                    null
                    );

                var getMethodName    = "get_" + property.Name;
                var getMethod        = methods.FirstOrDefault(x => x.Name == getMethodName);
                var getMethodBuilder = typeBuilder.DefineMethod(
                    getMethodName,
                    MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot | MethodAttributes.Virtual | MethodAttributes.Final,
                    property.PropertyType,
                    Type.EmptyTypes
                    );
                var getMethodBuilderIL = getMethodBuilder.GetILGenerator();
                if (value)
                {
                    getMethodBuilderIL.Emit(OpCodes.Ldc_I4_1);
                }
                else
                {
                    getMethodBuilderIL.Emit(OpCodes.Ldc_I4_0);
                }
                getMethodBuilderIL.Emit(OpCodes.Ret);

                typeBuilder.DefineMethodOverride(getMethodBuilder, getMethod);

                propertyBuilder.SetGetMethod(getMethodBuilder);
            }

            var objectType = typeBuilder.CreateTypeInfo();

            return(objectType);
        }
Exemplo n.º 6
0
        public GeneratedType Build(GeneratedAssembly assembly, DocumentOperations operations)
        {
            var selectorType = _selectorTypeSource(operations);

            return(buildDocumentStorageType(assembly, operations, _typeName, _baseType, selectorType));
        }
Exemplo n.º 7
0
 public Task AddCachedAssembly(string targetAssemblyName, GeneratedAssembly cachedAssembly)
 {
     CodeGeneratorManager.AddGeneratedAssembly(targetAssemblyName, cachedAssembly);
     return(Task.CompletedTask);
 }
        public DocumentProvider <T> Generate <T>()
        {
            var assembly = new GeneratedAssembly(new GenerationRules(SchemaConstants.MartenGeneratedNamespace));

            var operations = new DocumentOperations(assembly, _mapping, _options);

            assembly.Namespaces.Add(typeof(CommandExtensions).Namespace);
            assembly.Namespaces.Add(typeof(Weasel.Core.CommandExtensions).Namespace);
            assembly.Namespaces.Add(typeof(TenantIdArgument).Namespace);
            assembly.Namespaces.Add(typeof(NpgsqlCommand).Namespace);


            var queryOnly = new DocumentStorageBuilder(_mapping, StorageStyle.QueryOnly, x => x.QueryOnlySelector)
                            .Build(assembly, operations);

            var lightweight = new DocumentStorageBuilder(_mapping, StorageStyle.Lightweight, x => x.LightweightSelector)
                              .Build(assembly, operations);

            var identityMap = new DocumentStorageBuilder(_mapping, StorageStyle.IdentityMap, x => x.IdentityMapSelector)
                              .Build(assembly, operations);

            var dirtyTracking = new DocumentStorageBuilder(_mapping, StorageStyle.DirtyTracking, x => x.DirtyCheckingSelector)
                                .Build(assembly, operations);

            var bulkWriterType = new BulkLoaderBuilder(_mapping).BuildType(assembly);

            var compiler = new AssemblyGenerator();

            var types = new[]
            {
                typeof(IDocumentStorage <>),
                typeof(T),
            };

            foreach (var referencedAssembly in WalkReferencedAssemblies.ForTypes(types))
            {
                compiler.ReferenceAssembly(referencedAssembly);
            }

            var providerType = assembly.AddType(ProviderName,
                                                typeof(DocumentProvider <>).MakeGenericType(_mapping.DocumentType));

            providerType.AllInjectedFields.Clear();

            providerType.AllInjectedFields.Add(new InjectedField(typeof(DocumentMapping), "mapping"));

            var bulkWriterArgType = typeof(IBulkLoader <>).MakeGenericType(_mapping.DocumentType);
            var bulkWriterArgs    = $"new {queryOnly.TypeName}(mapping)";

            if (bulkWriterType.AllInjectedFields.Count == 2)
            {
                bulkWriterArgs += ", mapping";
            }

            var bulkWriterCode = $"new {bulkWriterType.TypeName}({bulkWriterArgs})";

            providerType.BaseConstructorArguments[0] = new Variable(bulkWriterArgType, bulkWriterCode);


            providerType.BaseConstructorArguments[1] = new CreateFromDocumentMapping(_mapping, typeof(IDocumentStorage <>), queryOnly);
            providerType.BaseConstructorArguments[2] = new CreateFromDocumentMapping(_mapping, typeof(IDocumentStorage <>), lightweight);
            providerType.BaseConstructorArguments[3] = new CreateFromDocumentMapping(_mapping, typeof(IDocumentStorage <>), identityMap);
            providerType.BaseConstructorArguments[4] = new CreateFromDocumentMapping(_mapping, typeof(IDocumentStorage <>), dirtyTracking);


            try
            {
                compiler.Compile(assembly);
            }
            catch (Exception e)
            {
                if (e.Message.Contains("is inaccessible due to its protection level"))
                {
                    throw new InvalidOperationException($"Requested document type '{_mapping.DocumentType.FullNameInCode()}' must be either scoped as 'public' or the assembly holding it must use the {nameof(InternalsVisibleToAttribute)} pointing to 'Marten.Generated'", e);
                }

                throw;
            }

            return((DocumentProvider <T>)Activator.CreateInstance(providerType.CompiledType, _mapping));
        }
Exemplo n.º 9
0
 /// <summary>
 /// Adds a pre-generated assembly.
 /// </summary>
 /// <param name="targetAssemblyName">
 /// The name of the assembly the provided <paramref name="generatedAssembly"/> targets.
 /// </param>
 /// <param name="generatedAssembly">
 /// The generated assembly.
 /// </param>
 public void AddGeneratedAssembly(string targetAssemblyName, GeneratedAssembly generatedAssembly)
 {
     CompiledAssemblies.TryAdd(targetAssemblyName, new CachedAssembly(generatedAssembly));
 }
Exemplo n.º 10
0
 /// <summary>
 /// Initializes a new instance of the <see cref="GeneratedAssembly"/> class.
 /// </summary>
 /// <param name="other">The other instance.</param>
 public GeneratedAssembly(GeneratedAssembly other)
 {
     this.RawBytes            = other.RawBytes;
     this.DebugSymbolRawBytes = other.DebugSymbolRawBytes;
 }
Exemplo n.º 11
0
 /// <summary>
 /// Initializes a new instance of the <see cref="GeneratedAssembly"/> class.
 /// </summary>
 /// <param name="other">The other instance.</param>
 public GeneratedAssembly(GeneratedAssembly other)
 {
     this.RawBytes = other.RawBytes;
     this.DebugSymbolRawBytes = other.DebugSymbolRawBytes;
 }
        /// <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"></param>
        /// <param name="serviceProvider"></param>
        /// <returns></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 assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (assembly.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, assembly));
                }
            }

            // 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 <IAssemblyGenerator>());
                this._logger.LogInformation("Done compiling {0} pipeline executors", typeToCreationMappings.Count);

                return(new CodeGennedExecutor(
                           serviceProvider,
                           model,
                           assembly,
                           typeToCreationMappings));
            }
        }
        public DocumentProvider <T> Generate <T>()
        {
            var assembly = new GeneratedAssembly(new GenerationRules(SchemaConstants.MartenGeneratedNamespace));

            var operations = new DocumentOperations(assembly, _mapping, _options);

            assembly.Namespaces.Add(typeof(CommandExtensions).Namespace);
            assembly.Namespaces.Add(typeof(TenantIdArgument).Namespace);
            assembly.Namespaces.Add(typeof(NpgsqlCommand).Namespace);


            var queryOnly = new DocumentStorageBuilder(_mapping, StorageStyle.QueryOnly, x => x.QueryOnlySelector)
                            .Build(assembly, operations);

            var lightweight = new DocumentStorageBuilder(_mapping, StorageStyle.Lightweight, x => x.LightweightSelector)
                              .Build(assembly, operations);

            var identityMap = new DocumentStorageBuilder(_mapping, StorageStyle.IdentityMap, x => x.IdentityMapSelector)
                              .Build(assembly, operations);

            var dirtyTracking = new DocumentStorageBuilder(_mapping, StorageStyle.DirtyTracking, x => x.DirtyCheckingSelector)
                                .Build(assembly, operations);

            var bulkWriterType = new BulkLoaderBuilder(_mapping).BuildType(assembly);

            var compiler = new AssemblyGenerator();

            compiler.ReferenceAssembly(typeof(IDocumentStorage <>).Assembly);
            compiler.ReferenceAssembly(typeof(T).Assembly);

            try
            {
                compiler.Compile(assembly);
            }
            catch (Exception e)
            {
                if (e.Message.Contains("is inaccessible due to its protection level"))
                {
                    throw new InvalidOperationException($"Requested document type '{_mapping.DocumentType.FullNameInCode()}' must be either scoped as 'public' or the assembly holding it must use the {nameof(InternalsVisibleToAttribute)} pointing to 'Marten.Generated'", e);
                }

                throw;
            }

            var slot = new DocumentProvider <T>
            {
                QueryOnly     = (IDocumentStorage <T>)Activator.CreateInstance(queryOnly.CompiledType, _mapping),
                Lightweight   = (IDocumentStorage <T>)Activator.CreateInstance(lightweight.CompiledType, _mapping),
                IdentityMap   = (IDocumentStorage <T>)Activator.CreateInstance(identityMap.CompiledType, _mapping),
                DirtyTracking = (IDocumentStorage <T>)Activator.CreateInstance(dirtyTracking.CompiledType, _mapping),

                Operations        = operations,
                QueryOnlyType     = queryOnly,
                LightweightType   = lightweight,
                IdentityMapType   = identityMap,
                DirtyTrackingType = dirtyTracking
            };

            slot.BulkLoader = _mapping.IsHierarchy()
                ? (IBulkLoader <T>)Activator.CreateInstance(bulkWriterType.CompiledType, slot.QueryOnly, _mapping)
                : (IBulkLoader <T>)Activator.CreateInstance(bulkWriterType.CompiledType, slot.QueryOnly);

            slot.BulkLoaderType = bulkWriterType;

            return(slot);
        }
Exemplo n.º 14
0
 /// <summary>Adds a cached assembly to the code generator.</summary>
 /// <param name="targetAssemblyName">The assembly which the cached assembly was generated for.</param>
 /// <param name="cachedAssembly">The generated assembly.</param>
 public void AddCachedAssembly(string targetAssemblyName, GeneratedAssembly cachedAssembly)
 {
     CodeGeneratorManager.AddGeneratedAssembly(targetAssemblyName, cachedAssembly, NullLogger.Instance);
 }
Exemplo n.º 15
0
 public string GenerateCodeWithInlineServices(GeneratedAssembly assembly)
 {
     return(assembly.GenerateCode(new ServiceVariableSource(ServiceGraph)));
 }
Exemplo n.º 16
0
 public CachedAssembly(GeneratedAssembly generated) : base(generated)
 {
 }
Exemplo n.º 17
0
        /// <summary>
        /// Generates and loads code for the specified inputs.
        /// </summary>
        /// <param name="inputs">The assemblies to generate code for.</param>
        public void GenerateAndLoadForAssemblies(params Assembly[] inputs)
        {
            if (inputs == null)
            {
                throw new ArgumentNullException(nameof(inputs));
            }

            var timer = Stopwatch.StartNew();

            foreach (var input in inputs)
            {
                RegisterGeneratedCodeTargets(input);
                TryLoadGeneratedAssemblyFromCache(input);
            }

            var grainAssemblies = inputs.Where(ShouldGenerateCodeForAssembly).ToList();

            if (grainAssemblies.Count == 0)
            {
                // Already up to date.
                return;
            }

            try
            {
                // Generate code for newly loaded assemblies.
                var generatedSyntax = GenerateForAssemblies(grainAssemblies, true);

                var compiled = default(byte[]);
                if (generatedSyntax.Syntax != null)
                {
                    compiled = CompileAndLoad(generatedSyntax);
                }

                foreach (var assembly in generatedSyntax.SourceAssemblies)
                {
                    var generatedAssembly = new GeneratedAssembly {
                        Loaded = true, RawBytes = compiled
                    };
                    CompiledAssemblies.AddOrUpdate(
                        assembly.GetName().FullName,
                        generatedAssembly,
                        (_, __) => generatedAssembly);
                }

                if (Logger.IsVerbose2)
                {
                    Logger.Verbose2(
                        ErrorCode.CodeGenCompilationSucceeded,
                        "Generated code for {0} assemblies in {1}ms",
                        generatedSyntax.SourceAssemblies.Count,
                        timer.ElapsedMilliseconds);
                }
            }
            catch (Exception exception)
            {
                var assemblyNames = string.Join("\n", grainAssemblies.Select(_ => _.GetName().FullName));
                var message       =
                    $"Exception generating code for input assemblies:\n{assemblyNames}\nException: {LogFormatter.PrintException(exception)}";
                Logger.Warn(ErrorCode.CodeGenCompilationFailed, message, exception);
                throw;
            }
        }
 void ICodeFile.AssembleTypes(GeneratedAssembly assembly)
 {
     EventDocumentStorageGenerator.AssembleTypes(Options, assembly);
 }