Skip to content

Commit 1a1adaa

Browse files
committed
refactor
1 parent 316d9c7 commit 1a1adaa

File tree

7 files changed

+123
-100
lines changed

7 files changed

+123
-100
lines changed

ExpressionDebugger.Console/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ static void Main(string[] args)
1414
var lambda = Expression.Lambda<Func<int, int, int>>(body, p1, p2);
1515
var fun = lambda.CompileWithDebugInfo();
1616
var result = fun(1, 2);
17-
System.Console.WriteLine(3);
17+
System.Console.WriteLine(result);
1818
}
1919
}
2020
}

ExpressionDebugger.Tests/DebugInfoInjectorTest.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public void TestLambda_Inline()
263263
Assert.AreEqual(@"
264264
public IQueryable<int> Main(IQueryable<int> q)
265265
{
266-
return q.Where(it => it > 0);
266+
return q.Where<int>(it => it > 0);
267267
}"
268268
, str);
269269
}
@@ -383,13 +383,13 @@ public void TestMemberInit()
383383
};
384384
var str = fn.ToScript();
385385
Assert.AreEqual(@"
386-
public Poco Main()
386+
public DebugInfoInjectorTest.Poco Main()
387387
{
388-
return new Poco()
388+
return new DebugInfoInjectorTest.Poco()
389389
{
390390
Name = ""1"",
391391
Parent = {Name = ""2""},
392-
Children = {new Poco(), new Poco()}
392+
Children = {new DebugInfoInjectorTest.Poco(), new DebugInfoInjectorTest.Poco()}
393393
};
394394
}"
395395
, str);
@@ -442,7 +442,7 @@ public void TestNewArray()
442442
Assert.AreEqual(@"
443443
public int[] Main(int i)
444444
{
445-
return new[] {i};
445+
return new int[] {i};
446446
}"
447447
, str);
448448
}

