[gh-196] Further improved static constructor block in JIT ASM.

This commit is contained in:
Andrey Shchekin 2017-09-24 23:21:41 +13:00
Родитель 2ad3e6f7fc
Коммит f0a77c9387
5 изменённых файлов: 29 добавлений и 65 удалений

Просмотреть файл

@ -7,6 +7,7 @@ using System.Threading.Tasks;
namespace SharpLab.Server.Common {
public static class TargetNames {
public const string Ast = "AST";
public const string JitAsm = "JIT ASM";
public const string Run = "Run";
}
}

Просмотреть файл

@ -11,6 +11,7 @@ using Microsoft.Diagnostics.Runtime;
using SharpDisasm;
using SharpDisasm.Translators;
using SharpLab.Runtime;
using SharpLab.Server.Common;
using SharpLab.Server.Decompilation.Internal;
namespace SharpLab.Server.Decompilation {
@ -18,7 +19,7 @@ namespace SharpLab.Server.Decompilation {
public class JitAsmDecompiler : IDecompiler {
private static readonly BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;
public string LanguageName => "JIT ASM";
public string LanguageName => TargetNames.JitAsm;
public void Decompile(Stream assemblyStream, TextWriter codeWriter) {
var currentSetup = AppDomain.CurrentDomain.SetupInformation;
@ -86,10 +87,6 @@ namespace SharpLab.Server.Decompilation {
writer.WriteLine(" ; However you can use attribute SharpLab.Runtime.JitGeneric to specify argument types.");
writer.WriteLine(" ; Example: [JitGeneric(typeof(int)), JitGeneric(typeof(string))] void M<T>() { ... }.");
return;
case Remote.MethodJitStatus.FailedTypeWithStaticConstructor:
writer.WriteLine(" ; Type {0} has a static constructor, which is not supported by SharpLab JIT decompiler.", method.Type.Name);
return;
}
var info = FindNonEmptyHotColdInfo(method);
@ -151,6 +148,13 @@ namespace SharpLab.Server.Decompilation {
private static class Remote {
public static IReadOnlyList<MethodJitResult> GetCompiledMethods(Stream assemblyStream) {
var assembly = Assembly.Load(ReadAllBytes(assemblyStream));
// This is a security consideration as PrepareMethod calls static ctors
foreach (var type in assembly.DefinedTypes) {
foreach (var constructor in type.DeclaredConstructors) {
if (constructor.IsStatic)
throw new NotSupportedException("Type " + type + " has a static constructor, which is not supported by SharpLab JIT decompiler.");
}
}
var results = new List<MethodJitResult>();
foreach (var type in assembly.DefinedTypes) {
CompileAndCollectMembers(results, type);
@ -158,49 +162,33 @@ namespace SharpLab.Server.Decompilation {
return results;
}
private static void CompileAndCollectMembers(ICollection<MethodJitResult> results, Type type) {
// This is a security consideration as PrepareMethod calls static ctors
var hasStaticConstructors = type.GetConstructors(BindingFlags.Static | BindingFlags.NonPublic).Any();
var typeIsGeneric = type.IsGenericTypeDefinition;
if (typeIsGeneric && !hasStaticConstructors) {
private static void CompileAndCollectMembers(ICollection<MethodJitResult> results, TypeInfo type) {
if (type.IsGenericTypeDefinition) {
if (TryCompileAndCollectMembersOfGeneric(results, type))
return;
}
foreach (var constructor in type.GetConstructors(BindingFlags)) {
if (hasStaticConstructors) {
results.Add(new MethodJitResult(constructor.MethodHandle, MethodJitStatus.FailedTypeWithStaticConstructor, typeIsGeneric));
continue;
}
foreach (var constructor in type.DeclaredConstructors) {
CollectCompiledWraps(results, constructor);
}
foreach (var method in type.GetMethods(BindingFlags)) {
foreach (var method in type.DeclaredMethods) {
if (method.IsAbstract)
continue;
if (hasStaticConstructors) {
var isGeneric = typeIsGeneric || method.IsGenericMethodDefinition;
results.Add(new MethodJitResult(method.MethodHandle, MethodJitStatus.FailedTypeWithStaticConstructor, isGeneric));
continue;
}
CollectCompiledWraps(results, method);
}
}
private static bool TryCompileAndCollectMembersOfGeneric(ICollection<MethodJitResult> results, Type type) {
private static bool TryCompileAndCollectMembersOfGeneric(ICollection<MethodJitResult> results, TypeInfo type) {
if (type.DeclaringType?.IsGenericTypeDefinition ?? false)
return true; // we expect to see that one separately when we visit the parent type
var hadAttribute = false;
foreach (var attribute in type.GetCustomAttributes<JitGenericAttribute>(false)) {
hadAttribute = true;
var genericInstance = type.MakeGenericType(attribute.ArgumentTypes);
var genericInstance = type.MakeGenericType(attribute.ArgumentTypes).GetTypeInfo();
CompileAndCollectMembers(results, genericInstance.GetTypeInfo());
foreach (var nested in genericInstance.GetNestedTypes(BindingFlags)) {
foreach (var nested in genericInstance.DeclaredNestedTypes) {
CompileAndCollectMembers(results, nested);
}
}
@ -276,8 +264,7 @@ namespace SharpLab.Server.Decompilation {
[Serializable]
public enum MethodJitStatus {
Success,
FailedOpenGenericWithNoAttribute,
FailedTypeWithStaticConstructor
FailedOpenGenericWithNoAttribute
}
}
}

Просмотреть файл

@ -108,8 +108,6 @@ namespace SharpLab.Tests {
[InlineData("JitAsm.OpenGenerics.cs2asm")]
[InlineData("JitAsm.GenericMethodWithAttribute.cs2asm")]
[InlineData("JitAsm.GenericClassWithAttribute.cs2asm")]
[InlineData("JitAsm.StaticConstructor.Explicit.cs2asm")]
[InlineData("JitAsm.StaticConstructor.Implicit.cs2asm")]
[InlineData("JitAsm.GenericMethodWithAttribute.fs2asm")]
public async Task SlowUpdate_ReturnsExpectedDecompiledCode_ForJitAsm(string resourceName) {
var data = TestData.FromResource(resourceName);
@ -124,6 +122,17 @@ namespace SharpLab.Tests {
Assert.Equal(data.Expected, decompiledText);
}
[Theory]
[InlineData("class C { static int F = 0; }")]
[InlineData("class C { static C() {} }")]
[InlineData("class C { class N { static N() {} } }")]
public async Task SlowUpdate_ReturnsNotSupportedError_ForJitAsmWithStaticConstructors(string code) {
var driver = MirrorSharpTestDriver.New(MirrorSharpOptions).SetText(code);
await driver.SendSetOptionsAsync(LanguageNames.CSharp, TargetNames.JitAsm);
await Assert.ThrowsAsync<NotSupportedException>(() => driver.SendSlowUpdateAsync<string>());
}
[Theory]
[InlineData("Ast.EmptyClass.cs2ast")]
[InlineData("Ast.StructuredTrivia.cs2ast")]

Просмотреть файл

@ -1,18 +0,0 @@
static class C {
private static readonly int X;
static C() {
X = 5;
}
static int M() => X;
}
#=>
; Desktop CLR v<IGNORE> (clr.dll) on x86.
C..cctor()
; Type C has a static constructor, which is not supported by SharpLab JIT decompiler.
C.M()
; Type C has a static constructor, which is not supported by SharpLab JIT decompiler.

Просмотреть файл

@ -1,15 +0,0 @@
static class C {
private static readonly int X = 5;
static int M() => X;
}
#=>
; Desktop CLR v<IGNORE> (clr.dll) on x86.
C..cctor()
; Type C has a static constructor, which is not supported by SharpLab JIT decompiler.
C.M()
; Type C has a static constructor, which is not supported by SharpLab JIT decompiler.