Skip to content

Commit

Permalink
@Property directive compilation pipeline changed to allow control bas…
Browse files Browse the repository at this point in the history
…e type generation in VS Extension
  • Loading branch information
Milan Mikuš committed Feb 9, 2024
1 parent 14dd80a commit bc40dcc
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 135 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public virtual IAbstractTreeRoot ResolveTree(DothtmlRootNode root, string fileNa
// We need to call BuildControlMetadata instead of ResolveControl. The control builder for the control doesn't have to be compiled yet so the
// metadata would be incomplete and ResolveControl caches them internally. BuildControlMetadata just builds the metadata and the control is
// actually resolved when the control builder is ready and the metadata are complete.
var viewMetadata = controlResolver.BuildControlMetadata(CreateControlType(directiveMetadata.BaseType, directiveMetadata.Properties, fileName));
var viewMetadata = controlResolver.BuildControlMetadata(CreateControlType(directiveMetadata.BaseType, fileName));

var dataContextTypeStack = CreateDataContextTypeStack(directiveMetadata.ViewModelType, null, directiveMetadata.Imports, new BindingExtensionParameter[] {
new CurrentMarkupControlExtensionParameter(directiveMetadata.BaseType),
Expand Down Expand Up @@ -776,7 +776,7 @@ public static (ITypeDescriptor? type, List<BindingExtensionParameter> extensionP
/// <summary>
/// Creates the IControlType identification of the control.
/// </summary>
protected abstract IControlType CreateControlType(ITypeDescriptor wrapperType, IReadOnlyList<IPropertyDescriptor> properties, string virtualPath);
protected abstract IControlType CreateControlType(ITypeDescriptor wrapperType, string virtualPath);

/// <summary>
/// Creates the data context type stack object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ protected override void ResolveRootContent(DothtmlRootNode root, IAbstractContro
((ResolvedTreeRoot)view).ResolveContentAction = () => base.ResolveRootContent(root, view, viewMetadata);
}

protected override IControlType CreateControlType(ITypeDescriptor wrapperType, IReadOnlyList<IPropertyDescriptor> markupProperties, string virtualPath)
protected override IControlType CreateControlType(ITypeDescriptor wrapperType, string virtualPath)
{
return new ControlType(ResolvedTypeDescriptor.ToSystemType(wrapperType), virtualPath: virtualPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace DotVVM.Framework.Compilation.Directives
{
public abstract class BaseTypeDirectiveCompiler : DirectiveCompiler<IAbstractBaseTypeDirective, ITypeDescriptor>
public class BaseTypeDirectiveCompiler : DirectiveCompiler<IAbstractBaseTypeDirective, ITypeDescriptor>
{
private readonly string fileName;
private readonly ImmutableList<NamespaceImport> imports;
Expand Down Expand Up @@ -68,41 +68,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
Expand Up @@ -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 @@ -43,14 +43,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 @@ -66,10 +66,10 @@ public MarkupPageMetadata Compile(DothtmlRootNode dothtmlRoot, string fileName)
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace DotVVM.Framework.Compilation.Directives
{
public record MarkupPageMetadata(
IReadOnlyDictionary<string, IReadOnlyList<IAbstractDirective>> Directives,
ImmutableList<NamespaceImport> Imports,
IReadOnlyList<NamespaceImport> Imports,
IAbstractDirective? MasterPageDirective,
ImmutableList<InjectedServiceExtensionParameter> InjectedServices,
IReadOnlyList<InjectedServiceExtensionParameter> InjectedServices,
ITypeDescriptor BaseType,
ITypeDescriptor? ViewModelType,
ViewModuleCompilationResult? ViewModuleResult,
ImmutableList<IPropertyDescriptor> Properties);
IReadOnlyList<IPropertyDescriptor> Properties);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,23 @@
using DotVVM.Framework.Binding;
using DotVVM.Framework.Compilation.ControlTree.Resolved;
using System;
using System.Text;
using System.Security.Cryptography;
using DotVVM.Framework.Controls;
using System.Reflection.Emit;
using System.Reflection;

namespace DotVVM.Framework.Compilation.Directives
{
public abstract class PropertyDeclarationDirectiveCompiler : DirectiveCompiler<IAbstractPropertyDeclarationDirective, ImmutableList<IPropertyDescriptor>>
public record PropertyDirectiveCompilerResult(IReadOnlyList<IPropertyDescriptor> Properties, ITypeDescriptor ModifiedMarkupControlType);

public abstract class PropertyDeclarationDirectiveCompiler : DirectiveCompiler<IAbstractPropertyDeclarationDirective, PropertyDirectiveCompilerResult>
{
private readonly ITypeDescriptor controlWrapperType;
private readonly ImmutableList<NamespaceImport> imports;

protected virtual ITypeDescriptor DotvvmMarkupControlType => new ResolvedTypeDescriptor(typeof(DotvvmMarkupControl));

public override string DirectiveName => ParserConstants.PropertyDeclarationDirective;

public PropertyDeclarationDirectiveCompiler(IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName, IAbstractTreeBuilder treeBuilder, ITypeDescriptor controlWrapperType, ImmutableList<NamespaceImport> imports)
Expand Down Expand Up @@ -103,53 +112,55 @@ private AttributeInfo GetAttributeInfo(BindingParserNode attributeReference, Dot
return new AttributeInfo(type, attributePropertyNameReference, initializer);
}

protected override ImmutableList<IPropertyDescriptor> CreateArtefact(IReadOnlyList<IAbstractPropertyDeclarationDirective> directives)
protected override PropertyDirectiveCompilerResult CreateArtefact(IReadOnlyList<IAbstractPropertyDeclarationDirective> directives)
{
var generatedWrapperType = directives.Any()
? (CreateDynamicDeclaringType(controlWrapperType, directives) ?? controlWrapperType)
: controlWrapperType;

foreach (var directive in directives)
{
directive.DeclaringType = controlWrapperType;
directive.DeclaringType = generatedWrapperType;
}

return directives
var properties = directives
.Where(HasPropertyType)
.Select(TryCreateDotvvmPropertyFromDirective)
.ToImmutableList();
}

protected abstract bool HasPropertyType(IAbstractPropertyDeclarationDirective directive);
protected abstract IPropertyDescriptor TryCreateDotvvmPropertyFromDirective(IAbstractPropertyDeclarationDirective propertyDeclarationDirective);

private record AttributeInfo(ActualTypeReferenceBindingParserNode Type, IdentifierNameBindingParserNode Name, BindingParserNode Initializer);
}
return new PropertyDirectiveCompilerResult(properties, generatedWrapperType);
}

public class ResolvedPropertyDeclarationDirectiveCompiler : PropertyDeclarationDirectiveCompiler
{
public ResolvedPropertyDeclarationDirectiveCompiler(
IReadOnlyDictionary<string, IReadOnlyList<DothtmlDirectiveNode>> directiveNodesByName,
IAbstractTreeBuilder treeBuilder, ITypeDescriptor controlWrapperType,
ImmutableList<NamespaceImport> imports)
: base(directiveNodesByName, treeBuilder, controlWrapperType, imports)
/// <summary> Gets or creates dynamic declaring type, and registers on it the properties declared using `@property` directives </summary>
protected virtual ITypeDescriptor? CreateDynamicDeclaringType(
ITypeDescriptor? originalWrapperType,
IReadOnlyList<IAbstractPropertyDeclarationDirective> 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, propertyDirectives);
}

protected override bool HasPropertyType(IAbstractPropertyDeclarationDirective directive)
=> directive.PropertyType is ResolvedTypeDescriptor { Type: not null };
protected abstract ITypeDescriptor? GetOrCreateDynamicType(ITypeDescriptor baseType, string typeName, IReadOnlyList<IAbstractPropertyDeclarationDirective> propertyDirectives);

protected override IPropertyDescriptor TryCreateDotvvmPropertyFromDirective(IAbstractPropertyDeclarationDirective propertyDeclarationDirective)
{
if (propertyDeclarationDirective.PropertyType is not ResolvedTypeDescriptor { Type: not null } propertyType) { throw new ArgumentException("propertyDeclarationDirective.PropertyType must be of type ResolvedTypeDescriptor and have non null type."); }
if (propertyDeclarationDirective.DeclaringType is not ResolvedTypeDescriptor { Type: not null } declaringType) { throw new ArgumentException("propertyDeclarationDirective.DeclaringType must be of type ResolvedTypeDescriptor and have non null type."); }

return DotvvmProperty.Register(
propertyDeclarationDirective.NameSyntax.Name,
propertyType.Type,
declaringType.Type,
propertyDeclarationDirective.InitialValue,
false,
null,
propertyDeclarationDirective,
false);
}
}
protected abstract bool HasPropertyType(IAbstractPropertyDeclarationDirective directive);
protected abstract IPropertyDescriptor TryCreateDotvvmPropertyFromDirective(IAbstractPropertyDeclarationDirective propertyDeclarationDirective);

private record AttributeInfo(ActualTypeReferenceBindingParserNode Type, IdentifierNameBindingParserNode Name, BindingParserNode Initializer);
}
}

This file was deleted.

Loading

0 comments on commit bc40dcc

Please sign in to comment.