protected async Task <TemplateFactoryResult> CompileAsync(IGeneratedRazorTemplate razorTemplate)
        {
            CompiledTemplateDescriptor templateDescriptor = await templateCompiler.CompileAsync(razorTemplate);

            templateDescriptor.ExpirationToken = razorTemplate.ProjectItem.ExpirationToken;

            string templateKey = templateDescriptor.TemplateKey;

            if (templateDescriptor.TemplateAttribute != null)
            {
                Type compiledType = templateDescriptor.TemplateAttribute.TemplateType;

                var newExpression              = Expression.New(compiledType);
                var keyProperty                = compiledType.GetTypeInfo().GetProperty(nameof(ITemplatePage.Key));
                var propertyBindExpression     = Expression.Bind(keyProperty, Expression.Constant(templateKey));
                var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression);

                var pageFactory = Expression
                                  .Lambda <Func <ITemplatePage> >(objectInitializeExpression)
                                  .Compile();
                return(new TemplateFactoryResult(templateDescriptor, pageFactory));
            }
            else
            {
                throw new RazorLightException($"Template {templateKey} is corrupted or invalid");
            }
        }
 /// <summary>
 /// Initializes a new instance of <see cref="TemplateFactoryResult"/> with the
 /// specified <see cref="ITemplatePage"/> factory.
 /// </summary>
 /// <param name="templatePageFactory">The <see cref="ITemplatePage"/> factory.</param>
 /// <param name="viewDescriptor">The <see cref="CompiledTemplateDescriptor"/>.</param>
 public TemplateFactoryResult(
     CompiledTemplateDescriptor viewDescriptor,
     Func <ITemplatePage> templatePageFactory)
 {
     TemplateDescriptor  = viewDescriptor ?? throw new ArgumentNullException(nameof(viewDescriptor));
     TemplatePageFactory = templatePageFactory;
 }
        protected CompiledTemplateDescriptor CompileAndEmit(IGeneratedRazorTemplate razorTemplate)
        {
            if (razorTemplate == null)
            {
                throw new ArgumentNullException(nameof(razorTemplate));
            }

            string assemblyName = Path.GetRandomFileName();
            var    compilation  = CreateCompilation(razorTemplate.GeneratedCode, assemblyName);

            using (var assemblyStream = new MemoryStream())
                using (var pdbStream = new MemoryStream())
                {
                    var result = compilation.Emit(
                        assemblyStream,
                        pdbStream,
                        options: EmitOptions);

                    if (!result.Success)
                    {
                        List <Diagnostic> errorsDiagnostics = result.Diagnostics
                                                              .Where(d => d.IsWarningAsError || d.Severity == DiagnosticSeverity.Error)
                                                              .ToList();

                        StringBuilder builder = new StringBuilder();
                        builder.AppendLine("Failed to compile generated Razor template:");

                        var errorMessages = new List <string>();
                        foreach (Diagnostic diagnostic in errorsDiagnostics)
                        {
                            FileLinePositionSpan lineSpan = diagnostic.Location.SourceTree.GetMappedLineSpan(diagnostic.Location.SourceSpan);
                            string errorMessage           = diagnostic.GetMessage();
                            string formattedMessage       = $"- ({lineSpan.StartLinePosition.Line}:{lineSpan.StartLinePosition.Character}) {errorMessage}";

                            errorMessages.Add(formattedMessage);
                            builder.AppendLine(formattedMessage);
                        }

                        builder.AppendLine("\nSee CompilationErrors for detailed information");

                        throw new TemplateCompilationException(builder.ToString(), errorMessages);
                    }

                    assemblyStream.Seek(0, SeekOrigin.Begin);
                    pdbStream.Seek(0, SeekOrigin.Begin);

                    var assembly           = Assembly.Load(assemblyStream.ToArray(), pdbStream.ToArray());
                    var templateDescriptor = new CompiledTemplateDescriptor()
                    {
                        TemplateKey       = razorTemplate.TemplateKey,
                        TemplateAttribute = assembly.GetCustomAttribute <RazorLightTemplateAttribute>(),
                    };

                    return(templateDescriptor);
                }
        }
        public Func <ITemplatePage> CreateFactory(CompiledTemplateDescriptor templateDescriptor)
        {
            string templateKey = templateDescriptor.TemplateKey;

            if (templateDescriptor.TemplateAttribute != null)
            {
                Type compiledType = templateDescriptor.TemplateAttribute.TemplateType;

                var newExpression              = Expression.New(compiledType);
                var keyProperty                = compiledType.GetTypeInfo().GetProperty(nameof(ITemplatePage.Key));
                var propertyBindExpression     = Expression.Bind(keyProperty, Expression.Constant(templateKey));
                var objectInitializeExpression = Expression.MemberInit(newExpression, propertyBindExpression);

                var pageFactory = Expression
                                  .Lambda <Func <ITemplatePage> >(objectInitializeExpression)
                                  .Compile();

                return(pageFactory);
            }
            else
            {
                throw new RazorLightException($"Template {templateKey} is corrupted or invalid");
            }
        }
        private Task <CompiledTemplateDescriptor> OnCacheMissAsync(string templateKey)
        {
            ViewCompilerWorkItem item;
            TaskCompletionSource <CompiledTemplateDescriptor> taskSource;
            MemoryCacheEntryOptions cacheEntryOptions;

            // Safe races cannot be allowed when compiling Razor pages. To ensure only one compilation request succeeds
            // per file, we'll lock the creation of a cache entry. Creating the cache entry should be very quick. The
            // actual work for compiling files happens outside the critical section.
            lock (_cacheLock)
            {
                string normalizedKey = GetNormalizedKey(templateKey);

                // Double-checked locking to handle a possible race.
                if (_cache.TryGetValue(normalizedKey, out Task <CompiledTemplateDescriptor> result))
                {
                    return(result);
                }

                if (_precompiledViews.TryGetValue(normalizedKey, out var precompiledView))
                {
                    item = null;
                    //item = CreatePrecompiledWorkItem(normalizedKey, precompiledView);
                }
                else
                {
                    item = CreateRuntimeCompilationWorkItem(templateKey).GetAwaiter().GetResult();
                }

                // At this point, we've decided what to do - but we should create the cache entry and
                // release the lock first.
                cacheEntryOptions = new MemoryCacheEntryOptions();
                if (item.ExpirationToken != null)
                {
                    cacheEntryOptions.ExpirationTokens.Add(item.ExpirationToken);
                }

                taskSource = new TaskCompletionSource <CompiledTemplateDescriptor>();
                if (item.SupportsCompilation)
                {
                    // We'll compile in just a sec, be patient.
                }
                else
                {
                    // If we can't compile, we should have already created the descriptor
                    Debug.Assert(item.Descriptor != null);
                    taskSource.SetResult(item.Descriptor);
                }

                _cache.Set(item.NormalizedKey, taskSource.Task, cacheEntryOptions);
            }

            // Now the lock has been released so we can do more expensive processing.
            if (item.SupportsCompilation)
            {
                Debug.Assert(taskSource != null);

                //if (item.Descriptor?.Item != null &&
                //	ChecksumValidator.IsItemValid(_projectEngine, item.Descriptor.Item))
                //{
                //	// If the item has checksums to validate, we should also have a precompiled view.
                //	Debug.Assert(item.Descriptor != null);

                //	taskSource.SetResult(item.Descriptor);
                //	return taskSource.Task;
                //}

                try
                {
                    CompiledTemplateDescriptor descriptor = CompileAndEmit(item.ProjectItem);
                    descriptor.ExpirationToken = cacheEntryOptions.ExpirationTokens.FirstOrDefault();
                    taskSource.SetResult(descriptor);
                }
                catch (Exception ex)
                {
                    taskSource.SetException(ex);
                }
            }

            return(taskSource.Task);
        }