ExpressionDebugger/ExpressionCompiler.cs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.IO;
88
using System.Linq;
9+
using System.Linq.Expressions;
910
using System.Reflection;
1011
using System.Runtime.Loader;
1112
using System.Text;
@@ -14,8 +15,10 @@ namespace ExpressionDebugger
1415
{
1516
public class ExpressionCompiler
1617
{
18+
public List<ExpressionTranslator> Translators { get; } = new List<ExpressionTranslator>();
19+
1720
private readonly ExpressionCompilationOptions _options;
18-
public ExpressionCompiler(ExpressionCompilationOptions options)
21+
public ExpressionCompiler(ExpressionCompilationOptions options = null)
1922
{
2023
_options = options;
2124
}
@@ -51,16 +54,41 @@ public void AddFile(string code, string filename)
5154
_codes.Add(encoded);
5255
}
5356

54-
public Assembly CreateAssembly(IEnumerable<Assembly> assemblies)
57+
public void AddFile(LambdaExpression node, ExpressionDefinitions definitions = null)
58+
{
59+
if (definitions == null)
60+
definitions = _options?.DefaultDefinitions ?? new ExpressionDefinitions { IsStatic = true };
61+
if (definitions.TypeName == null)
62+
definitions.TypeName = "Program";
63+
64+
var translator = ExpressionTranslator.Create(node, definitions);
65+
var script = translator.ToString();
66+
Translators.Add(translator);
67+
68+
this.AddFile(script, Path.ChangeExtension(Path.GetRandomFileName(), ".cs"));
69+
}
70+
71+
public Assembly CreateAssembly()
5572
{
73+
var references = new HashSet<Assembly>();
74+
references.UnionWith(from t in Translators
75+
from n in t.TypeNames
76+
select n.Key.Assembly);
77+
78+
if (_options?.References != null)
79+
references.UnionWith(_options.References);
80+
references.Add(typeof(object).Assembly);
81+
references.Add(Assembly.Load(new AssemblyName("System.Runtime")));
82+
references.Add(Assembly.Load(new AssemblyName("System.Collections")));
83+
5684
var assemblyName = Path.GetRandomFileName();
5785
var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
5886

59-
var references = assemblies.Select(it => MetadataReference.CreateFromFile(it.Location));
87+
var metadataReferences = references.Select(it => MetadataReference.CreateFromFile(it.Location));
6088
CSharpCompilation compilation = CSharpCompilation.Create(
6189
assemblyName,
6290
_codes,
63-
references,
91+
metadataReferences,
6492
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, usings: new[] { "System" })
6593
.WithOptimizationLevel(_options?.IsRelease == true ? OptimizationLevel.Release : OptimizationLevel.Debug)
6694
.WithPlatform(Platform.AnyCpu)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using ExpressionDebugger;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Reflection;
5+
using System.Collections.Generic;
6+
7+
namespace System.Linq.Expressions
8+
{
9+
public static class ExpressionDebuggerExtensions
10+
{
11+
12+
/// <summary>
13+
/// Compile with debugging info injected
14+
/// </summary>
15+
/// <typeparam name="T">Type of lambda expression</typeparam>
16+
/// <param name="node">Lambda expression</param>
17+
/// <param name="options">Compilation options</param>
18+
/// <returns>Generated method</returns>
19+
public static T CompileWithDebugInfo<T>(this Expression<T> node, ExpressionCompilationOptions options = null)
20+
{
21+
return (T)(object)CompileWithDebugInfo((LambdaExpression)node, options);
22+
}
23+
24+
public static Delegate CompileWithDebugInfo(this LambdaExpression node, ExpressionCompilationOptions options = null)
25+
{
26+
try
27+
{
28+
var compiler = new ExpressionCompiler(options);
29+
compiler.AddFile(node);
30+
var assembly = compiler.CreateAssembly();
31+
32+
var translator = compiler.Translators[0];
33+
return translator.CreateDelegate(assembly);
34+
}
35+
catch (Exception ex)
36+
{
37+
Debug.Print(ex.ToString());
38+
return node.Compile();
39+
}
40+
}
41+
42+
public static Delegate CreateDelegate(this ExpressionTranslator translator, Assembly assembly)
43+
{
44+
var definitions = translator.Definitions;
45+
var typeName = definitions.Namespace == null
46+
? definitions.TypeName
47+
: (definitions.Namespace + "." + definitions.TypeName);
48+
var type = assembly.GetType(typeName);
49+
var method = type.GetMethod(definitions.MethodName ?? "Main");
50+
var obj = definitions.IsStatic ? null : Activator.CreateInstance(type);
51+
foreach (var kvp in translator.Constants)
52+
{
53+
var field = type.GetField(kvp.Value);
54+
field.SetValue(obj, kvp.Key);
55+
}
56+
return method.CreateDelegate(translator.Expression.Type, obj);
57+
}
58+
}
59+
}

ExpressionDebugger/ExpressionExtensions.cs

Lines changed: 0 additions & 69 deletions
This file was deleted.

ExpressionTranslator/ExpressionTranslator.cs

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,41 @@ public class ExpressionTranslator : ExpressionVisitor
2121
private Dictionary<Type, string> _typeNames;
2222
private HashSet<string> _usings;
2323
private Dictionary<object, string> _constants;
24-
private readonly ExpressionDefinitions _definitions;
2524

2625
public Dictionary<object, string> Constants => _constants ?? (_constants = new Dictionary<object, string>());
2726
public Dictionary<Type, string> TypeNames => _typeNames ?? (_typeNames = new Dictionary<Type, string>());
2827

2928
public bool HasDynamic { get; private set; }
29+
public ExpressionDefinitions Definitions { get; }
30+
public Expression Expression { get; private set; }
3031

31-
public ExpressionTranslator(ExpressionDefinitions definitions = null)
32+
private ExpressionTranslator(ExpressionDefinitions definitions = null)
3233
{
33-
_definitions = definitions;
34+
Definitions = definitions;
3435
_writer = new StringWriter();
3536
ResetIndentLevel();
3637
}
3738

3839
private void ResetIndentLevel()
3940
{
4041
_indentLevel = 0;
41-
if (_definitions?.TypeName != null)
42+
if (Definitions?.TypeName != null)
4243
{
4344
_indentLevel++;
44-
if (_definitions.Namespace != null)
45+
if (Definitions.Namespace != null)
4546
_indentLevel++;
4647
}
4748
}
4849

49-
public string Translate(Expression node)
50+
public static ExpressionTranslator Create(Expression node, ExpressionDefinitions definitions = null)
5051
{
52+
var translator = new ExpressionTranslator(definitions);
5153
if (node.NodeType == ExpressionType.Lambda)
52-
VisitLambda((LambdaExpression)node, LambdaType.Main);
54+
translator.VisitLambda((LambdaExpression)node, LambdaType.Main);
5355
else
54-
Visit(node);
55-
return this.ToString();
56+
translator.Visit(node);
57+
translator.Expression = node;
58+
return translator;
5659
}
5760

5861
private static int GetPrecedence(ExpressionType nodeType)
@@ -574,7 +577,7 @@ private IEnumerable<Expression> VisitBlockBody(IList<Expression> exprs, bool sho
574577
Expression next;
575578
if (isInline)
576579
{
577-
if (shouldReturn && i == last)
580+
if (shouldReturn && i == last && expr.NodeType != ExpressionType.Throw)
578581
Write("return ");
579582
next = Visit(expr);
580583
Write(";");
@@ -1093,7 +1096,7 @@ private ParameterExpression VisitParameterDeclaration(ParameterExpression node)
10931096
private void WriteModifier(bool isPublic)
10941097
{
10951098
WriteNextLine(isPublic ? "public " : "private ");
1096-
if (_definitions?.IsStatic == true)
1099+
if (Definitions?.IsStatic == true)
10971100
Write("static ");
10981101
}
10991102

@@ -1102,7 +1105,7 @@ private Expression VisitLambda(LambdaExpression node, LambdaType type)
11021105
if (type == LambdaType.Function || type == LambdaType.Main)
11031106
{
11041107
var name = type == LambdaType.Main
1105-
? (_definitions?.MethodName ?? "Main")
1108+
? (Definitions?.MethodName ?? "Main")
11061109
: GetName(node);
11071110
WriteModifier(type == LambdaType.Main);
11081111
Write(Translate(node.ReturnType), " ", name);
@@ -1325,6 +1328,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
13251328
{
13261329
obj = VisitGroup(node.Object, node.NodeType);
13271330
}
1331+
#if !NET40
13281332
else if (!node.Method.IsPublic || node.Method.DeclaringType?.GetTypeInfo().IsNotPublic == true)
13291333
{
13301334
isNotPublic = true;
@@ -1339,6 +1343,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
13391343
var func = node.Method.CreateDelegate(del);
13401344
Write(GetConstant(func, GetVarName(node.Method.Name)), ".Invoke");
13411345
}
1346+
#endif
13421347
else if (node.Method.GetCustomAttribute<ExtensionAttribute>() != null)
13431348
{
13441349
isExtension = true;
@@ -1749,14 +1754,14 @@ public override string ToString()
17491754
_writer = codeWriter;
17501755

17511756
//exercise to update _usings
1752-
var implements = _definitions?.Implements?.OrderBy(it => it.GetTypeInfo().IsInterface ? 1 : 0)
1757+
var implements = Definitions?.Implements?.OrderBy(it => it.GetTypeInfo().IsInterface ? 1 : 0)
17531758
.Select(Translate)
17541759
.ToList();
17551760
var constants = _constants?.OrderBy(it => it.Value)
17561761
.Select(kvp => $"{Translate(kvp.Key.GetType())} {kvp.Value};")
17571762
.ToList();
17581763

1759-
if (_definitions?.TypeName != null)
1764+
if (Definitions?.TypeName != null)
17601765
{
17611766
if (_usings != null)
17621767
{
@@ -1780,14 +1785,14 @@ public override string ToString()
17801785
}
17811786
WriteLine();
17821787
}
1783-
if (_definitions?.Namespace != null)
1788+
if (Definitions?.Namespace != null)
17841789
{
1785-
WriteNextLine("namespace ", _definitions.Namespace);
1790+
WriteNextLine("namespace ", Definitions.Namespace);
17861791
Indent();
17871792
}
17881793

17891794
WriteModifier(true);
1790-
Write("class ", _definitions.TypeName);
1795+
Write("class ", Definitions.TypeName);
17911796
if (implements?.Any() == true)
17921797
{
17931798
Write(" : ", string.Join(", ", implements));
@@ -1806,10 +1811,10 @@ public override string ToString()
18061811
_writer.Write(temp);
18071812
if (_appendWriter != null)
18081813
_writer.Write(_appendWriter);
1809-
if (_definitions?.TypeName != null)
1814+
if (Definitions?.TypeName != null)
18101815
{
18111816
Outdent();
1812-
if (_definitions?.Namespace != null)
1817+
if (Definitions?.Namespace != null)
18131818
Outdent();
18141819
}
18151820
return _writer.ToString();

ExpressionTranslator/ExpressionTranslatorExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ public static class ExpressionTranslatorExtensions
1212
/// <returns>Script text</returns>
1313
public static string ToScript(this Expression node, ExpressionDefinitions definitions = null)
1414
{
15-
var translator = new ExpressionTranslator(definitions);
16-
return translator.Translate(node);
15+
var translator = ExpressionTranslator.Create(node, definitions);
16+
return translator.ToString();
1717
}
1818
}
1919
}

0 commit comments

Comments
 (0)