Browse Source

Added .NET 3 polyfill for Expression.Block, Expression.Variable, and Expression.Assign

pull/46/head
Anairkoen Schno 5 years ago
parent
commit
93746f9492
9 changed files with 686 additions and 159 deletions
  1. +1
    -1
      IPA.Loader/IPA.Loader.csproj
  2. +19
    -16
      IPA.Loader/Loader/PluginExecutor.cs
  3. +9
    -5
      IPA.Loader/Loader/PluginInitInjector.cs
  4. +386
    -0
      Net3-Proxy/ExpressionEx.cs
  5. +6
    -0
      Net3-Proxy/Extensions.cs
  6. +63
    -58
      Net3-Proxy/Net3-Proxy.csproj
  7. +93
    -53
      Net3-Proxy/Tuple.cs
  8. +60
    -0
      Net3-Proxy/TypeUtils.cs
  9. +49
    -26
      Net3-Proxy/Utils.cs

+ 1
- 1
IPA.Loader/IPA.Loader.csproj View File

@ -4,7 +4,7 @@
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">Net4</Platform> <Platform Condition=" '$(Platform)' == '' ">Net4</Platform>
<BuildForBeatSaber Condition=" '$(BuildForBeatSaber)' == '' ">true</BuildForBeatSaber>
<BuildForBeatSaber Condition=" '$(BuildForBeatSaber)' == '' And '$(Platform)' == 'Net4' ">true</BuildForBeatSaber>
<ProjectGuid>{5AD344F0-01A0-4CA8-92E5-9D095737744D}</ProjectGuid> <ProjectGuid>{5AD344F0-01A0-4CA8-92E5-9D095737744D}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>


+ 19
- 16
IPA.Loader/Loader/PluginExecutor.cs View File

