Added shared bind cache for improved performance and enhanced binder leak mitigation.
This commit is contained in:
Родитель
7df5fa298b
Коммит
1ea01c8490
|
@ -65,13 +65,15 @@ namespace Microsoft.ClearScript
|
|||
{
|
||||
internal class BindSignature : IEquatable<BindSignature>
|
||||
{
|
||||
private readonly Type context;
|
||||
private readonly TargetInfo targetInfo;
|
||||
private readonly string name;
|
||||
private readonly Type[] typeArgs;
|
||||
private readonly ArgInfo[] argData;
|
||||
|
||||
public BindSignature(HostTarget target, string name, Type[] typeArgs, object[] args)
|
||||
public BindSignature(Type context, HostTarget target, string name, Type[] typeArgs, object[] args)
|
||||
{
|
||||
this.context = context;
|
||||
targetInfo = new TargetInfo(target);
|
||||
this.typeArgs = typeArgs;
|
||||
this.name = name;
|
||||
|
@ -94,6 +96,7 @@ namespace Microsoft.ClearScript
|
|||
{
|
||||
var accumulator = new HashAccumulator();
|
||||
|
||||
accumulator.Update(context);
|
||||
targetInfo.UpdateHash(ref accumulator);
|
||||
accumulator.Update(name);
|
||||
|
||||
|
@ -121,6 +124,11 @@ namespace Microsoft.ClearScript
|
|||
return false;
|
||||
}
|
||||
|
||||
if (context != that.context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetInfo.Equals(that.targetInfo))
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -60,12 +60,14 @@
|
|||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Microsoft.CSharp.RuntimeBinder;
|
||||
using Microsoft.ClearScript.Util;
|
||||
using Binder = Microsoft.CSharp.RuntimeBinder.Binder;
|
||||
|
@ -74,6 +76,13 @@ namespace Microsoft.ClearScript
|
|||
{
|
||||
internal partial class HostItem
|
||||
{
|
||||
#region data
|
||||
|
||||
private static readonly ConcurrentDictionary<BindSignature, object> coreBindCache = new ConcurrentDictionary<BindSignature, object>();
|
||||
private static long coreBindCount;
|
||||
|
||||
#endregion
|
||||
|
||||
#region internal members
|
||||
|
||||
private object InvokeMethod(string name, object[] args, object[] bindArgs)
|
||||
|
@ -138,7 +147,8 @@ namespace Microsoft.ClearScript
|
|||
// WARNING: BindSignature holds on to the specified typeArgs; subsequent modification
|
||||
// will result in bugs that are difficult to diagnose. Create a copy if necessary.
|
||||
|
||||
var signature = new BindSignature(target, name, typeArgs, bindArgs);
|
||||
var context = accessContext ?? engine.AccessContext;
|
||||
var signature = new BindSignature(context, target, name, typeArgs, bindArgs);
|
||||
MethodBindResult result;
|
||||
|
||||
object rawResult;
|
||||
|
@ -148,7 +158,7 @@ namespace Microsoft.ClearScript
|
|||
}
|
||||
else
|
||||
{
|
||||
result = BindMethodInternal(name, typeArgs, args, bindArgs);
|
||||
result = BindMethodInternal(context, name, typeArgs, args, bindArgs);
|
||||
if (!result.IsPreferredMethod(name))
|
||||
{
|
||||
if (result is MethodBindSuccess)
|
||||
|
@ -158,7 +168,7 @@ namespace Microsoft.ClearScript
|
|||
|
||||
foreach (var altName in GetAltMethodNames(name))
|
||||
{
|
||||
var altResult = BindMethodInternal(altName, typeArgs, args, bindArgs);
|
||||
var altResult = BindMethodInternal(context, altName, typeArgs, args, bindArgs);
|
||||
if (altResult.IsUnblockedMethod())
|
||||
{
|
||||
result = altResult;
|
||||
|
@ -173,11 +183,32 @@ namespace Microsoft.ClearScript
|
|||
return result;
|
||||
}
|
||||
|
||||
private MethodBindResult BindMethodInternal(string name, Type[] typeArgs, object[] args, object[] bindArgs)
|
||||
private MethodBindResult BindMethodInternal(Type context, string name, Type[] typeArgs, object[] args, object[] bindArgs)
|
||||
{
|
||||
var signature = new BindSignature(context, target, name, typeArgs, bindArgs);
|
||||
MethodBindResult result;
|
||||
|
||||
object rawResult;
|
||||
if (coreBindCache.TryGetValue(signature, out rawResult))
|
||||
{
|
||||
result = MethodBindResult.Create(name, rawResult, target, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = BindMethodCore(context, name, typeArgs, args, bindArgs);
|
||||
coreBindCache.TryAdd(signature, result.RawResult);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private MethodBindResult BindMethodCore(Type context, string name, Type[] typeArgs, object[] args, object[] bindArgs)
|
||||
{
|
||||
Interlocked.Increment(ref coreBindCount);
|
||||
|
||||
// create C# member invocation binder
|
||||
const CSharpBinderFlags binderFlags = CSharpBinderFlags.InvokeSimpleName | CSharpBinderFlags.ResultDiscarded;
|
||||
var binder = Binder.InvokeMember(binderFlags, name, typeArgs, accessContext ?? engine.AccessContext, CreateArgInfoEnum(bindArgs));
|
||||
var binder = Binder.InvokeMember(binderFlags, name, typeArgs, context, CreateArgInfoEnum(bindArgs));
|
||||
|
||||
// perform default binding
|
||||
var binding = DynamicHelpers.Bind((DynamicMetaObjectBinder)binder, target, bindArgs);
|
||||
|
@ -275,6 +306,20 @@ namespace Microsoft.ClearScript
|
|||
|
||||
#endregion
|
||||
|
||||
#region unit test support
|
||||
|
||||
internal static void ResetCoreBindCount()
|
||||
{
|
||||
Interlocked.Exchange(ref coreBindCount, 0);
|
||||
}
|
||||
|
||||
internal static long GetCoreBindCount()
|
||||
{
|
||||
return Interlocked.Read(ref coreBindCount);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: MethodBindResult
|
||||
|
||||
private abstract class MethodBindResult
|
||||
|
|
|
@ -84,6 +84,8 @@ namespace Microsoft.ClearScript.Windows
|
|||
{
|
||||
#region data
|
||||
|
||||
private static readonly object nullDispatch = new DispatchWrapper(null);
|
||||
|
||||
private ActiveScriptWrapper activeScript;
|
||||
private WindowsScriptEngineFlags engineFlags;
|
||||
|
||||
|
@ -428,7 +430,7 @@ namespace Microsoft.ClearScript.Windows
|
|||
{
|
||||
if (engineFlags.HasFlag(WindowsScriptEngineFlags.MarshalNullAsDispatch))
|
||||
{
|
||||
return new DispatchWrapper(null);
|
||||
return nullDispatch;
|
||||
}
|
||||
|
||||
return DBNull.Value;
|
||||
|
|
|
@ -110,50 +110,56 @@ namespace Microsoft.ClearScript.Test
|
|||
};
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(new HostVariable<string>(null), "foo", typeArgs1, args1);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(ClearScriptTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
AssertNotEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(new HostVariable<string>(null), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(HostObject.Wrap("baz"), "foo", typeArgs1, args1);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), new HostVariable<string>(null), "foo", typeArgs1, args1);
|
||||
AssertNotEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(HostObject.Wrap("baz"), "foo", typeArgs1, args1);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), new HostVariable<string>(null), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), HostObject.Wrap("baz"), "foo", typeArgs1, args1);
|
||||
AssertNotEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(new HostVariable<string>(null), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(new HostVariable<string>("baz"), "foo", typeArgs1, args1);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), HostObject.Wrap("baz"), "foo", typeArgs1, args1);
|
||||
AssertNotEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(new HostVariable<string>("baz"), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(HostObject.Wrap("qux"), "foo", typeArgs1, args1);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), new HostVariable<string>(null), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), new HostVariable<string>("baz"), "foo", typeArgs1, args1);
|
||||
AssertNotEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), new HostVariable<string>("baz"), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), HostObject.Wrap("qux"), "foo", typeArgs1, args1);
|
||||
AssertEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(HostType.Wrap(typeof(string)), "bar", typeArgs1, args1);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "bar", typeArgs1, args1);
|
||||
AssertNotEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(HostType.Wrap(typeof(string)), "foo", typeArgs2, args1);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs2, args1);
|
||||
AssertNotEqual(sig1, sig2);
|
||||
}
|
||||
|
||||
{
|
||||
var sig1 = new BindSignature(HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(HostType.Wrap(typeof(string)), "foo", typeArgs1, args2);
|
||||
var sig1 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args1);
|
||||
var sig2 = new BindSignature(typeof(BindSignatureTest), HostType.Wrap(typeof(string)), "foo", typeArgs1, args2);
|
||||
AssertEqual(sig1, sig2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -379,6 +379,27 @@ namespace Microsoft.ClearScript.Test
|
|||
results.CompiledAssembly.GetType("TestModule").InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, null, MiscHelpers.GetEmptyArray<object>());
|
||||
}
|
||||
|
||||
[TestMethod, TestCategory("BugFix")]
|
||||
public void BugFix_CoreBindCache()
|
||||
{
|
||||
HostItem.ResetCoreBindCount();
|
||||
|
||||
engine.Dispose();
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
using (engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging))
|
||||
{
|
||||
engine.Script.host = new HostFunctions();
|
||||
Assert.AreEqual(' ', engine.Evaluate("host.toChar(32)"));
|
||||
Assert.AreEqual('A', engine.Evaluate("host.toChar(65)"));
|
||||
Assert.AreEqual(0.0F, engine.Evaluate("host.toSingle(0)"));
|
||||
Assert.AreEqual(1.0F, engine.Evaluate("host.toSingle(1)"));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.AreEqual(2L, HostItem.GetCoreBindCount());
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
||||
#endregion
|
||||
|
|
Загрузка…
Ссылка в новой задаче