Skip to content

Commit

Permalink
Merge pull request #1771 from riganti/property-compilation-improvements
Browse files Browse the repository at this point in the history
Property directive compilation extensibility improvements
  • Loading branch information
exyi authored Feb 28, 2024
2 parents d73e409 + 6117fc9 commit 88ffbf6
Show file tree
Hide file tree
Showing 18 changed files with 193 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace DotVVM.Framework.Compilation.ControlTree
{
public interface IAbstractTreeBuilder
{
IAbstractTreeRoot BuildTreeRoot(IControlTreeResolver controlTreeResolver, IControlResolverMetadata metadata, DothtmlRootNode node, IDataContextStack dataContext, IReadOnlyDictionary<string, IReadOnlyList<IAbstractDirective>> directives, IAbstractControlBuilderDescriptor? masterPage);
IAbstractTreeRoot BuildTreeRoot(IControlTreeResolver controlTreeResolver, IControlResolverMetadata metadata, DothtmlRootNode node, IDataContextStack dataContext, ImmutableDictionary<string, ImmutableList<IAbstractDirective>> directives, IAbstractControlBuilderDescriptor? masterPage);

IAbstractControl BuildControl(IControlResolverMetadata metadata, DothtmlNode? node, IDataContextStack dataContext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public ResolvedTreeBuilder(BindingCompilationService bindingService, DirectiveCo
this.directiveService = directiveService;
}

public IAbstractTreeRoot BuildTreeRoot(IControlTreeResolver controlTreeResolver, IControlResolverMetadata metadata, DothtmlRootNode node, IDataContextStack dataContext, IReadOnlyDictionary<string, IReadOnlyList<IAbstractDirective>> directives, IAbstractControlBuilderDescriptor? masterPage)
public IAbstractTreeRoot BuildTreeRoot(IControlTreeResolver controlTreeResolver, IControlResolverMetadata metadata, DothtmlRootNode node, IDataContextStack dataContext, ImmutableDictionary<string, ImmutableList<IAbstractDirective>> directives, IAbstractControlBuilderDescriptor? masterPage)
{
return new ResolvedTreeRoot((ControlResolverMetadata)metadata, node, (DataContextStack)dataContext, directives, (ControlBuilderDescriptor?)masterPage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ from d in ds.Value
return null;
}

public ResolvedTreeRoot(ControlResolverMetadata metadata, DothtmlNode node, DataContextStack dataContext, IReadOnlyDictionary<string, IReadOnlyList<IAbstractDirective>> directives, ControlBuilderDescriptor? masterPage)
public ResolvedTreeRoot(ControlResolverMetadata metadata, DothtmlNode node, DataContextStack dataContext, ImmutableDictionary<string, ImmutableList<IAbstractDirective>> directives, ControlBuilderDescriptor? masterPage)
: base(metadata, node, null, dataContext)
{
this.MasterPage = masterPage;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
using System;
using System.Collections.Generic;
using DotVVM.Framework.Compilation.Parser.Dothtml.Parser;
using DotVVM.Framework.Compilation.ControlTree;
using DotVVM.Framework.Compilation.Parser;
using DotVVM.Framework.Compilation.ControlTree.Resolved;
using DotVVM.Framework.Controls;
using System.Reflection;
using System.Reflection.Emit;
using DotVVM.Framework.Controls.Infrastructure;
using DotVVM.Framework.Utils;
using System.Linq;
using System.Collections.Immutable;
using System.Security.Cryptography;
using System.Text;

namespace DotVVM.Framework.Compilation.Directives
{
public abstract class BaseTypeDirectiveCompiler : DirectiveCompiler<IAbstractBaseTypeDirective, ITypeDescriptor>
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public class BaseTypeDirectiveCompiler : DirectiveCompiler<IAbstractBaseTypeDirective, ITypeDescriptor>
{
private readonly string fileName;
private readonly ImmutableList<NamespaceImport> imports;
Expand All @@ -27,7 +23,7 @@ public abstract class BaseTypeDirectiveCompiler : DirectiveCompiler<IAbstractBas
protected virtual ITypeDescriptor DotvvmMarkupControlType => new ResolvedTypeDescriptor(typeof(DotvvmMarkupControl));

public BaseTypeDirectiveCompiler(
IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName, IAbstractTreeBuilder treeBuilder, string fileName, ImmutableList<NamespaceImport> imports)
DirectiveDictionary directiveNodesByName, IAbstractTreeBuilder treeBuilder, string fileName, ImmutableList<NamespaceImport> imports)
: base(directiveNodesByName, treeBuilder)
{
this.fileName = fileName;
Expand All @@ -37,7 +33,7 @@ public BaseTypeDirectiveCompiler(
protected override IAbstractBaseTypeDirective Resolve(DothtmlDirectiveNode directiveNode)
=> TreeBuilder.BuildBaseTypeDirective(directiveNode, ParseDirective(directiveNode, p => p.ReadDirectiveTypeName()), imports);

protected override ITypeDescriptor CreateArtefact(IReadOnlyList<IAbstractBaseTypeDirective> resolvedDirectives)
protected override ITypeDescriptor CreateArtefact(ImmutableList<IAbstractBaseTypeDirective> resolvedDirectives)
{
var wrapperType = GetDefaultWrapperType();

Expand Down Expand Up @@ -68,41 +64,9 @@ protected override ITypeDescriptor CreateArtefact(IReadOnlyList<IAbstractBaseTyp
}
}

if (DirectiveNodesByName.TryGetValue(ParserConstants.PropertyDeclarationDirective, out var propertyDirectives) && propertyDirectives.Any())
{
wrapperType = CreateDynamicDeclaringType(wrapperType, propertyDirectives) ?? wrapperType;
}

return wrapperType;
}

/// <summary> Gets or creates dynamic declaring type, and registers on it the properties declared using `@property` directives </summary>
protected virtual ITypeDescriptor? CreateDynamicDeclaringType(
ITypeDescriptor? originalWrapperType,
IEnumerable<DothtmlDirectiveNode> propertyDirectives
)
{
var imports = DirectiveNodesByName.GetValueOrDefault(ParserConstants.ImportNamespaceDirective, Array.Empty<DothtmlDirectiveNode>())
.Select(d => d.Value.Trim()).OrderBy(s => s).ToImmutableArray();
var properties = propertyDirectives
.Select(p => p.Value.Trim()).OrderBy(s => s).ToImmutableArray();
var baseType = originalWrapperType ?? DotvvmMarkupControlType;

using var sha = SHA256.Create();
var hashBytes = sha.ComputeHash(
new UTF8Encoding(false).GetBytes(
baseType.FullName + "||" + string.Join("|", imports) + "||" + string.Join("|", properties)
)
);
var hash = Convert.ToBase64String(hashBytes, 0, 16);

var typeName = "DotvvmMarkupControl-" + hash;

return GetOrCreateDynamicType(baseType, typeName);
}

protected abstract ITypeDescriptor? GetOrCreateDynamicType(ITypeDescriptor baseType, string typeName);

/// <summary>
/// Gets the default type of the wrapper for the view.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Immutable;
using DotVVM.Framework.Compilation.Parser.Dothtml.Parser;
using DotVVM.Framework.Compilation.ControlTree;

namespace DotVVM.Framework.Compilation.Directives
{
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public class DefaultDirectiveResolver : DirectiveResolver<IAbstractDirective>
{
private readonly IAbstractTreeBuilder treeBuilder;

public DefaultDirectiveResolver(IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName, IAbstractTreeBuilder treeBuilder)
public DefaultDirectiveResolver(DirectiveDictionary directiveNodesByName, IAbstractTreeBuilder treeBuilder)
: base(directiveNodesByName)
{
this.treeBuilder = treeBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using DotVVM.Framework.Compilation.Parser.Dothtml.Parser;
using DotVVM.Framework.Compilation.Parser.Binding.Tokenizer;
Expand All @@ -8,13 +7,15 @@

namespace DotVVM.Framework.Compilation.Directives
{
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public abstract class DirectiveCompiler<TDirective, TArtefact> : DirectiveResolver<TDirective>
where TDirective : IAbstractDirective
{
public abstract string DirectiveName { get; }
protected IAbstractTreeBuilder TreeBuilder { get; }

public DirectiveCompiler(IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName, IAbstractTreeBuilder treeBuilder)
public DirectiveCompiler(DirectiveDictionary directiveNodesByName, IAbstractTreeBuilder treeBuilder)
: base(directiveNodesByName)
{
TreeBuilder = treeBuilder;
Expand All @@ -29,7 +30,7 @@ public DirectiveCompilationResult Compile()
);
}

protected abstract TArtefact CreateArtefact(IReadOnlyList<TDirective> resolvedDirectives);
protected abstract TArtefact CreateArtefact(ImmutableList<TDirective> resolvedDirectives);

protected BindingParserNode ParseDirective(DothtmlDirectiveNode directiveNode, Func<BindingParser, BindingParserNode> parserFunc)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace DotVVM.Framework.Compilation.Directives
{
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public abstract class DirectiveResolver<TDirective>
where TDirective : IAbstractDirective
{
Expand All @@ -19,9 +21,9 @@ public abstract class DirectiveResolver<TDirective>
ParserConstants.ViewModelDirectiveName
};

protected IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> DirectiveNodesByName { get; }
protected DirectiveDictionary DirectiveNodesByName { get; }

public DirectiveResolver(IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName)
public DirectiveResolver(DirectiveDictionary directiveNodesByName)
{
DirectiveNodesByName = directiveNodesByName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Immutable;
using System.Linq;
using DotVVM.Framework.Compilation.Parser;
using DotVVM.Framework.Compilation.Parser.Dothtml.Parser;
Expand All @@ -9,11 +8,13 @@

namespace DotVVM.Framework.Compilation.Directives
{
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public class ImportDirectiveCompiler : DirectiveCompiler<IAbstractImportDirective, ImmutableList<NamespaceImport>>
{
public override string DirectiveName => ParserConstants.ImportNamespaceDirective;

public ImportDirectiveCompiler(IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName, IAbstractTreeBuilder treeBuilder)
public ImportDirectiveCompiler(DirectiveDictionary directiveNodesByName, IAbstractTreeBuilder treeBuilder)
: base(directiveNodesByName, treeBuilder)
{
}
Expand All @@ -37,13 +38,11 @@ protected override IAbstractImportDirective Resolve(DothtmlDirectiveNode directi
return TreeBuilder.BuildImportDirective(directiveNode, alias, name);
}

protected override ImmutableList<NamespaceImport> CreateArtefact(IReadOnlyList<IAbstractImportDirective> directives)
=> ResolveNamespaceImportsCore(directives).ToImmutableList();

private IEnumerable<NamespaceImport> ResolveNamespaceImportsCore(IReadOnlyList<IAbstractImportDirective> directives)
protected override ImmutableList<NamespaceImport> CreateArtefact(ImmutableList<IAbstractImportDirective> directives)
=> directives
.Where(d => !d.HasError)
.Select(d => new NamespaceImport(d.NameSyntax.ToDisplayString(), d.AliasSyntax.As<IdentifierNameBindingParserNode>()?.Name));
.Select(d => new NamespaceImport(d.NameSyntax.ToDisplayString(), d.AliasSyntax.As<IdentifierNameBindingParserNode>()?.Name))
.ToImmutableList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
using DotVVM.Framework.Compilation.ControlTree.Resolved;
using DotVVM.Framework.Controls.Infrastructure;
using DotVVM.Framework.ResourceManagement;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;
using DirectiveDictionary = System.Collections.Generic.Dictionary<string, System.Collections.Generic.IReadOnlyList<DotVVM.Framework.Compilation.Parser.Dothtml.Parser.DothtmlDirectiveNode>>;
using DotVVM.Framework.Compilation.Parser.Dothtml.Parser;

namespace DotVVM.Framework.Compilation.Directives
{
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public class MarkupDirectiveCompilerPipeline : MarkupDirectiveCompilerPipelineBase
{
private readonly IAbstractTreeBuilder treeBuilder;
Expand All @@ -34,7 +34,7 @@ protected override ViewModuleDirectiveCompiler CreateViewModuleDirectiveCompiler
resourceRepository);

protected override BaseTypeDirectiveCompiler CreateBaseTypeCompiler(string fileName, DirectiveDictionary directivesByName, ImmutableList<NamespaceImport> imports)
=> new ResolvedBaseTypeDirectiveCompiler(directivesByName, treeBuilder, fileName, imports);
=> new BaseTypeDirectiveCompiler (directivesByName, treeBuilder, fileName, imports);
protected override ServiceDirectiveCompiler CreateServiceCompiler(DirectiveDictionary directivesByName, ImmutableList<NamespaceImport> imports)
=> new(directivesByName, treeBuilder, imports);
protected override MasterPageDirectiveCompiler CreateMasterPageDirectiveCompiler(DirectiveDictionary directivesByName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;
using DirectiveDictionary = System.Collections.Generic.Dictionary<string, System.Collections.Generic.IReadOnlyList<DotVVM.Framework.Compilation.Parser.Dothtml.Parser.DothtmlDirectiveNode>>;

namespace DotVVM.Framework.Compilation.Directives
{
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public abstract class MarkupDirectiveCompilerPipelineBase : IMarkupDirectiveCompilerPipeline
{
public MarkupPageMetadata Compile(DothtmlRootNode dothtmlRoot, string fileName)
{
var directivesByName = dothtmlRoot.Directives
.GroupBy(d => d.Name, StringComparer.OrdinalIgnoreCase)
.ToDictionary(d => d.Key, d => (IReadOnlyList<DothtmlDirectiveNode>)d.ToList(), StringComparer.OrdinalIgnoreCase);
.ToImmutableDictionary(d => d.Key, d => d.ToImmutableList(), StringComparer.OrdinalIgnoreCase);

var resolvedDirectives = new Dictionary<string, IReadOnlyList<IAbstractDirective>>();
var resolvedDirectives = new Dictionary<string, ImmutableList<IAbstractDirective>>();

var importCompiler = CreateImportCompiler(directivesByName);
var importResult = importCompiler.Compile();
Expand All @@ -43,14 +44,14 @@ public MarkupPageMetadata Compile(DothtmlRootNode dothtmlRoot, string fileName)
var baseType = baseTypeResult.Artefact;
resolvedDirectives.AddIfAny(baseTypeCompiler.DirectiveName, baseTypeResult.Directives);

var viewModuleDirectiveCompiler = CreateViewModuleDirectiveCompiler(directivesByName, baseType);
var viewModuleResult = viewModuleDirectiveCompiler.Compile();
resolvedDirectives.AddIfAny(viewModuleDirectiveCompiler.DirectiveName, viewModuleResult.Directives);

var propertyDirectiveCompiler = CreatePropertyDirectiveCompiler(directivesByName, imports, baseType);
var propertyResult = propertyDirectiveCompiler.Compile();
resolvedDirectives.AddIfAny(propertyDirectiveCompiler.DirectiveName, propertyResult.Directives);

var viewModuleDirectiveCompiler = CreateViewModuleDirectiveCompiler(directivesByName, propertyResult.Artefact.ModifiedMarkupControlType);
var viewModuleResult = viewModuleDirectiveCompiler.Compile();
resolvedDirectives.AddIfAny(viewModuleDirectiveCompiler.DirectiveName, viewModuleResult.Directives);

var defaultResolver = CreateDefaultResolver(directivesByName);

foreach (var directiveGroup in directivesByName)
Expand All @@ -62,14 +63,14 @@ public MarkupPageMetadata Compile(DothtmlRootNode dothtmlRoot, string fileName)
}

return new MarkupPageMetadata(
resolvedDirectives,
resolvedDirectives.ToImmutableDictionary(),
imports,
masterPageDirectiveResult.Artefact,
injectedServicesResult.Artefact,
baseType,
propertyResult.Artefact.ModifiedMarkupControlType,
viewModelType.TypeDescriptor,
viewModuleResult.Artefact,
propertyResult.Artefact);
propertyResult.Artefact.Properties);
}

protected abstract DefaultDirectiveResolver CreateDefaultResolver(DirectiveDictionary directivesByName);
Expand All @@ -84,11 +85,11 @@ public MarkupPageMetadata Compile(DothtmlRootNode dothtmlRoot, string fileName)

internal static class DirectivesExtensions
{
internal static void AddIfAny(this Dictionary<string, IReadOnlyList<IAbstractDirective>> resolvedDirectives, string directiveName, IReadOnlyList<IAbstractDirective> newDirectives)
internal static void AddIfAny(this Dictionary<string, ImmutableList<IAbstractDirective>> resolvedDirectives, string directiveName, IReadOnlyList<IAbstractDirective> newDirectives)
{
if (newDirectives.Any())
{
resolvedDirectives.Add(directiveName, newDirectives);
resolvedDirectives.Add(directiveName, newDirectives.ToImmutableList());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using DotVVM.Framework.Binding;
using System.Collections.Immutable;
using DotVVM.Framework.Compilation.ControlTree;

namespace DotVVM.Framework.Compilation.Directives
{
public record MarkupPageMetadata(
IReadOnlyDictionary<string, IReadOnlyList<IAbstractDirective>> Directives,
ImmutableDictionary<string, ImmutableList<IAbstractDirective>> Directives,
ImmutableList<NamespaceImport> Imports,
IAbstractDirective? MasterPageDirective,
ImmutableList<InjectedServiceExtensionParameter> InjectedServices,
ITypeDescriptor BaseType,
ITypeDescriptor? ViewModelType,
ViewModuleCompilationResult? ViewModuleResult,
ImmutableList<DotvvmProperty> Properties);
ImmutableList<IPropertyDescriptor> Properties);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using DotVVM.Framework.Compilation.ControlTree;
using DotVVM.Framework.Compilation.Parser;
using DotVVM.Framework.Compilation.Parser.Dothtml.Parser;

namespace DotVVM.Framework.Compilation.Directives
{
using DirectiveDictionary = ImmutableDictionary<string, ImmutableList<DothtmlDirectiveNode>>;

public class MasterPageDirectiveCompiler : DirectiveCompiler<IAbstractDirective, IAbstractDirective?>
{
public MasterPageDirectiveCompiler(IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName, IAbstractTreeBuilder treeBuilder)
public MasterPageDirectiveCompiler(DirectiveDictionary directiveNodesByName, IAbstractTreeBuilder treeBuilder)
: base(directiveNodesByName, treeBuilder)
{
}

public override string DirectiveName => ParserConstants.MasterPageDirective;

protected override IAbstractDirective? CreateArtefact(IReadOnlyList<IAbstractDirective> resolvedDirectives)
protected override IAbstractDirective? CreateArtefact(ImmutableList<IAbstractDirective> resolvedDirectives)
{
return resolvedDirectives.FirstOrDefault();
}
Expand Down
Loading

0 comments on commit 88ffbf6

Please sign in to comment.