@ -8,6 +8,8 @@ using System.Linq.Expressions;
#if NET4 #if NET4
using Task = System.Threading.Tasks.Task; using Task = System.Threading.Tasks.Task;
using TaskEx = System.Threading.Tasks.Task; using TaskEx = System.Threading.Tasks.Task;
using Expression = System.Linq.Expressions.Expression;
using ExpressionEx = System.Linq.Expressions.Expression;
#endif #endif
#if NET3 #if NET3
using System.Threading.Tasks; using System.Threading.Tasks;
@ -19,6 +21,7 @@ using Directory = Net3_Proxy.Directory;
namespace IPA.Loader namespace IPA.Loader
{ {
// NOTE: TaskEx.WhenAll() (Task.WhenAll() in .NET 4) returns CompletedTask if it has no arguments, which we need for .NET 3
internal class PluginExecutor internal class PluginExecutor
{ {
public PluginMetadata Metadata { get; } public PluginMetadata Metadata { get; }
@ -29,7 +32,7 @@ namespace IPA.Loader
{ {
CreatePlugin = m => null; CreatePlugin = m => null;
LifecycleEnable = o => { }; LifecycleEnable = o => { };
LifecycleDisable = o => TaskEx.CompletedTask;
LifecycleDisable = o => TaskEx.WhenAll();
} }
else else
PrepareDelegates(); PrepareDelegates();
@ -95,13 +98,13 @@ namespace IPA.Loader
// TODO: how do I make this work for .NET 3? FEC.LightExpression but hacked to work on .NET 3? // TODO: how do I make this work for .NET 3? FEC.LightExpression but hacked to work on .NET 3?
var metaParam = Expression.Parameter(typeof(PluginMetadata), "meta"); var metaParam = Expression.Parameter(typeof(PluginMetadata), "meta");
var objVar = Expression.Variable(type, "objVar");
var persistVar = Expression.Variable(typeof(object), "persistVar");
var objVar = ExpressionEx.Variable(type, "objVar");
var persistVar = ExpressionEx.Variable(typeof(object), "persistVar");
var createExpr = Expression.Lambda<Func<PluginMetadata, object>>( var createExpr = Expression.Lambda<Func<PluginMetadata, object>>(
Expression.Block(new[] { objVar, persistVar },
ExpressionEx.Block(new[] { objVar, persistVar },
initMethods initMethods
.Select(m => PluginInitInjector.InjectedCallExpr(m.GetParameters(), metaParam, persistVar, es => Expression.Call(objVar, m, es))) .Select(m => PluginInitInjector.InjectedCallExpr(m.GetParameters(), metaParam, persistVar, es => Expression.Call(objVar, m, es)))
.Prepend(Expression.Assign(objVar,
.Prepend(ExpressionEx.Assign(objVar,
usingDefaultCtor usingDefaultCtor
? Expression.New(ctor) ? Expression.New(ctor)
: PluginInitInjector.InjectedCallExpr(ctor.GetParameters(), metaParam, persistVar, es => Expression.New(ctor, es)))) : PluginInitInjector.InjectedCallExpr(ctor.GetParameters(), metaParam, persistVar, es => Expression.New(ctor, es))))
@ -133,12 +136,12 @@ namespace IPA.Loader
} }
var objParam = Expression.Parameter(typeof(object), "obj"); var objParam = Expression.Parameter(typeof(object), "obj");
var instVar = Expression.Variable(type, "inst");
var instVar = ExpressionEx.Variable(type, "inst");
var createExpr = Expression.Lambda<Action<object>>( var createExpr = Expression.Lambda<Action<object>>(
Expression.Block(new[] { instVar },
ExpressionEx.Block(new[] { instVar },
enableMethods enableMethods
.Select(m => Expression.Call(instVar, m))
.Prepend<Expression>(Expression.Assign(instVar, Expression.Convert(objParam, type)))),
.Select(m => (Expression)Expression.Call(instVar, m))
.Prepend(ExpressionEx.Assign(instVar, Expression.Convert(objParam, type)))),
objParam); objParam);
return createExpr.Compile(); return createExpr.Compile();
} }
@ -152,7 +155,7 @@ namespace IPA.Loader
if (disableMethods.Length == 0) if (disableMethods.Length == 0)
{ {
Logger.loader.Notice($"Plugin {name} has no methods marked [OnExit] or [OnDisable]. Is this intentional?"); Logger.loader.Notice($"Plugin {name} has no methods marked [OnExit] or [OnDisable]. Is this intentional?");
return o => Task.CompletedTask;
return o => TaskEx.WhenAll();
} }
var taskMethods = new List<MethodInfo>(); var taskMethods = new List<MethodInfo>();
@ -175,24 +178,24 @@ namespace IPA.Loader
nonTaskMethods.Add(m); nonTaskMethods.Add(m);
} }
Expression<Func<Task>> completedTaskDel = () => TaskEx.CompletedTask;
Expression<Func<Task>> completedTaskDel = () => TaskEx.WhenAll();
var getCompletedTask = completedTaskDel.Body; var getCompletedTask = completedTaskDel.Body;
var taskWhenAll = typeof(TaskEx).GetMethod(nameof(TaskEx.WhenAll), new[] { typeof(Task[]) }); var taskWhenAll = typeof(TaskEx).GetMethod(nameof(TaskEx.WhenAll), new[] { typeof(Task[]) });
var objParam = Expression.Parameter(typeof(object), "obj"); var objParam = Expression.Parameter(typeof(object), "obj");
var instVar = Expression.Variable(type, "inst");
var instVar = ExpressionEx.Variable(type, "inst");
var createExpr = Expression.Lambda<Func<object, Task>>( var createExpr = Expression.Lambda<Func<object, Task>>(
Expression.Block(new[] { instVar },
ExpressionEx.Block(new[] { instVar },
nonTaskMethods nonTaskMethods
.Select(m => Expression.Call(instVar, m))
.Prepend<Expression>(Expression.Assign(instVar, Expression.Convert(objParam, type)))
.Select(m => (Expression)Expression.Call(instVar, m))
.Prepend(ExpressionEx.Assign(instVar, Expression.Convert(objParam, type)))
.Append( .Append(
taskMethods.Count == 0 taskMethods.Count == 0
? getCompletedTask ? getCompletedTask
: Expression.Call(taskWhenAll, : Expression.Call(taskWhenAll,
Expression.NewArrayInit(typeof(Task), Expression.NewArrayInit(typeof(Task),
taskMethods.Select(m => taskMethods.Select(m =>
Expression.Convert(Expression.Call(instVar, m), typeof(Task))))))),
(Expression)Expression.Convert(Expression.Call(instVar, m), typeof(Task))))))),
objParam); objParam);
return createExpr.Compile(); return createExpr.Compile();
} }


