Skip to content

Commit

Permalink
Introducing Mono.Cecil
Browse files Browse the repository at this point in the history
This change modifies a little the architecture around Reflection.

- Using Mono.Cecil for reflecting assemblies
- Added assembly abstraction layer in order to limit the use of Cecil to
one single project only (avoiding dep spreading across the product and
tests)
- Added layer for assembly loading strategies

Verified that assemblies are correctly loaded without any need to load
external dependencies on that one. IL parsing executed fine and
attribute info available.

Unfortunately `System.Reflection` triggers many problems:
- If the assembly is loaded in reflection-only-context,
custom-attributes are not available.
- In order to have custom-attributes available, tried loading assembly
in normal exec context. Failure occurs as the assembly references other
assemblies (`mscorlib` and `Script.Import.dll`) which cannot be found.
  • Loading branch information
andry-tino committed May 11, 2017
1 parent d8b1c8e commit 88ed43b
Show file tree
Hide file tree
Showing 42 changed files with 923 additions and 195 deletions.
14 changes: 10 additions & 4 deletions src/Reflection.ScriptSharp/ASTBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
namespace Rosetta.Reflection.ScriptSharp
{
using System;
using System.Reflection;
using System.IO;

using Rosetta.Reflection.Proxies;
using Rosetta.Reflection.ScriptSharp.Helpers;

/// <summary>
Expand All @@ -21,12 +22,17 @@ public class ASTBuilder : Rosetta.Reflection.ASTBuilder
/// This class builds a plain tree from all the types in the assembly and considers the ScriptSharp
/// namespace substitution attribute <code>ScriptNamespace</code>.
/// </summary>
/// <param name="assembly">The path to the assembly.</param>
public ASTBuilder(Assembly assembly) : base(assembly)
/// <param name="assembly">The assembly.</param>
/// <param name="rawAssembly">
/// The raw assembly to be used to create the <see cref="Compilation"/> object.
/// If not provided, the builder will return an <see cref="ASTInfo"/> where <see cref="ASTInfo.CompilationUnit"/> is <code>null</code>.
/// </param>
public ASTBuilder(IAssemblyProxy assembly, Stream rawAssembly = null)
: base(assembly, rawAssembly)
{
}

protected override Rosetta.Reflection.Helpers.Namespace CreateNamespaceHelper(System.Reflection.TypeInfo type)
protected override Rosetta.Reflection.Helpers.Namespace CreateNamespaceHelper(ITypeInfoProxy type)
{
return new Namespace(type);
}
Expand Down
6 changes: 4 additions & 2 deletions src/Reflection.ScriptSharp/ProgramWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
namespace Rosetta.Reflection.ScriptSharp
{
using System;
using System.Reflection;
using System.IO;

using Rosetta.Reflection.Proxies;

/// <summary>
/// Initiates the translation.
Expand All @@ -22,6 +24,6 @@ public ProgramWrapper(string assemblyPath)
{
}

protected override IASTBuilder CreateASTBuilder(Assembly assembly) => new ASTBuilder(assembly);
protected override IASTBuilder CreateASTBuilder(IAssemblyProxy assembly, Stream rawAssembly) => new ASTBuilder(assembly, rawAssembly);
}
}
6 changes: 4 additions & 2 deletions src/Reflection.ScriptSharp/Reflection.ScriptSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
<Private>True</Private>
</Reference>
<Reference Include="System.Configuration" />
<Reference Include="System.Reflection.Metadata, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Reference Include="System.Reflection.Metadata, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Reflection.Metadata.1.2.0\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath>
<Private>True</Private>
</Reference>
Expand Down Expand Up @@ -113,7 +113,9 @@
<Name>Reflection</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
Expand Down
11 changes: 11 additions & 0 deletions src/Reflection.ScriptSharp/app.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
17 changes: 14 additions & 3 deletions src/Reflection.ScriptSharp/helpers/Namespace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace Rosetta.Reflection.ScriptSharp.Helpers
{
using System;
using System.Collections.Generic;
using System.Reflection;

using Rosetta.Reflection.Proxies;

/// <summary>
/// Helper for <see cref="TypeInfo"/> in order to retrieve information about its namespace.
Expand All @@ -21,7 +22,7 @@ public class Namespace : Rosetta.Reflection.Helpers.Namespace
/// Initializes a new instance of the <see cref="Namespace"/> class.
/// </summary>
/// <param name="type"></param>
public Namespace(TypeInfo type) : base(type)
public Namespace(ITypeInfoProxy type) : base(type)
{
}

Expand All @@ -40,7 +41,17 @@ public override string FullName
{
this.fullName = base.FullName;

foreach (CustomAttributeData attribute in this.Type.CustomAttributes)
IEnumerable<ICustomAttributeDataProxy> customAttributes = null;
try
{
customAttributes = this.Type.CustomAttributes;
} catch (TypeLoadException e)
{
// Could not access the attributes NEEDED???
return this.fullName;
}

foreach (ICustomAttributeDataProxy attribute in customAttributes)
{
if (ScriptNamespaceAttributeDecoration.IsScriptNamespaceAttributeDecoration(attribute.AttributeType))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
namespace Rosetta.Reflection.ScriptSharp.Helpers
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Rosetta.Reflection.Proxies;

/// <summary>
/// Abstraction for building an AST from an assembly.
Expand All @@ -18,7 +17,7 @@ public class ScriptNamespaceAttributeDecoration
public const string ScriptNamespaceName = "ScriptNamespace";
public const string ScriptNamespaceFullName = "ScriptNamespace"; // TODO: Find namespace used by ScriptSharp for this class

private readonly CustomAttributeData attributeData;
private readonly ICustomAttributeDataProxy attributeData;

// Cached values
private string overridenNamespace;
Expand All @@ -27,7 +26,7 @@ public class ScriptNamespaceAttributeDecoration
/// Initializes a new instance of the <see cref="Namespace"/> class.
/// </summary>
/// <param name="attributeData"></param>
public ScriptNamespaceAttributeDecoration(CustomAttributeData attributeData)
public ScriptNamespaceAttributeDecoration(ICustomAttributeDataProxy attributeData)
{
if (attributeData == null)
{
Expand Down Expand Up @@ -71,7 +70,7 @@ public string OverridenNamespace
/// </summary>
/// <param name="attribute"></param>
/// <returns></returns>
public static bool IsScriptNamespaceAttributeDecoration(Type attribute)
public static bool IsScriptNamespaceAttributeDecoration(ITypeProxy attribute)
{
return attribute.Name == ScriptNamespaceFullName;
}
Expand Down
65 changes: 27 additions & 38 deletions src/Reflection/ASTBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ namespace Rosetta.Reflection
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

using Rosetta.Reflection.Helpers;
using Rosetta.Reflection.Proxies;

/// <summary>
/// Builds an AST from an assembly.
Expand All @@ -29,7 +28,8 @@ namespace Rosetta.Reflection
/// </remarks>
public class ASTBuilder : IASTBuilder
{
private readonly Assembly assembly;
private readonly IAssemblyProxy assembly;
private readonly Stream rawAssembly;

// Cached quantities
private ASTInfo astInfo;
Expand All @@ -39,14 +39,19 @@ public class ASTBuilder : IASTBuilder
/// This class builds a plain tree from all the types in the assembly.
/// </summary>
/// <param name="assembly">The path to the assembly.</param>
public ASTBuilder(Assembly assembly)
/// <param name="rawAssembly">
/// The raw assembly to be used to create the <see cref="Compilation"/> object.
/// If not provided, the builder will return an <see cref="ASTInfo"/> where <see cref="ASTInfo.CompilationUnit"/> is <code>null</code>.
/// </param>
public ASTBuilder(IAssemblyProxy assembly, Stream rawAssembly = null)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}

this.assembly = assembly;
this.rawAssembly = rawAssembly;
}

/// <summary>
Expand All @@ -60,29 +65,14 @@ public ASTInfo Build()
return this.astInfo;
}

IEnumerable<System.Reflection.TypeInfo> types = null;
IEnumerable<ITypeInfoProxy> types = null;
try
{
types = this.assembly.DefinedTypes;
}
catch(ReflectionTypeLoadException ex)
catch(Exception ex)
{
var errorMessage = new StringBuilder();
errorMessage.AppendLine($"Error loading defined types in assembly {this.assembly.FullName}. Found {ex.LoaderExceptions.Length} errors:");

foreach (var innerException in ex.LoaderExceptions)
{
errorMessage.AppendLine($"{innerException.GetType().Name} - {innerException.HResult}: {innerException.Message}");
}

// Try to get all the available types
types = ex.Types.Where(t => t != null).Select(IntrospectionExtensions.GetTypeInfo);

if (types == null || types.Count() == 0)
{
// Rethrow by emitting all errors
throw new InvalidOperationException(errorMessage.ToString(), ex);
}
throw new InvalidOperationException("An error occurred while trying to load defined types", ex);
}

var nodes = new List<MemberDeclarationSyntax>();
Expand Down Expand Up @@ -117,43 +107,42 @@ public ASTInfo Build()
};
}

private MemberDeclarationSyntax BuildClassNode(System.Reflection.TypeInfo type)
private MemberDeclarationSyntax BuildClassNode(ITypeInfoProxy type)
{
return this.BuildNode(type, SyntaxFactory.ClassDeclaration);
}

private MemberDeclarationSyntax BuildStructNode(System.Reflection.TypeInfo type)
private MemberDeclarationSyntax BuildStructNode(ITypeInfoProxy type)
{
return this.BuildNode(type, SyntaxFactory.StructDeclaration);
}

private MemberDeclarationSyntax BuildEnumNode(System.Reflection.TypeInfo type)
private MemberDeclarationSyntax BuildEnumNode(ITypeInfoProxy type)
{
return this.BuildNode(type, SyntaxFactory.EnumDeclaration);
}

private MemberDeclarationSyntax BuildInterfaceNode(System.Reflection.TypeInfo type)
private MemberDeclarationSyntax BuildInterfaceNode(ITypeInfoProxy type)
{
return this.BuildNode(type, SyntaxFactory.InterfaceDeclaration);
}

private Compilation BuildCompilationUnit(SyntaxTree tree)
{
var references = new List<MetadataReference>();

using (var stream = new MemoryStream())
if (this.rawAssembly == null)
{
var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
bf.Serialize(stream, this.assembly);

var metadataReference = MetadataReference.CreateFromStream(stream);
references.Add(metadataReference);
return null;
}


var references = new List<MetadataReference>();

var metadataReference = MetadataReference.CreateFromStream(this.rawAssembly);
references.Add(metadataReference);

return CSharpCompilation.Create("GeneratedCompilation", new[] { tree }, references);
}

private MemberDeclarationSyntax BuildNode(System.Reflection.TypeInfo type, RoslynNodeFactory factory)
private MemberDeclarationSyntax BuildNode(ITypeInfoProxy type, RoslynNodeFactory factory)
{
var helper = this.CreateNamespaceHelper(type);

Expand All @@ -175,7 +164,7 @@ private MemberDeclarationSyntax BuildNode(System.Reflection.TypeInfo type, Rosly
return node;
}

protected virtual Namespace CreateNamespaceHelper(System.Reflection.TypeInfo type)
protected virtual Namespace CreateNamespaceHelper(ITypeInfoProxy type)
{
return new Namespace(type);
}
Expand Down
45 changes: 0 additions & 45 deletions src/Reflection/FSAssemblyLoader.cs

This file was deleted.

22 changes: 0 additions & 22 deletions src/Reflection/IAssemblyLoader.cs

This file was deleted.

Loading

1 comment on commit 88ed43b

@andry-tino
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Targets #41

Please sign in to comment.