Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow weird assembly name #1741

Merged
merged 1 commit into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DotVVM.Framework.Compilation.Parser.Binding.Tokenizer;
using System.Diagnostics;
using System.Collections.Immutable;

namespace DotVVM.Framework.Compilation.Parser.Binding.Parser
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class AssemblyNameBindingParserNode : BindingParserNode
{
public string Name { get; }
public AssemblyNameBindingParserNode(List<BindingToken> tokens)
{
Tokens = tokens;
Name = string.Concat(tokens.Select(t => t.Text));
}

public AssemblyNameBindingParserNode(string name)
: this(new List<BindingToken>() { new BindingToken(name, BindingTokenType.Identifier, 0, 0, 0, 0)})
{ }

public override IEnumerable<BindingParserNode> EnumerateChildNodes()
=> Enumerable.Empty<BindingParserNode>();

public override string ToDisplayString() => $"{Name}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ namespace DotVVM.Framework.Compilation.Parser.Binding.Parser
public class AssemblyQualifiedNameBindingParserNode: BindingParserNode
{
public BindingParserNode TypeName { get; }
public BindingParserNode AssemblyName { get; }
public AssemblyNameBindingParserNode AssemblyName { get; }

public AssemblyQualifiedNameBindingParserNode(BindingParserNode typeName, BindingParserNode assemblyName)
public AssemblyQualifiedNameBindingParserNode(BindingParserNode typeName, AssemblyNameBindingParserNode assemblyName)
{
this.TypeName = typeName;
this.AssemblyName = assemblyName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using DotVVM.Framework.Utils;

namespace DotVVM.Framework.Compilation.Parser.Binding.Parser
{
Expand Down Expand Up @@ -128,31 +129,7 @@ public BindingParserNode ReadDirectiveTypeName()
if (PeekType() == BindingTokenType.Comma)
{
Read();
var assemblyName = ReadNamespaceOrTypeName();

// SimpleNameBinding means that assembly name does not contain dots
// MemberAccessBinding means that assembly name is complex (multiple identifiers delimited with dots)
if (!(assemblyName is SimpleNameBindingParserNode || assemblyName is MemberAccessBindingParserNode))
{
assemblyName.NodeErrors.Add($"Expected assembly name but instead got {assemblyName.GetType().Name}.");
}
else if (assemblyName is MemberAccessBindingParserNode)
{
// Make sure there is no GenericNameBinding within assemblyName
var assemblyBinding = assemblyName;
while (assemblyBinding is MemberAccessBindingParserNode assemblyMemberBinding)
{
var memberExprType = assemblyMemberBinding.MemberNameExpression.GetType();
var targetExprType = assemblyMemberBinding.TargetExpression.GetType();
if (memberExprType == typeof(GenericTypeReferenceBindingParserNode) || targetExprType == typeof(GenericTypeReferenceBindingParserNode))
{
assemblyName.NodeErrors.Add($"Generic identifier name is not allowed in an assembly name.");
break;
}

assemblyBinding = assemblyMemberBinding.TargetExpression;
}
}
var assemblyName = ReadAssemblyName();

return new AssemblyQualifiedNameBindingParserNode(typeName, assemblyName);
}
Expand All @@ -163,6 +140,34 @@ public BindingParserNode ReadDirectiveTypeName()
return typeName;
}

public AssemblyNameBindingParserNode ReadAssemblyName()
{
// almost anything can be an assembly name, so we just read everything until the end
SkipWhiteSpace();
var tokens = this.Tokens.Skip(CurrentIndex).ToList();
CurrentIndex = Tokens.Count;

while (tokens.Count > 0 && tokens[tokens.Count - 1].Type == BindingTokenType.WhiteSpace)
tokens.RemoveAt(tokens.Count - 1);

var node = new AssemblyNameBindingParserNode(tokens);
if (node.Name.Length == 0)
{
node.NodeErrors.Add("Assembly name cannot be empty.");
return node;
}
try
{
new AssemblyName(node.Name);
}
catch (Exception ex)
{
node.NodeErrors.Add($"'{node.Name}' is invalid assembly name ({ex.Message})");
return node;
}
return node;
}

public BindingParserNode ReadNamespaceOrTypeName()
{
return ReadIdentifierExpression(true);
Expand Down
20 changes: 13 additions & 7 deletions src/Tests/Parser/Binding/BindingParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -859,20 +859,26 @@ public void BindingParser_ArrayType_ValidAssemblyName(string binding)
}

[TestMethod]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain.Company.Product")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Product")]
public void BindingParser_AssemblyQualifiedName_ValidAssemblyName(string binding)
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain.Company.Product", "Domain.Company.Product")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Product", "Product")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, My-Assembly-Name", "My-Assembly-Name")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain.Company.Product<int>", "Domain.Company.Product<int>")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain.Company<int>.Product", "Domain.Company<int>.Product")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain<int>.Company.Product", "Domain<int>.Company.Product")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Product<int>", "Product<int>")]

public void BindingParser_AssemblyQualifiedName_ValidAssemblyName(string binding, string assemblyName)
{
var parser = bindingParserNodeFactory.SetupParser(binding);
var node = parser.ReadDirectiveTypeName() as AssemblyQualifiedNameBindingParserNode;
Assert.IsFalse(node.AssemblyName.HasNodeErrors);
Assert.AreEqual(assemblyName, node.AssemblyName.ToDisplayString());
}

[TestMethod]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain.Company.Product<int>")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain.Company<int>.Product")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Domain<int>.Company.Product")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, Product<int>")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, ")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, ,,,,,,,,,")]
[DataRow("Domain.Company.Product.DotVVM.Feature.Type, this/is/apparently/invalid/for/some/reason")]
public void BindingParser_AssemblyQualifiedName_InvalidAssemblyName(string binding)
{
var parser = bindingParserNodeFactory.SetupParser(binding);
Expand Down
Loading