+ 9
- 5
IPA.Loader/Loader/PluginInitInjector.cs View File

@ -6,6 +6,10 @@ using IPA.Config;
using IPA.Logging; using IPA.Logging;
using IPA.Utilities; using IPA.Utilities;
using System.Linq.Expressions; using System.Linq.Expressions;
#if NET4
using Expression = System.Linq.Expressions.Expression;
using ExpressionEx = System.Linq.Expressions.Expression;
#endif
#if NET3 #if NET3
using Net3_Proxy; using Net3_Proxy;
#endif #endif
@ -124,14 +128,14 @@ namespace IPA.Loader
} }
private static readonly MethodInfo InjectMethod = typeof(PluginInitInjector).GetMethod(nameof(Inject), BindingFlags.NonPublic | BindingFlags.Static); private static readonly MethodInfo InjectMethod = typeof(PluginInitInjector).GetMethod(nameof(Inject), BindingFlags.NonPublic | BindingFlags.Static);
internal static Expression InjectedCallExpr(ParameterInfo[] initParams, Expression meta, ParameterExpression persistVar, Func<IEnumerable<Expression>, Expression> exprGen)
internal static Expression InjectedCallExpr(ParameterInfo[] initParams, Expression meta, Expression persistVar, Func<IEnumerable<Expression>, Expression> exprGen)
{ {
var arr = Expression.Variable(typeof(object[]), "initArr");
return Expression.Block(new[] { arr },
Expression.Assign(arr, Expression.Call(InjectMethod, Expression.Constant(initParams), meta, persistVar)),
var arr = ExpressionEx.Variable(typeof(object[]), "initArr");
return ExpressionEx.Block(new[] { arr },
ExpressionEx.Assign(arr, Expression.Call(InjectMethod, Expression.Constant(initParams), meta, persistVar)),
exprGen(initParams exprGen(initParams
.Select(p => p.ParameterType) .Select(p => p.ParameterType)
.Select((t, i) => Expression.Convert(
.Select((t, i) => (Expression)Expression.Convert(
Expression.ArrayIndex(arr, Expression.Constant(i)), t)))); Expression.ArrayIndex(arr, Expression.Constant(i)), t))));
} }


+ 386
- 0
Net3-Proxy/ExpressionEx.cs View File

