aspclassic-core/AspClassic.Scripting/Runtime/DynamicOperations.cs
Jelle Luteijn 484dbfc9d9 progress
2022-05-15 11:19:49 +02:00

514 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using AspClassic.Scripting.Utils;
namespace AspClassic.Scripting.Runtime;
public sealed class DynamicOperations
{
private class SiteKey : IEquatable<SiteKey>
{
internal readonly CallSiteBinder SiteBinder;
private readonly Type _siteType;
public int HitCount;
public CallSite Site;
public SiteKey(Type siteType, CallSiteBinder siteBinder)
{
SiteBinder = siteBinder;
_siteType = siteType;
}
public override bool Equals(object obj)
{
return Equals(obj as SiteKey);
}
public override int GetHashCode()
{
return SiteBinder.GetHashCode() ^ _siteType.GetHashCode();
}
public bool Equals(SiteKey other)
{
if (other == null)
{
return false;
}
if (other.SiteBinder.Equals(SiteBinder))
{
return other._siteType == _siteType;
}
return false;
}
}
private const int CleanupThreshold = 20;
private const int RemoveThreshold = 2;
private const int StopCleanupThreshold = 10;
private const int ClearThreshold = 50;
private readonly LanguageContext _lc;
private Dictionary<SiteKey, SiteKey> _sites = new Dictionary<SiteKey, SiteKey>();
private int LastCleanup;
private int SitesCreated;
private Dictionary<int, Func<DynamicOperations, CallSiteBinder, object, object[], object>> _invokers = new Dictionary<int, Func<DynamicOperations, CallSiteBinder, object, object[], object>>();
public DynamicOperations(LanguageContext lc)
{
ContractUtils.RequiresNotNull(lc, "lc");
_lc = lc;
}
public object Invoke(object obj, params object[] parameters)
{
return GetInvoker(parameters.Length)(this, _lc.CreateInvokeBinder(new CallInfo(parameters.Length)), obj, parameters);
}
public object InvokeMember(object obj, string memberName, params object[] parameters)
{
return InvokeMember(obj, memberName, ignoreCase: false, parameters);
}
public object InvokeMember(object obj, string memberName, bool ignoreCase, params object[] parameters)
{
return GetInvoker(parameters.Length)(this, _lc.CreateCallBinder(memberName, ignoreCase, new CallInfo(parameters.Length)), obj, parameters);
}
public object CreateInstance(object obj, params object[] parameters)
{
return GetInvoker(parameters.Length)(this, _lc.CreateCreateBinder(new CallInfo(parameters.Length)), obj, parameters);
}
public object GetMember(object obj, string name)
{
return GetMember(obj, name, ignoreCase: false);
}
public T GetMember<T>(object obj, string name)
{
return GetMember<T>(obj, name, ignoreCase: false);
}
public bool TryGetMember(object obj, string name, out object value)
{
return TryGetMember(obj, name, ignoreCase: false, out value);
}
public bool ContainsMember(object obj, string name)
{
return ContainsMember(obj, name, ignoreCase: false);
}
public void RemoveMember(object obj, string name)
{
RemoveMember(obj, name, ignoreCase: false);
}
public void SetMember(object obj, string name, object value)
{
SetMember(obj, name, value, ignoreCase: false);
}
public void SetMember<T>(object obj, string name, T value)
{
SetMember(obj, name, value, ignoreCase: false);
}
public object GetMember(object obj, string name, bool ignoreCase)
{
CallSite<Func<CallSite, object, object>> orCreateSite = GetOrCreateSite<object, object>(_lc.CreateGetMemberBinder(name, ignoreCase));
return orCreateSite.Target(orCreateSite, obj);
}
public T GetMember<T>(object obj, string name, bool ignoreCase)
{
CallSite<Func<CallSite, object, T>> orCreateSite = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), null));
CallSite<Func<CallSite, object, object>> orCreateSite2 = GetOrCreateSite<object, object>(_lc.CreateGetMemberBinder(name, ignoreCase));
return orCreateSite.Target(orCreateSite, orCreateSite2.Target(orCreateSite2, obj));
}
public bool TryGetMember(object obj, string name, bool ignoreCase, out object value)
{
try
{
value = GetMember(obj, name, ignoreCase);
return true;
}
catch (MissingMemberException)
{
value = null;
return false;
}
}
public bool ContainsMember(object obj, string name, bool ignoreCase)
{
object value;
return TryGetMember(obj, name, ignoreCase, out value);
}
public void RemoveMember(object obj, string name, bool ignoreCase)
{
CallSite<Action<CallSite, object>> orCreateActionSite = GetOrCreateActionSite<object>(_lc.CreateDeleteMemberBinder(name, ignoreCase));
orCreateActionSite.Target(orCreateActionSite, obj);
}
public void SetMember(object obj, string name, object value, bool ignoreCase)
{
CallSite<Func<CallSite, object, object, object>> orCreateSite = GetOrCreateSite<object, object, object>(_lc.CreateSetMemberBinder(name, ignoreCase));
orCreateSite.Target(orCreateSite, obj, value);
}
public void SetMember<T>(object obj, string name, T value, bool ignoreCase)
{
CallSite<Func<CallSite, object, T, object>> orCreateSite = GetOrCreateSite<object, T, object>(_lc.CreateSetMemberBinder(name, ignoreCase));
orCreateSite.Target(orCreateSite, obj, value);
}
public T ConvertTo<T>(object obj)
{
CallSite<Func<CallSite, object, T>> orCreateSite = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), null));
return orCreateSite.Target(orCreateSite, obj);
}
public object ConvertTo(object obj, Type type)
{
if (type.IsInterface || type.IsClass)
{
CallSite<Func<CallSite, object, object>> orCreateSite = GetOrCreateSite<object, object>(_lc.CreateConvertBinder(type, null));
return orCreateSite.Target(orCreateSite, obj);
}
MemberInfo[] member = typeof(DynamicOperations).GetMember("ConvertTo");
for (int i = 0; i < member.Length; i++)
{
MethodInfo methodInfo = (MethodInfo)member[i];
if (methodInfo.IsGenericMethod)
{
try
{
return methodInfo.MakeGenericMethod(type).Invoke(this, new object[1] { obj });
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
}
throw new InvalidOperationException();
}
public bool TryConvertTo<T>(object obj, out T result)
{
try
{
result = ConvertTo<T>(obj);
return true;
}
catch (ArgumentTypeException)
{
result = default(T);
return false;
}
catch (InvalidCastException)
{
result = default(T);
return false;
}
}
public bool TryConvertTo(object obj, Type type, out object result)
{
try
{
result = ConvertTo(obj, type);
return true;
}
catch (ArgumentTypeException)
{
result = null;
return false;
}
catch (InvalidCastException)
{
result = null;
return false;
}
}
public T ExplicitConvertTo<T>(object obj)
{
CallSite<Func<CallSite, object, T>> orCreateSite = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), true));
return orCreateSite.Target(orCreateSite, obj);
}
public object ExplicitConvertTo(object obj, Type type)
{
CallSite<Func<CallSite, object, object>> orCreateSite = GetOrCreateSite<object, object>(_lc.CreateConvertBinder(type, true));
return orCreateSite.Target(orCreateSite, obj);
}
public bool TryExplicitConvertTo(object obj, Type type, out object result)
{
try
{
result = ExplicitConvertTo(obj, type);
return true;
}
catch (ArgumentTypeException)
{
result = null;
return false;
}
catch (InvalidCastException)
{
result = null;
return false;
}
}
public bool TryExplicitConvertTo<T>(object obj, out T result)
{
try
{
result = ExplicitConvertTo<T>(obj);
return true;
}
catch (ArgumentTypeException)
{
result = default(T);
return false;
}
catch (InvalidCastException)
{
result = default(T);
return false;
}
}
public T ImplicitConvertTo<T>(object obj)
{
CallSite<Func<CallSite, object, T>> orCreateSite = GetOrCreateSite<object, T>(_lc.CreateConvertBinder(typeof(T), false));
return orCreateSite.Target(orCreateSite, obj);
}
public object ImplicitConvertTo(object obj, Type type)
{
CallSite<Func<CallSite, object, object>> orCreateSite = GetOrCreateSite<object, object>(_lc.CreateConvertBinder(type, false));
return orCreateSite.Target(orCreateSite, obj);
}
public bool TryImplicitConvertTo(object obj, Type type, out object result)
{
try
{
result = ImplicitConvertTo(obj, type);
return true;
}
catch (ArgumentTypeException)
{
result = null;
return false;
}
catch (InvalidCastException)
{
result = null;
return false;
}
}
public bool TryImplicitConvertTo<T>(object obj, out T result)
{
try
{
result = ImplicitConvertTo<T>(obj);
return true;
}
catch (ArgumentTypeException)
{
result = default(T);
return false;
}
catch (InvalidCastException)
{
result = default(T);
return false;
}
}
public TResult DoOperation<TTarget, TResult>(ExpressionType operation, TTarget target)
{
CallSite<Func<CallSite, TTarget, TResult>> orCreateSite = GetOrCreateSite<TTarget, TResult>(_lc.CreateUnaryOperationBinder(operation));
return orCreateSite.Target(orCreateSite, target);
}
public TResult DoOperation<TTarget, TOther, TResult>(ExpressionType operation, TTarget target, TOther other)
{
CallSite<Func<CallSite, TTarget, TOther, TResult>> orCreateSite = GetOrCreateSite<TTarget, TOther, TResult>(_lc.CreateBinaryOperationBinder(operation));
return orCreateSite.Target(orCreateSite, target, other);
}
public string GetDocumentation(object o)
{
return _lc.GetDocumentation(o);
}
public IList<string> GetCallSignatures(object o)
{
return _lc.GetCallSignatures(o);
}
public bool IsCallable(object o)
{
return _lc.IsCallable(o);
}
public IList<string> GetMemberNames(object obj)
{
return _lc.GetMemberNames(obj);
}
public string Format(object obj)
{
return _lc.FormatObject(this, obj);
}
public CallSite<Func<CallSite, T1, TResult>> GetOrCreateSite<T1, TResult>(CallSiteBinder siteBinder)
{
return GetOrCreateSite(siteBinder, CallSite<Func<CallSite, T1, TResult>>.Create);
}
public CallSite<Action<CallSite, T1>> GetOrCreateActionSite<T1>(CallSiteBinder siteBinder)
{
return GetOrCreateSite(siteBinder, CallSite<Action<CallSite, T1>>.Create);
}
public CallSite<Func<CallSite, T1, T2, TResult>> GetOrCreateSite<T1, T2, TResult>(CallSiteBinder siteBinder)
{
return GetOrCreateSite(siteBinder, CallSite<Func<CallSite, T1, T2, TResult>>.Create);
}
public CallSite<Func<CallSite, T1, T2, T3, TResult>> GetOrCreateSite<T1, T2, T3, TResult>(CallSiteBinder siteBinder)
{
return GetOrCreateSite(siteBinder, CallSite<Func<CallSite, T1, T2, T3, TResult>>.Create);
}
public CallSite<TSiteFunc> GetOrCreateSite<TSiteFunc>(CallSiteBinder siteBinder) where TSiteFunc : class
{
return GetOrCreateSite(siteBinder, CallSite<TSiteFunc>.Create);
}
private T GetOrCreateSite<T>(CallSiteBinder siteBinder, Func<CallSiteBinder, T> factory) where T : CallSite
{
SiteKey siteKey = new SiteKey(typeof(T), siteBinder);
lock (_sites)
{
if (!_sites.TryGetValue(siteKey, out var value))
{
SitesCreated++;
if (SitesCreated < 0)
{
SitesCreated = 0;
LastCleanup = 0;
}
siteKey.Site = factory(siteKey.SiteBinder);
_sites[siteKey] = siteKey;
}
else
{
siteKey = value;
}
siteKey.HitCount++;
CleanupNoLock();
}
return (T)siteKey.Site;
}
private void CleanupNoLock()
{
if (_sites.Count <= 20 || LastCleanup >= SitesCreated - 20)
{
return;
}
LastCleanup = SitesCreated;
int num = 0;
foreach (SiteKey key in _sites.Keys)
{
num += key.HitCount;
}
int num2 = num / _sites.Count;
if (num2 == 1 && _sites.Count > 50)
{
_sites.Clear();
return;
}
List<SiteKey> list = null;
foreach (SiteKey key2 in _sites.Keys)
{
if (key2.HitCount < num2 - 2)
{
if (list == null)
{
list = new List<SiteKey>();
}
list.Add(key2);
if (list.Count > 10)
{
break;
}
}
}
if (list == null)
{
return;
}
foreach (SiteKey item in list)
{
_sites.Remove(item);
}
foreach (SiteKey key3 in _sites.Keys)
{
key3.HitCount = 0;
}
}
private Func<DynamicOperations, CallSiteBinder, object, object[], object> GetInvoker(int paramCount)
{
lock (_invokers)
{
if (!_invokers.TryGetValue(paramCount, out var value))
{
ParameterExpression parameterExpression = Expression.Parameter(typeof(DynamicOperations));
ParameterExpression parameterExpression2 = Expression.Parameter(typeof(CallSiteBinder));
ParameterExpression parameterExpression3 = Expression.Parameter(typeof(object));
ParameterExpression parameterExpression4 = Expression.Parameter(typeof(object[]));
Type objectCallSiteDelegateType = ReflectionUtils.GetObjectCallSiteDelegateType(paramCount);
ParameterExpression parameterExpression5 = Expression.Parameter(typeof(CallSite<>).MakeGenericType(objectCallSiteDelegateType));
Expression[] array = new Expression[paramCount + 2];
array[0] = parameterExpression5;
array[1] = parameterExpression3;
for (int i = 0; i < paramCount; i++)
{
array[i + 2] = Expression.ArrayIndex(parameterExpression4, Expression.Constant(i));
}
MethodInfo genericMethodDefinition = new Func<CallSiteBinder, CallSite<Func<object>>>(GetOrCreateSite<Func<object>>).Method.GetGenericMethodDefinition();
return _invokers[paramCount] = Expression.Lambda<Func<DynamicOperations, CallSiteBinder, object, object[], object>>(Expression.Block(new ParameterExpression[1] { parameterExpression5 }, Expression.Assign(parameterExpression5, Expression.Call(parameterExpression, genericMethodDefinition.MakeGenericMethod(objectCallSiteDelegateType), parameterExpression2)), Expression.Invoke(Expression.Field(parameterExpression5, parameterExpression5.Type.GetField("Target")), array)), new ParameterExpression[4] { parameterExpression, parameterExpression2, parameterExpression3, parameterExpression4 }).Compile();
}
return value;
}
}
}