[gh-88] Initial prototype.
This commit is contained in:
Родитель
f0a6b04bca
Коммит
dc708c6880
|
@ -43,7 +43,7 @@ namespace SharpLab.Server.Compilation.Setups {
|
|||
// ReSharper disable HeapView.ObjectAllocation.Evident
|
||||
|
||||
options.CSharp.ParseOptions = new CSharpParseOptions(MaxLanguageVersion, preprocessorSymbols: PreprocessorSymbols).WithFeatures(_features);
|
||||
options.CSharp.CompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true);
|
||||
options.CSharp.CompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
|
||||
options.CSharp.MetadataReferences = _references;
|
||||
|
||||
// ReSharper restore HeapView.ObjectAllocation.Evident
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using AppDomainToolkit;
|
||||
using AshMind.Extensions;
|
||||
using Microsoft.IO;
|
||||
using Unbreakable;
|
||||
|
||||
namespace SharpLab.Server.Execution {
|
||||
public class Executor {
|
||||
private readonly RecyclableMemoryStreamManager _memoryStreamManager;
|
||||
|
||||
public Executor(RecyclableMemoryStreamManager memoryStreamManager) {
|
||||
_memoryStreamManager = memoryStreamManager;
|
||||
}
|
||||
|
||||
public string Execute(Stream assemblyStream) {
|
||||
using (var guardedStream = _memoryStreamManager.GetStream()) {
|
||||
RuntimeGuardToken guardToken;
|
||||
using (assemblyStream) {
|
||||
guardToken = AssemblyGuard.Rewrite(assemblyStream, guardedStream);
|
||||
}
|
||||
|
||||
var currentSetup = AppDomain.CurrentDomain.SetupInformation;
|
||||
using (var context = AppDomainContext.Create(new AppDomainSetup {
|
||||
ApplicationBase = currentSetup.ApplicationBase,
|
||||
PrivateBinPath = currentSetup.PrivateBinPath
|
||||
})) {
|
||||
context.LoadAssembly(LoadMethod.LoadFrom, Assembly.GetExecutingAssembly().GetAssemblyFile().FullName);
|
||||
return RemoteFunc.Invoke(context.Domain, guardedStream, guardToken, Remote.Execute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Remote {
|
||||
public static string Execute(Stream assemblyStream, RuntimeGuardToken guardToken) {
|
||||
try {
|
||||
var assembly = Assembly.Load(ReadAllBytes(assemblyStream));
|
||||
var c = assembly.GetType("C");
|
||||
var m = c.GetMethod("M");
|
||||
|
||||
using (guardToken.Scope()) {
|
||||
return m.Invoke(Activator.CreateInstance(c), null)?.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return ex.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] ReadAllBytes(Stream stream) {
|
||||
byte[] bytes;
|
||||
if (stream is MemoryStream memoryStream) {
|
||||
bytes = memoryStream.GetBuffer();
|
||||
if (bytes.Length != memoryStream.Length)
|
||||
bytes = memoryStream.ToArray();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// we can't use ArrayPool here as this method is called in a temp AppDomain
|
||||
bytes = new byte[stream.Length];
|
||||
if (stream.Read(bytes, 0, (int)stream.Length) != bytes.Length)
|
||||
throw new NotSupportedException();
|
||||
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpLab.Server.MirrorSharp.Internal {
|
||||
public static class TargetNames {
|
||||
public const string Ast = "AST";
|
||||
public const string Run = "Run";
|
||||
}
|
||||
}
|
|
@ -11,6 +11,10 @@ namespace SharpLab.Server.MirrorSharp {
|
|||
return false;
|
||||
|
||||
session.SetTargetName(value);
|
||||
//if (value == TargetNames.Run) {
|
||||
// session.Roslyn.Project.
|
||||
//}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,34 +12,38 @@ using MirrorSharp.Advanced;
|
|||
using SharpLab.Server.Compilation;
|
||||
using SharpLab.Server.Decompilation;
|
||||
using SharpLab.Server.Decompilation.AstOnly;
|
||||
using SharpLab.Server.Execution;
|
||||
using SharpLab.Server.MirrorSharp.Internal;
|
||||
|
||||
namespace SharpLab.Server.MirrorSharp {
|
||||
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
|
||||
public class SlowUpdate : ISlowUpdateExtension {
|
||||
private const string AstTargetName = "AST";
|
||||
|
||||
private readonly ICompiler _compiler;
|
||||
private readonly IReadOnlyDictionary<string, IDecompiler> _decompilers;
|
||||
private readonly RecyclableMemoryStreamManager _memoryStreamManager;
|
||||
private readonly IReadOnlyDictionary<string, IAstTarget> _astTargets;
|
||||
private readonly Executor _executor;
|
||||
private readonly RecyclableMemoryStreamManager _memoryStreamManager;
|
||||
|
||||
public SlowUpdate(
|
||||
ICompiler compiler,
|
||||
IReadOnlyCollection<IDecompiler> decompilers,
|
||||
RecyclableMemoryStreamManager memoryStreamManager,
|
||||
IReadOnlyCollection<IAstTarget> astTargets
|
||||
IReadOnlyCollection<IAstTarget> astTargets,
|
||||
Executor executor,
|
||||
RecyclableMemoryStreamManager memoryStreamManager
|
||||
) {
|
||||
_compiler = compiler;
|
||||
_decompilers = decompilers.ToDictionary(d => d.LanguageName);
|
||||
_memoryStreamManager = memoryStreamManager;
|
||||
_astTargets = astTargets
|
||||
.SelectMany(t => t.SupportedLanguageNames.Select(n => (target: t, languageName: n)))
|
||||
.ToDictionary(x => x.languageName, x => x.target);
|
||||
_executor = executor;
|
||||
_memoryStreamManager = memoryStreamManager;
|
||||
}
|
||||
|
||||
public async Task<object> ProcessAsync(IWorkSession session, IList<Diagnostic> diagnostics, CancellationToken cancellationToken) {
|
||||
var targetName = session.GetTargetName();
|
||||
if (targetName == AstTargetName) {
|
||||
if (targetName == TargetNames.Ast) {
|
||||
var astTarget = _astTargets.GetValueOrDefault(session.LanguageName);
|
||||
return await astTarget.GetAstAsync(session, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
@ -47,7 +51,7 @@ namespace SharpLab.Server.MirrorSharp {
|
|||
if (diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))
|
||||
return null;
|
||||
|
||||
if (!_decompilers.ContainsKey(targetName))
|
||||
if (targetName != TargetNames.Run && !_decompilers.ContainsKey(targetName))
|
||||
throw new NotSupportedException($"Target '{targetName}' is not (yet?) supported by this branch.");
|
||||
|
||||
MemoryStream stream = null;
|
||||
|
@ -58,6 +62,9 @@ namespace SharpLab.Server.MirrorSharp {
|
|||
return null;
|
||||
}
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (targetName == TargetNames.Run)
|
||||
return _executor.Execute(stream);
|
||||
|
||||
// it's fine not to Dispose() here -- MirrorSharp will dispose it after calling WriteResult()
|
||||
return stream;
|
||||
}
|
||||
|
@ -74,12 +81,17 @@ namespace SharpLab.Server.MirrorSharp {
|
|||
}
|
||||
|
||||
var targetName = session.GetTargetName();
|
||||
if (session.GetTargetName() == AstTargetName) {
|
||||
if (targetName == TargetNames.Ast) {
|
||||
var astTarget = _astTargets.GetValueOrDefault(session.LanguageName);
|
||||
astTarget.SerializeAst(result, writer, session);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetName == TargetNames.Run) {
|
||||
writer.WriteValue((string)result);
|
||||
return;
|
||||
}
|
||||
|
||||
var decompiler = _decompilers[targetName];
|
||||
using (var stream = (Stream)result)
|
||||
using (var stringWriter = writer.OpenString()) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
<PackageReference Include="MirrorSharp.Owin" Version="0.9.0-pre-20170523" />
|
||||
<PackageReference Include="MirrorSharp.VisualBasic" Version="0.9.0-pre-20170523" />
|
||||
<PackageReference Include="SharpDisasm" Version="1.1.5" />
|
||||
<PackageReference Include="Unbreakable" Version="0.1.0-unproven" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -9,6 +9,7 @@ using SharpLab.Server.Compilation.Internal;
|
|||
using SharpLab.Server.Compilation.Setups;
|
||||
using SharpLab.Server.Decompilation;
|
||||
using SharpLab.Server.Decompilation.AstOnly;
|
||||
using SharpLab.Server.Execution;
|
||||
using SharpLab.Server.MirrorSharp;
|
||||
|
||||
namespace SharpLab.Server {
|
||||
|
@ -53,6 +54,8 @@ namespace SharpLab.Server {
|
|||
builder.RegisterType<ILDecompiler>().As<IDecompiler>().SingleInstance();
|
||||
builder.RegisterType<JitAsmDecompiler>().As<IDecompiler>().SingleInstance();
|
||||
|
||||
builder.RegisterType<Executor>().AsSelf().SingleInstance();
|
||||
|
||||
builder.RegisterInstance(new RecyclableMemoryStreamManager())
|
||||
.AsSelf();
|
||||
|
||||
|
|
|
@ -82,11 +82,16 @@
|
|||
|
||||
<div class="select-wrapper option-target-language option">
|
||||
<select v-model="options.target">
|
||||
<option v-bind:value="targets.csharp">C#</option>
|
||||
<option v-bind:value="targets.vb">Visual Basic</option>
|
||||
<option v-bind:value="targets.il">IL</option>
|
||||
<option v-bind:value="targets.asm">JIT Asm</option>
|
||||
<option v-bind:value="targets.ast">Syntax Tree</option>
|
||||
<optgroup label="Decompiled">
|
||||
<option v-bind:value="targets.csharp">C#</option>
|
||||
<option v-bind:value="targets.vb">Visual Basic</option>
|
||||
<option v-bind:value="targets.il">IL</option>
|
||||
<option v-bind:value="targets.asm">JIT Asm</option>
|
||||
</optgroup>
|
||||
<optgroup label="Other">
|
||||
<option v-bind:value="targets.ast">Syntax Tree</option>
|
||||
<option v-bind:value="targets.run">[Run]</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -5,5 +5,6 @@ export default Object.freeze({
|
|||
vb: languages.vb,
|
||||
il: 'IL',
|
||||
asm: 'JIT ASM',
|
||||
ast: 'AST'
|
||||
ast: 'AST',
|
||||
run: 'Run'
|
||||
});
|
|
@ -12,7 +12,8 @@ const languageAndTargetMap = {
|
|||
[languages.fsharp]: 'fs',
|
||||
[targets.il]: 'il',
|
||||
[targets.asm]: 'asm',
|
||||
[targets.ast]: 'ast'
|
||||
[targets.ast]: 'ast',
|
||||
[targets.run]: 'run'
|
||||
};
|
||||
const languageAndTargetMapReverse = mapObject(languageAndTargetMap, (key, value) => [value, key]);
|
||||
const targetMapReverseV1 = mapObject(languageAndTargetMapReverse, (key, value) => ['>' + key, value]); // eslint-disable-line prefer-template
|
||||
|
|
Загрузка…
Ссылка в новой задаче