@ -0,0 +1,386 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
namespace Net3_Proxy
{
public static class ExpressionEx
{
internal static ExpressionType ExprT(this ExpressionTypeEx ex)
=> (ExpressionType)ex;
internal static ExpressionTypeEx ExprTEx(this ExpressionType ex)
=> (ExpressionTypeEx)ex;
private static void Validate(Type type, bool allowByRef)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
TypeUtils.ValidateType(type, nameof(type), allowByRef, false);
if (type == typeof(void))
throw new ArgumentException("Argument cannot be of type void", nameof(type));
}
private static void RequiresCanWrite(Expression expression, string paramName)
{
if (expression == null)
{
throw new ArgumentNullException(paramName);
}
ExpressionType nodeType = expression.NodeType;
if (nodeType != ExpressionType.MemberAccess)
{
if (nodeType == ExpressionType.Parameter)
return;
}
else
{
MemberInfo member = ((MemberExpression)expression).Member;
if (member is PropertyInfo propertyInfo)
{
if (propertyInfo.CanWrite)
{
return;
}
}
else
{
FieldInfo fieldInfo = (FieldInfo)member;
if (!fieldInfo.IsInitOnly && !fieldInfo.IsLiteral)
{
return;
}
}
}
throw new ArgumentException("Expression must be writeable", paramName);
}
public static void RequiresCanRead(Expression expression, string paramName, int idx = -1)
{
if (expression == null)
throw new ArgumentNullException(TypeUtils.GetParamName(paramName, idx));
ExpressionType nodeType = expression.NodeType;
if (nodeType == ExpressionType.MemberAccess)
{
if (((MemberExpression)expression).Member is PropertyInfo propertyInfo && !propertyInfo.CanRead)
{
throw new ArgumentException("Expression must be readable", TypeUtils.GetParamName(paramName, idx));
}
}
}
public static VariableExpression Variable(Type type, string name = null)
{
Validate(type, false);
return new VariableExpression(type, name);
}
public static AssignExpression Assign(Expression left, Expression right)
{
RequiresCanWrite(left, nameof(left));
RequiresCanRead(right, nameof(right));
TypeUtils.ValidateType(left.Type, nameof(left), true, true);
TypeUtils.ValidateType(right.Type, nameof(right), true, true);
if (!TypeUtils.AreReferenceAssignable(left.Type, right.Type))
throw new ArgumentException($"Expression of type '{left.Type}' cannot be used for assignment to type '{right.Type}'");
if (left.NodeType.ExprTEx() != ExpressionTypeEx.Variable)
throw new NotSupportedException("Non-variable left hand operands to assignment is currently not supported");
return new AssignExpression(left, right);
}
public static Expression Block(IEnumerable<VariableExpression> vars, params Expression[] body) => Block(vars, body);
public static Expression Block(IEnumerable<VariableExpression> vars, IEnumerable<Expression> body)
{ // this is probably terrible performance-wise when executing, but i'm not giving up BlockExpression damnit!
var varSet = new HashSet<VariableExpression>(vars);
var bodyArr = body.ToArray();
var finalExprList = new Stack<(List<Expression> list, BlockParseStackInfo info)>();
var remaining = bodyArr.Length;
var varParams = new Dictionary<VariableExpression, ParameterExpression>(varSet.Count);
finalExprList.Push((new List<Expression>(remaining), default(BlockParseStackInfo)));
while (remaining > 0)
{
var (list, info) = finalExprList.Pop();
var targetExpr = bodyArr[bodyArr.Length - remaining--];
if (targetExpr.NodeType.ExprTEx() == ExpressionTypeEx.Assign)
{
var assign = (AssignExpression)targetExpr;
var left = (VariableExpression)assign.Left;
var right = assign.Right;
var param = Expression.Parameter(left.Type, left.Name);
finalExprList.Push((list, info));
finalExprList.Push((new List<Expression>(remaining), new BlockParseStackInfo
{
ValueExpr = BlockVisitReplaceVariables(right, varParams), Param = param
}));
varParams.Add(left, param);
continue;
}
list.Add(BlockVisitReplaceVariables(targetExpr, varParams));
finalExprList.Push((list, info));
}
var funcType = typeof(Func<>);
Expression topExpr = null;
while (finalExprList.Count > 0)
{
var (list, info) = finalExprList.Pop();
// there is an optimization opportunity for consecutive assignments, but it needs to make sure they don't depend on each other
if (topExpr != null) list.Add(topExpr);
Expression last = null;
Type lastType = null;
var rest = new List<Expression<Action>>(list.Count);
for (int i = 0; i < list.Count; i++)
{
var expr = list[i];
if (i + 1 == list.Count)
{
var ty = expr.Type;
if (ty == typeof(void))
rest.Add(Expression.Lambda<Action>(expr));
else
{
lastType = ty;
var func = funcType.MakeGenericType(ty);
last = Expression.Lambda(func, expr);
}
}
else
rest.Add(Expression.Lambda<Action>(expr));
}
Expression topBody;
if (lastType != null)
{
var execSeq = ExecuteSequenceTyped.MakeGenericMethod(lastType);
topBody = Expression.Call(null, execSeq, last, Expression.NewArrayInit(typeof(Action), rest.Cast<Expression>()));
}
else
topBody = Expression.Call(null, ExecuteSequenceVoid, Expression.NewArrayInit(typeof(Action), rest.Cast<Expression>()));
if (info.Param != null && info.ValueExpr != null)
topExpr = Expression.Invoke(Expression.Lambda(topBody, info.Param), info.ValueExpr);
else
topExpr = topBody;
}
return topExpr;
}
/*
* {
* Console.WriteLine("ho");
* int i = 3;
* Console.WriteLine(i);
* i // return last
* }
*
* ExecuteSequence<int>(
* () => (i =>
* ExecuteSequence<int>(
* () => i,
* () => Console.WriteLine(i)
* )
* )(3),
* () => Console.WriteLine("ho")
* )
*/
private struct BlockParseStackInfo
{
public Expression ValueExpr;
public ParameterExpression Param;
}
private static Expression BlockVisitReplaceVariables(Expression expr, Dictionary<VariableExpression, ParameterExpression> mapping)
{
var binary = expr as BinaryExpression;
var unary = expr as UnaryExpression;
switch (expr.NodeType)
{
case ExpressionType.Add:
return Expression.Add(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.AddChecked:
return Expression.AddChecked(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.And:
return Expression.And(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.AndAlso:
return Expression.AndAlso(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.ArrayIndex:
return Expression.ArrayIndex(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Coalesce:
return Expression.Coalesce(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Divide:
return Expression.Divide(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Equal:
return Expression.Equal(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.ExclusiveOr:
return Expression.ExclusiveOr(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.GreaterThan:
return Expression.GreaterThan(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.GreaterThanOrEqual:
return Expression.GreaterThanOrEqual(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.LeftShift:
return Expression.LeftShift(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.LessThan:
return Expression.LessThan(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.LessThanOrEqual:
return Expression.LessThanOrEqual(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Modulo:
return Expression.Modulo(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Multiply:
return Expression.Multiply(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.MultiplyChecked:
return Expression.MultiplyChecked(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.NotEqual:
return Expression.NotEqual(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Or:
return Expression.Or(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.OrElse:
return Expression.OrElse(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Power:
return Expression.Power(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.RightShift:
return Expression.RightShift(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.Subtract:
return Expression.Subtract(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.SubtractChecked:
return Expression.SubtractChecked(BlockVisitReplaceVariables(binary.Left, mapping), BlockVisitReplaceVariables(binary.Right, mapping));
case ExpressionType.ArrayLength:
return Expression.ArrayLength(BlockVisitReplaceVariables(unary.Operand, mapping));
case ExpressionType.Negate:
return Expression.Negate(BlockVisitReplaceVariables(unary.Operand, mapping));
case ExpressionType.UnaryPlus:
return Expression.UnaryPlus(BlockVisitReplaceVariables(unary.Operand, mapping));
case ExpressionType.NegateChecked:
return Expression.NegateChecked(BlockVisitReplaceVariables(unary.Operand, mapping));
case ExpressionType.Not:
return Expression.Not(BlockVisitReplaceVariables(unary.Operand, mapping));
case ExpressionType.TypeAs:
return Expression.TypeAs(BlockVisitReplaceVariables(unary.Operand, mapping), unary.Type);
case ExpressionType.Call:
var callExpr = expr as MethodCallExpression;
return Expression.Call(BlockVisitReplaceVariables(callExpr.Object, mapping),
callExpr.Method, callExpr.Arguments.Select(a => BlockVisitReplaceVariables(a, mapping)));
case ExpressionType.Conditional:
var condExpr = expr as ConditionalExpression;
return Expression.Condition(BlockVisitReplaceVariables(condExpr.Test, mapping),
BlockVisitReplaceVariables(condExpr.IfTrue, mapping), BlockVisitReplaceVariables(condExpr.IfFalse, mapping));
case ExpressionType.Constant: return expr; // constants should be unchanged
case ExpressionType.Convert:
return Expression.Convert(BlockVisitReplaceVariables(unary.Operand, mapping), unary.Type, unary.Method);
case ExpressionType.ConvertChecked:
return Expression.ConvertChecked(BlockVisitReplaceVariables(unary.Operand, mapping), unary.Type, unary.Method);
case ExpressionType.Invoke:
var invokeExpr = expr as InvocationExpression;
return Expression.Invoke(BlockVisitReplaceVariables(invokeExpr.Expression, mapping),
invokeExpr.Arguments.Select(e => BlockVisitReplaceVariables(e, mapping)));
case ExpressionType.Lambda:
var lambdaExpr = expr as LambdaExpression;
return Expression.Lambda(lambdaExpr.Type, BlockVisitReplaceVariables(lambdaExpr.Body, mapping), lambdaExpr.Parameters);
case ExpressionType.ListInit:
var listInitExpr = expr as ListInitExpression;
return Expression.ListInit((NewExpression)BlockVisitReplaceVariables(listInitExpr.NewExpression, mapping),
listInitExpr.Initializers.Select(i => i.AddMethod).First(),
listInitExpr.Initializers.SelectMany(i => i.Arguments)
.Select(e => BlockVisitReplaceVariables(e, mapping)));
case ExpressionType.MemberAccess:
var memberExpr = expr as MemberExpression;
return Expression.MakeMemberAccess(BlockVisitReplaceVariables(memberExpr.Expression, mapping), memberExpr.Member);
case ExpressionType.MemberInit:
var memberInitExpr = expr as MemberInitExpression;
return Expression.MemberInit((NewExpression)BlockVisitReplaceVariables(memberInitExpr.NewExpression, mapping),
memberInitExpr.Bindings);
case ExpressionType.New:
var newExpr = expr as NewExpression;
return Expression.New(newExpr.Constructor,
newExpr.Arguments.Select(e => BlockVisitReplaceVariables(e, mapping)),
newExpr.Members);
case ExpressionType.NewArrayInit:
var newArrayInitExpr = expr as NewArrayExpression;
return Expression.NewArrayInit(newArrayInitExpr.Type,
newArrayInitExpr.Expressions.Select(e => BlockVisitReplaceVariables(e, mapping)));
case ExpressionType.NewArrayBounds:
var newArrayBoundsExpr = expr as NewArrayExpression;
return Expression.NewArrayBounds(newArrayBoundsExpr.Type,
newArrayBoundsExpr.Expressions.Select(e => BlockVisitReplaceVariables(e, mapping)));
case ExpressionType.Parameter: return expr; // like constant
case ExpressionType.Quote:
return Expression.Quote(BlockVisitReplaceVariables(unary.Operand, mapping));
case ExpressionType.TypeIs:
var typeIsExpr = expr as TypeBinaryExpression;
return Expression.TypeIs(BlockVisitReplaceVariables(typeIsExpr.Expression, mapping), typeIsExpr.TypeOperand);
default:
switch (expr.NodeType.ExprTEx())
{
case ExpressionTypeEx.Variable:
var varExpr = expr as VariableExpression;
if (mapping.TryGetValue(varExpr, out var paramExpr))
return paramExpr;
else
return varExpr; // not in scope in the current context, might be later
case ExpressionTypeEx.Assign:
throw new InvalidOperationException("Assign expression must appear directly inside a block expression");
default:
throw new ArgumentException($"Unhandled expression type '{expr.NodeType}'");
}
}
}
private static readonly MethodInfo ExecuteSequenceVoid = typeof(ExpressionEx).GetMethod(nameof(ExecuteSequence),
BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Action[]) }, null);
private static void ExecuteSequence(params Action[] exec)
{
foreach (var act in exec) act();
}
private static readonly MethodInfo ExecuteSequenceTyped = typeof(ExpressionEx).GetMethod(nameof(ExecuteSequence),
BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Func<>), typeof(Action[]) }, null);
private static T ExecuteSequence<T>(Func<T> last, params Action[] first)
{
ExecuteSequence(first);
return last();
}
}
internal enum ExpressionTypeEx
{
Variable = 46,
Assign = 47
}
public class VariableExpression : Expression, IEquatable<VariableExpression>
{
public string Name { get; }
internal VariableExpression(Type varType, string name) : base(ExpressionTypeEx.Variable.ExprT(), varType)
=> Name = name;
public bool Equals(VariableExpression other)
=> Name == other.Name && Type == other.Type;
}
public class AssignExpression : Expression
{
public Expression Left { get; }
public Expression Right { get; }
internal AssignExpression(Expression left, Expression right) : base(ExpressionTypeEx.Assign.ExprT(), left.Type)
{
Left = left;
Right = right;
}
}
}

+ 6
- 0
Net3-Proxy/Extensions.cs View File

@ -15,9 +15,15 @@ namespace Net3_Proxy
{ {
public static T GetCustomAttribute<T>(this ParameterInfo element) where T : Attribute public static T GetCustomAttribute<T>(this ParameterInfo element) where T : Attribute
=> (T)GetCustomAttribute(element, typeof(T)); => (T)GetCustomAttribute(element, typeof(T));
public static T GetCustomAttribute<T>(this MethodInfo element) where T : Attribute
=> (T)GetCustomAttribute(element, typeof(T));
public static T GetCustomAttribute<T>(this ConstructorInfo element) where T : Attribute
=> (T)GetCustomAttribute(element, typeof(T));
public static Attribute GetCustomAttribute(this MemberInfo element, Type attributeType) public static Attribute GetCustomAttribute(this MemberInfo element, Type attributeType)
=> Attribute.GetCustomAttribute(element, attributeType); => Attribute.GetCustomAttribute(element, attributeType);
public static Attribute GetCustomAttribute(this ConstructorInfo element, Type attributeType)
=> Attribute.GetCustomAttribute(element, attributeType);
public static Attribute GetCustomAttribute(this ParameterInfo element, Type attributeType) public static Attribute GetCustomAttribute(this ParameterInfo element, Type attributeType)
=> Attribute.GetCustomAttribute(element, attributeType); => Attribute.GetCustomAttribute(element, attributeType);


+ 63
- 58
Net3-Proxy/Net3-Proxy.csproj View File

@ -1,59 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{642F52DA-90F9-40E3-8784-6964F36752FB}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Net3_Proxy</RootNamespace>
<AssemblyName>Net3-Proxy</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Array.cs" />
<Compile Include="CompilerServices.cs" />
<Compile Include="Directory.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="File.cs" />
<Compile Include="IReadOnlyList.cs" />
<Compile Include="Path.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tuple.cs" />
<Compile Include="Utils.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncBridge">
<Version>0.3.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{642F52DA-90F9-40E3-8784-6964F36752FB}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Net3_Proxy</RootNamespace>
<AssemblyName>Net3-Proxy</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Array.cs" />
<Compile Include="CompilerServices.cs" />
<Compile Include="Directory.cs" />
<Compile Include="ExpressionEx.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="File.cs" />
<Compile Include="IReadOnlyList.cs" />
<Compile Include="Path.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tuple.cs" />
<Compile Include="TypeUtils.cs" />
<Compile Include="Utils.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncBridge">
<Version>0.3.1</Version>
</PackageReference>
<PackageReference Include="Portable.System.ValueTuple">
<Version>1.0.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

+ 93
- 53
Net3-Proxy/Tuple.cs View File

@ -1,53 +1,93 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Net3_Proxy
{
public static class Tuple
{
public static Tuple<T1, T2> Create<T1, T2>(T1 i1, T2 i2)
=> new Tuple<T1, T2>(i1, i2);
internal static int CombineHashCodes(int h1, int h2)
{
return (h1 << 5) + h1 ^ h2;
}
}
[Serializable]
public class Tuple<T1, T2> : IComparable
{
public T1 Item1 { get; private set; }
public T2 Item2 { get; private set; }
public Tuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
public override bool Equals(object obj)
=> obj is Tuple<T1, T2> tup
&& Equals(Item1, tup.Item1)
&& Equals(Item2, tup.Item2);
int IComparable.CompareTo(object obj)
{
if (obj == null) return 1;
var tup = obj as Tuple<T1, T2>;
if (tup == null) throw new ArgumentException($"Argument must be of type {GetType()}.", "other");
int num = Comparer<T1>.Default.Compare(Item1, tup.Item1);
if (num != 0) return num;
return Comparer<T2>.Default.Compare(Item2, tup.Item2);
}
public override int GetHashCode() =>
Tuple.CombineHashCodes(EqualityComparer<T1>.Default.GetHashCode(Item1), EqualityComparer<T2>.Default.GetHashCode(Item2));
public override string ToString() =>
$"({Item1}, {Item2})";
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Net3_Proxy
{
public static class Tuple
{
public static Tuple<T1, T2> Create<T1, T2>(T1 i1, T2 i2)
=> new Tuple<T1, T2>(i1, i2);
internal static int CombineHashCodes(int h1, int h2)
{
return (h1 << 5) + h1 ^ h2;
}
}
[Serializable]
public class Tuple<T1, T2> : IComparable, IComparable<Tuple<T1, T2>>
{
public T1 Item1 { get; private set; }
public T2 Item2 { get; private set; }
public Tuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
public override bool Equals(object obj)
=> obj is Tuple<T1, T2> tup
&& Equals(Item1, tup.Item1)
&& Equals(Item2, tup.Item2);
int IComparable.CompareTo(object obj)
{
if (obj == null) return 1;
var tup = obj as Tuple<T1, T2>;
if (tup == null) throw new ArgumentException($"Argument must be of type {GetType()}.", "other");
return CompareTo(tup);
}
public override int GetHashCode() =>
Tuple.CombineHashCodes(EqualityComparer<T1>.Default.GetHashCode(Item1), EqualityComparer<T2>.Default.GetHashCode(Item2));
public override string ToString() =>
$"({Item1}, {Item2})";
public int CompareTo(Tuple<T1, T2> tup)
{
int num = Comparer<T1>.Default.Compare(Item1, tup.Item1);
if (num != 0) return num;
return Comparer<T2>.Default.Compare(Item2, tup.Item2);
}
public static bool operator ==(Tuple<T1, T2> left, Tuple<T1, T2> right)
{
if (ReferenceEquals(left, null))
{
return ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Tuple<T1, T2> left, Tuple<T1, T2> right)
{
return !(left == right);
}
public static bool operator <(Tuple<T1, T2> left, Tuple<T1, T2> right)
{
return ReferenceEquals(left, null) ? !ReferenceEquals(right, null) : left.CompareTo(right) < 0;
}
public static bool operator <=(Tuple<T1, T2> left, Tuple<T1, T2> right)
{
return ReferenceEquals(left, null) || left.CompareTo(right) <= 0;
}
public static bool operator >(Tuple<T1, T2> left, Tuple<T1, T2> right)
{
return !ReferenceEquals(left, null) && left.CompareTo(right) > 0;
}
public static bool operator >=(Tuple<T1, T2> left, Tuple<T1, T2> right)
{
return ReferenceEquals(left, null) ? ReferenceEquals(right, null) : left.CompareTo(right) >= 0;
}
}
}

+ 60
- 0
Net3-Proxy/TypeUtils.cs View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Net3_Proxy
{
internal static class TypeUtils
{
public static void ValidateType(Type type, string paramName)
=> ValidateType(type, paramName, false, false);
// Token: 0x0600197D RID: 6525 RVA: 0x00053C07 File Offset: 0x00051E07
public static void ValidateType(Type type, string paramName, bool allowByRef, bool allowPointer)
{
if (ValidateType(type, paramName, -1))
{
if (!allowByRef && type.IsByRef)
{
throw new ArgumentException("Type must not be ref", paramName);
}
if (!allowPointer && type.IsPointer)
{
throw new ArgumentException("Type must not be pointer", paramName);
}
}
}
// Token: 0x0600197E RID: 6526 RVA: 0x00053C37 File Offset: 0x00051E37
public static bool ValidateType(Type type, string paramName, int index)
{
if (type == typeof(void))
return false;
if (type.ContainsGenericParameters)
throw type.IsGenericTypeDefinition
? new ArgumentException($"Type {type} is a generic type definition", GetParamName(paramName, index))
: new ArgumentException($"Type {type} contains generic parameters", GetParamName(paramName, index));
return true;
}
public static string GetParamName(string paramName, int index)
{
if (index >= 0)
{
return string.Format("{0}[{1}]", paramName, index);
}
return paramName;
}
public static bool AreEquivalent(Type t1, Type t2)
{
return t1 != null && t1 == t2;
}
public static bool AreReferenceAssignable(Type dest, Type src)
{
return TypeUtils.AreEquivalent(dest, src) || (!dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src));
}
}
}

+ 49
- 26
Net3-Proxy/Utils.cs View File

@ -1,26 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Net3_Proxy
{
internal static class Utils
{
public static bool IsNullOrWhiteSpace(string value)
{
if (value == null)
{
return true;
}
for (int i = 0; i < value.Length; i++)
{
if (!char.IsWhiteSpace(value[i]))
{
return false;
}
}
return true;
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Net3_Proxy
{
internal static class Utils
{
public static bool IsNullOrWhiteSpace(string value)
{
if (value == null)
{
return true;
}
for (int i = 0; i < value.Length; i++)
{
if (!char.IsWhiteSpace(value[i]))
{
return false;
}
}
return true;
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> seq, T prep)
=> new PrependEnumerable<T>(seq, prep);
private sealed class PrependEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> rest;
private readonly T first;
public PrependEnumerable(IEnumerable<T> rest, T first)
{
this.rest = rest;
this.first = first;
}
public IEnumerator<T> GetEnumerator()
{ // TODO: a custom impl that is less garbage
yield return first;
foreach (var v in rest) yield return v;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
}

Loading…
Cancel
Save