progress
This commit is contained in:
parent
16e76d6b31
commit
484dbfc9d9
529 changed files with 113694 additions and 0 deletions
514
AspClassic.Scripting/Runtime/DynamicOperations.cs
Normal file
514
AspClassic.Scripting/Runtime/DynamicOperations.cs
Normal file
|
@ -0,0 +1,514 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue