diff --git a/ClearScript/ClearScript.csproj b/ClearScript/ClearScript.csproj index 99ff84c6a..79cee5c62 100644 --- a/ClearScript/ClearScript.csproj +++ b/ClearScript/ClearScript.csproj @@ -81,12 +81,16 @@ + + + + diff --git a/ClearScript/DelegateFactory.Generated.cs b/ClearScript/DelegateFactory.Generated.cs index 34871c038..877242b04 100644 --- a/ClearScript/DelegateFactory.Generated.cs +++ b/ClearScript/DelegateFactory.Generated.cs @@ -73,10 +73,11 @@ internal static partial class DelegateFactory private const int maxArgCount = 16; - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = true; private readonly object target; private readonly Delegate del; @@ -91,7 +92,22 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget() { - Invoke(() => ((dynamic)target)()); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)()); + } + else + { + + try + { + Invoke(() => ((dynamic)target)()); + } + finally + { + + } + } } // ReSharper restore UnusedMember.Local @@ -106,10 +122,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1)); private readonly object target; private readonly Delegate del; @@ -124,7 +141,22 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1) { - Invoke(() => ((dynamic)target)(a1)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1)); + } + else + { + var v1 = GetArgValue(a1); + try + { + Invoke(() => ((dynamic)target)(ref v1)); + } + finally + { + SetArgValue(a1, v1); + } + } } // ReSharper restore UnusedMember.Local @@ -139,10 +171,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2)); private readonly object target; private readonly Delegate del; @@ -157,7 +190,24 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2) { - Invoke(() => ((dynamic)target)(a1, a2)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + } + } } // ReSharper restore UnusedMember.Local @@ -172,10 +222,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3)); private readonly object target; private readonly Delegate del; @@ -190,7 +241,26 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3) { - Invoke(() => ((dynamic)target)(a1, a2, a3)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + } + } } // ReSharper restore UnusedMember.Local @@ -205,10 +275,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4)); private readonly object target; private readonly Delegate del; @@ -223,7 +294,28 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + } + } } // ReSharper restore UnusedMember.Local @@ -238,10 +330,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)); private readonly object target; private readonly Delegate del; @@ -256,7 +349,30 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + } + } } // ReSharper restore UnusedMember.Local @@ -271,10 +387,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)); private readonly object target; private readonly Delegate del; @@ -289,7 +406,32 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + } + } } // ReSharper restore UnusedMember.Local @@ -304,10 +446,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7)); private readonly object target; private readonly Delegate del; @@ -322,7 +465,34 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + } + } } // ReSharper restore UnusedMember.Local @@ -337,10 +507,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8)); private readonly object target; private readonly Delegate del; @@ -355,7 +526,36 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + } + } } // ReSharper restore UnusedMember.Local @@ -370,10 +570,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9)); private readonly object target; private readonly Delegate del; @@ -388,7 +589,38 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + } + } } // ReSharper restore UnusedMember.Local @@ -403,10 +635,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10)); private readonly object target; private readonly Delegate del; @@ -421,7 +654,40 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + } + } } // ReSharper restore UnusedMember.Local @@ -436,10 +702,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11)); private readonly object target; private readonly Delegate del; @@ -454,7 +721,42 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + } + } } // ReSharper restore UnusedMember.Local @@ -469,10 +771,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12)); private readonly object target; private readonly Delegate del; @@ -487,7 +790,44 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + } + } } // ReSharper restore UnusedMember.Local @@ -502,10 +842,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13)); private readonly object target; private readonly Delegate del; @@ -520,7 +861,46 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + } + } } // ReSharper restore UnusedMember.Local @@ -535,10 +915,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14)); private readonly object target; private readonly Delegate del; @@ -553,7 +934,48 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13, T14 a14) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + var v14 = GetArgValue(a14); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13, ref v14)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + SetArgValue(a14, v14); + } + } } // ReSharper restore UnusedMember.Local @@ -568,10 +990,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15)); private readonly object target; private readonly Delegate del; @@ -586,7 +1009,50 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13, T14 a14, T15 a15) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + var v14 = GetArgValue(a14); + var v15 = GetArgValue(a15); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13, ref v14, ref v15)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + SetArgValue(a14, v14); + SetArgValue(a15, v15); + } + } } // ReSharper restore UnusedMember.Local @@ -601,10 +1067,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim : ProcShim { private static readonly MethodInfo method = typeof(ProcShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15), typeof(T16)); private readonly object target; private readonly Delegate del; @@ -619,7 +1086,52 @@ public ProcShim(ScriptEngine engine, object target) public void InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13, T14 a14, T15 a15, T16 a16) { - Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)); + } + else + { + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + var v14 = GetArgValue(a14); + var v15 = GetArgValue(a15); + var v16 = GetArgValue(a16); + try + { + Invoke(() => ((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13, ref v14, ref v15, ref v16)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + SetArgValue(a14, v14); + SetArgValue(a15, v15); + SetArgValue(a16, v16); + } + } } // ReSharper restore UnusedMember.Local @@ -635,10 +1147,11 @@ public override Delegate Delegate } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = true; private readonly object target; private readonly Delegate del; @@ -653,7 +1166,20 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget() { - return Invoke(() => (TResult)((dynamic)target)()); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)()); + } + + + try + { + return Invoke(() => (TResult)((dynamic)target)()); + } + finally + { + + } } // ReSharper restore UnusedMember.Local @@ -668,10 +1194,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1)); private readonly object target; private readonly Delegate del; @@ -686,7 +1213,20 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1) { - return Invoke(() => (TResult)((dynamic)target)(a1)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1)); + } + + var v1 = GetArgValue(a1); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1)); + } + finally + { + SetArgValue(a1, v1); + } } // ReSharper restore UnusedMember.Local @@ -701,10 +1241,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2)); private readonly object target; private readonly Delegate del; @@ -719,7 +1260,22 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + } } // ReSharper restore UnusedMember.Local @@ -734,10 +1290,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3)); private readonly object target; private readonly Delegate del; @@ -752,7 +1309,24 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + } } // ReSharper restore UnusedMember.Local @@ -767,10 +1341,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4)); private readonly object target; private readonly Delegate del; @@ -785,7 +1360,26 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + } } // ReSharper restore UnusedMember.Local @@ -800,10 +1394,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)); private readonly object target; private readonly Delegate del; @@ -818,7 +1413,28 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + } } // ReSharper restore UnusedMember.Local @@ -833,10 +1449,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)); private readonly object target; private readonly Delegate del; @@ -851,7 +1468,30 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + } } // ReSharper restore UnusedMember.Local @@ -866,10 +1506,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7)); private readonly object target; private readonly Delegate del; @@ -884,7 +1525,32 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + } } // ReSharper restore UnusedMember.Local @@ -899,10 +1565,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8)); private readonly object target; private readonly Delegate del; @@ -917,7 +1584,34 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + } } // ReSharper restore UnusedMember.Local @@ -932,10 +1626,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9)); private readonly object target; private readonly Delegate del; @@ -950,7 +1645,36 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + } } // ReSharper restore UnusedMember.Local @@ -965,10 +1689,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10)); private readonly object target; private readonly Delegate del; @@ -983,7 +1708,38 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + } } // ReSharper restore UnusedMember.Local @@ -998,10 +1754,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11)); private readonly object target; private readonly Delegate del; @@ -1016,7 +1773,40 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + } } // ReSharper restore UnusedMember.Local @@ -1031,10 +1821,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12)); private readonly object target; private readonly Delegate del; @@ -1049,7 +1840,42 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + } } // ReSharper restore UnusedMember.Local @@ -1064,10 +1890,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13)); private readonly object target; private readonly Delegate del; @@ -1082,7 +1909,44 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + } } // ReSharper restore UnusedMember.Local @@ -1097,10 +1961,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14)); private readonly object target; private readonly Delegate del; @@ -1115,7 +1980,46 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13, T14 a14) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + var v14 = GetArgValue(a14); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13, ref v14)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + SetArgValue(a14, v14); + } } // ReSharper restore UnusedMember.Local @@ -1130,10 +2034,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15)); private readonly object target; private readonly Delegate del; @@ -1148,7 +2053,48 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13, T14 a14, T15 a15) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + var v14 = GetArgValue(a14); + var v15 = GetArgValue(a15); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13, ref v14, ref v15)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + SetArgValue(a14, v14); + SetArgValue(a15, v15); + } } // ReSharper restore UnusedMember.Local @@ -1163,10 +2109,11 @@ public override Delegate Delegate #endregion } - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim : FuncShim { private static readonly MethodInfo method = typeof(FuncShim).GetMethod("InvokeTarget"); + private static readonly bool allByValue = GetAllByValue(typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15), typeof(T16)); private readonly object target; private readonly Delegate del; @@ -1181,7 +2128,50 @@ public FuncShim(ScriptEngine engine, object target) public TResult InvokeTarget(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8, T9 a9, T10 a10, T11 a11, T12 a12, T13 a13, T14 a14, T15 a15, T16 a16) { - return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16)); + } + + var v1 = GetArgValue(a1); + var v2 = GetArgValue(a2); + var v3 = GetArgValue(a3); + var v4 = GetArgValue(a4); + var v5 = GetArgValue(a5); + var v6 = GetArgValue(a6); + var v7 = GetArgValue(a7); + var v8 = GetArgValue(a8); + var v9 = GetArgValue(a9); + var v10 = GetArgValue(a10); + var v11 = GetArgValue(a11); + var v12 = GetArgValue(a12); + var v13 = GetArgValue(a13); + var v14 = GetArgValue(a14); + var v15 = GetArgValue(a15); + var v16 = GetArgValue(a16); + try + { + return Invoke(() => (TResult)((dynamic)target)(ref v1, ref v2, ref v3, ref v4, ref v5, ref v6, ref v7, ref v8, ref v9, ref v10, ref v11, ref v12, ref v13, ref v14, ref v15, ref v16)); + } + finally + { + SetArgValue(a1, v1); + SetArgValue(a2, v2); + SetArgValue(a3, v3); + SetArgValue(a4, v4); + SetArgValue(a5, v5); + SetArgValue(a6, v6); + SetArgValue(a7, v7); + SetArgValue(a8, v8); + SetArgValue(a9, v9); + SetArgValue(a10, v10); + SetArgValue(a11, v11); + SetArgValue(a12, v12); + SetArgValue(a13, v13); + SetArgValue(a14, v14); + SetArgValue(a15, v15); + SetArgValue(a16, v16); + } } // ReSharper restore UnusedMember.Local diff --git a/ClearScript/DelegateFactory.cs b/ClearScript/DelegateFactory.cs index 07b7f6fea..e31a02d49 100644 --- a/ClearScript/DelegateFactory.cs +++ b/ClearScript/DelegateFactory.cs @@ -116,7 +116,7 @@ public static Delegate CreateDelegate(ScriptEngine engine, object target, Type d throw new ArgumentException("Invalid delegate type (parameter count too large)"); } - var paramTypes = parameters.Select(parameter => parameter.ParameterType).ToArray(); + var paramTypes = parameters.Select(param => param.ParameterType).ToArray(); if (paramTypes.Any(paramType => paramType.IsByRef)) { return CreateComplexDelegate(engine, target, delegateType); @@ -128,7 +128,7 @@ public static Delegate CreateDelegate(ScriptEngine engine, object target, Type d private static Delegate CreateSimpleDelegate(ScriptEngine engine, object target, Type delegateType) { var method = delegateType.GetMethod("Invoke"); - var paramTypes = method.GetParameters().Select(parameter => parameter.ParameterType).ToArray(); + var paramTypes = method.GetParameters().Select(param => param.ParameterType).ToArray(); Type shimType; if (method.ReturnType == typeof(void)) @@ -153,7 +153,7 @@ private static Delegate CreateComplexDelegate(ScriptEngine engine, object target var method = delegateType.GetMethod("Invoke"); var parameters = method.GetParameters(); - var paramTypes = parameters.Select(parameter => parameter.ParameterType).ToArray(); + var paramTypes = parameters.Select(param => param.ParameterType).ToArray(); var innerParamTypes = new Type[parameters.Length]; for (var index = 0; index < parameters.Length; index++) @@ -232,6 +232,33 @@ private abstract class DelegateShim { public abstract Delegate Delegate { get; } + protected ScriptEngine Engine { get; private set; } + + protected DelegateShim(ScriptEngine engine) + { + Engine = engine; + } + + protected static bool GetAllByValue(params Type[] types) + { + return !types.Any(type => typeof(IByRefArg).IsAssignableFrom(type)); + } + + protected static object GetArgValue(object arg) + { + var byRefArg = arg as IByRefArg; + return (byRefArg != null) ? byRefArg.Value : arg; + } + + protected static void SetArgValue(object arg, object value) + { + var byRefArg = arg as IByRefArg; + if (byRefArg != null) + { + byRefArg.Value = value; + } + } + protected static object GetCompatibleTarget(Type delegateType, object target) { var del = target as Delegate; @@ -257,6 +284,7 @@ private abstract class ProcShim : DelegateShim private readonly Action invoker; protected ProcShim(ScriptEngine engine) + : base(engine) { if (engine == null) { @@ -279,6 +307,7 @@ private abstract class FuncShim : DelegateShim private readonly Func, TResult> invoker; protected FuncShim(ScriptEngine engine) + : base(engine) { if (engine == null) { diff --git a/ClearScript/DelegateFactory.tt b/ClearScript/DelegateFactory.tt index 03c5849e9..7e709b4af 100644 --- a/ClearScript/DelegateFactory.tt +++ b/ClearScript/DelegateFactory.tt @@ -81,14 +81,19 @@ namespace Microsoft.ClearScript for (var count = 0; count <= maxArgCount; count++) { var typeParamList = (count == 0) ? "" : "<" + string.Join(", ", Enumerable.Range(1, count).Select(index => "T" + index)) + ", TDelegate>"; + var allByValueExpr = (count == 0) ? "true" : "GetAllByValue(" + string.Join(", ", Enumerable.Range(1, count).Select(index => "typeof(T" + index + ")")) + ")"; var methodParamList = string.Join(", ", Enumerable.Range(1, count).Select(index => "T" + index + " a" + index)); - var methodArgList = string.Join(", ", Enumerable.Range(1, count).Select(index => "a" + index)); + var argList = string.Join(", ", Enumerable.Range(1, count).Select(index => "a" + index)); + var varDeclList = string.Join("\r\n ", Enumerable.Range(1, count).Select(index => "var v" + index + " = GetArgValue(a" + index + ");")); + var byRefArgList = string.Join(", ", Enumerable.Range(1, count).Select(index => "ref v" + index)); + var varAssignList = string.Join("\r\n ", Enumerable.Range(1, count).Select(index => "SetArgValue(a" + index + ", v" + index + ");")); #> - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class ProcShim<#= typeParamList #> : ProcShim { private static readonly MethodInfo method = typeof(ProcShim<#= typeParamList #>).GetMethod("InvokeTarget"); + private static readonly bool allByValue = <#= allByValueExpr #>; private readonly object target; private readonly Delegate del; @@ -103,7 +108,22 @@ namespace Microsoft.ClearScript public void InvokeTarget(<#= methodParamList #>) { - Invoke(() => ((dynamic)target)(<#= methodArgList #>)); + if (allByValue || Engine.EnableAutoHostVariables) + { + Invoke(() => ((dynamic)target)(<#= argList #>)); + } + else + { + <#= varDeclList #> + try + { + Invoke(() => ((dynamic)target)(<#= byRefArgList #>)); + } + finally + { + <#= varAssignList #> + } + } } // ReSharper restore UnusedMember.Local @@ -125,14 +145,19 @@ namespace Microsoft.ClearScript for (var count = 0; count <= maxArgCount; count++) { var typeParamList = (count == 0) ? "" : "<" + string.Join(", ", Enumerable.Range(1, count).Select(index => "T" + index)) + ", TResult, TDelegate>"; + var allByValueExpr = (count == 0) ? "true" : "GetAllByValue(" + string.Join(", ", Enumerable.Range(1, count).Select(index => "typeof(T" + index + ")")) + ")"; var methodParamList = string.Join(", ", Enumerable.Range(1, count).Select(index => "T" + index + " a" + index)); - var methodArgList = string.Join(", ", Enumerable.Range(1, count).Select(index => "a" + index)); + var argList = string.Join(", ", Enumerable.Range(1, count).Select(index => "a" + index)); + var varDeclList = string.Join("\r\n ", Enumerable.Range(1, count).Select(index => "var v" + index + " = GetArgValue(a" + index + ");")); + var byRefArgList = string.Join(", ", Enumerable.Range(1, count).Select(index => "ref v" + index)); + var varAssignList = string.Join("\r\n ", Enumerable.Range(1, count).Select(index => "SetArgValue(a" + index + ", v" + index + ");")); #> - [ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] private class FuncShim<#= typeParamList #> : FuncShim { private static readonly MethodInfo method = typeof(FuncShim<#= typeParamList #>).GetMethod("InvokeTarget"); + private static readonly bool allByValue = <#= allByValueExpr #>; private readonly object target; private readonly Delegate del; @@ -147,7 +172,20 @@ namespace Microsoft.ClearScript public TResult InvokeTarget(<#= methodParamList #>) { - return Invoke(() => (TResult)((dynamic)target)(<#= methodArgList #>)); + if (allByValue || Engine.EnableAutoHostVariables) + { + return Invoke(() => (TResult)((dynamic)target)(<#= argList #>)); + } + + <#= varDeclList #> + try + { + return Invoke(() => (TResult)((dynamic)target)(<#= byRefArgList #>)); + } + finally + { + <#= varAssignList #> + } } // ReSharper restore UnusedMember.Local diff --git a/ClearScript/Exports/VersionSymbols.h b/ClearScript/Exports/VersionSymbols.h index f260246c5..3d5b0c74e 100644 --- a/ClearScript/Exports/VersionSymbols.h +++ b/ClearScript/Exports/VersionSymbols.h @@ -63,5 +63,5 @@ #pragma once -#define CLEARSCRIPT_VERSION_STRING "5.4.0.0" -#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 5,4,0,0 +#define CLEARSCRIPT_VERSION_STRING "5.4.1.0" +#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 5,4,1,0 diff --git a/ClearScript/HostFunctions.cs b/ClearScript/HostFunctions.cs index 229c7adbd..da4629536 100644 --- a/ClearScript/HostFunctions.cs +++ b/ClearScript/HostFunctions.cs @@ -206,11 +206,11 @@ public object newObj(IDynamicMetaObjectProvider target, params object[] args) } /// - /// Creates a host array. + /// Creates a host array with the specified element type. /// /// The element type of the array to create. /// One or more integers representing the array dimension lengths. - /// A new host array. + /// A new host array with the specified element type. /// /// For information about the mapping between host members and script-callable properties /// and methods, see @@ -226,12 +226,29 @@ public object newObj(IDynamicMetaObjectProvider target, params object[] args) /// var array = host.newArr(StringT, 5, 3); /// /// + /// /// public object newArr(params int[] lengths) { return Array.CreateInstance(typeof(T), lengths); } + /// + /// Creates a host array with as the element type. + /// + /// One or more integers representing the array dimension lengths. + /// A new host array with as the element type. + /// + /// For information about the mapping between host members and script-callable properties + /// and methods, see + /// AddHostObject. + /// + /// + public object newArr(params int[] lengths) + { + return newArr(lengths); + } + /// /// Creates a host variable of the specified type. /// @@ -400,6 +417,7 @@ public object proc(int argCount, object scriptFunc) /// array = array.Select(selector).ToArray(); /// /// + /// /// /// public object func(int argCount, object scriptFunc) @@ -407,6 +425,29 @@ public object func(int argCount, object scriptFunc) return DelegateFactory.CreateFunc(GetEngine(), scriptFunc, argCount); } + /// + /// Creates a delegate that invokes a script function and returns its result value. + /// + /// The number of arguments to pass to the script function. + /// The script function for which to create a delegate. + /// A new delegate that invokes the specified script function and returns its result value. + /// + /// This function creates a delegate that accepts arguments and + /// returns the result of invoking . The type of all + /// parameters and the return value is . Such a delegate is + /// often useful in strongly typed contexts because of + /// contravariance. + /// + /// For information about the types of result values that script code can return, see + /// . + /// + /// + /// + public object func(int argCount, object scriptFunc) + { + return func(argCount, scriptFunc); + } + /// /// Gets the for the specified host type. This version is invoked /// if the specified object can be used as a type argument. @@ -1325,12 +1366,13 @@ public bool tryCatch(object tryFunc, object catchFunc, object finallyFunc = null internal ScriptEngine GetEngine() { - if (engine == null) + var activeEngine = ScriptEngine.Current ?? engine; + if (activeEngine == null) { throw new InvalidOperationException("Operation requires a script engine"); } - return engine; + return activeEngine; } internal static Type GetUniqueHostType(object type, string paramName) @@ -1357,17 +1399,7 @@ internal static Type GetUniqueHostType(object type, string paramName) void IScriptableObject.OnExposedToScriptCode(ScriptEngine engine) { MiscHelpers.VerifyNonNullArgument(engine, "engine"); - - if (this.engine == null) - { - this.engine = engine; - return; - } - - if (engine != this.engine) - { - throw new ArgumentException("Invalid script engine", "engine"); - } + this.engine = engine; } // ReSharper restore ParameterHidesMember diff --git a/ClearScript/HostItem.cs b/ClearScript/HostItem.cs index a3e62a8cc..c6626bcec 100644 --- a/ClearScript/HostItem.cs +++ b/ClearScript/HostItem.cs @@ -194,11 +194,6 @@ private static object Wrap(ScriptEngine engine, object obj, Type type, HostItemF #region public members - public ScriptEngine Engine - { - get { return engine; } - } - public HostTarget Target { get { return target; } @@ -1662,6 +1657,11 @@ public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr pInterfa #region IScriptMarshalWrapper implementation + public ScriptEngine Engine + { + get { return engine; } + } + public object Unwrap() { return target.Target; diff --git a/ClearScript/Properties/AssemblyInfo.cs b/ClearScript/Properties/AssemblyInfo.cs index 01dcb26f8..9e49c97b6 100644 --- a/ClearScript/Properties/AssemblyInfo.cs +++ b/ClearScript/Properties/AssemblyInfo.cs @@ -75,5 +75,5 @@ [assembly: InternalsVisibleTo("ClearScriptTest")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("5.4.0.0")] -[assembly: AssemblyFileVersion("5.4.0.0")] +[assembly: AssemblyVersion("5.4.1.0")] +[assembly: AssemblyFileVersion("5.4.1.0")] diff --git a/ClearScript/ScriptEngine.cs b/ClearScript/ScriptEngine.cs index 4560c0c2b..216fd51e2 100644 --- a/ClearScript/ScriptEngine.cs +++ b/ClearScript/ScriptEngine.cs @@ -77,6 +77,7 @@ public abstract class ScriptEngine : IDisposable private readonly string name; private static readonly IUniqueNameManager nameManager = new UniqueNameManager(); + [ThreadStatic] private static ScriptEngine currentEngine; #endregion @@ -103,6 +104,20 @@ public string Name get { return name; } } + /// + /// Gets the script engine that is invoking a host member on the current thread. + /// + /// + /// If multiple script engines are invoking host members on the current thread, this + /// property gets the one responsible for the most deeply nested invocation. If no script + /// engines are invoking host members on the current thread, this property returns + /// null. + /// + public static ScriptEngine Current + { + get { return currentEngine; } + } + /// /// Gets the script engine's recommended file name extension for script files. /// @@ -165,6 +180,17 @@ public string Name /// public bool UseReflectionBindFallback { get; set; } + /// + /// Enables or disables automatic host variable tunneling for by-reference arguments to script functions and delegates. + /// + /// + /// When this property is set to true, the script engine replaces by-reference + /// arguments to script functions and delegates with host variables, allowing script code + /// to simulate output arguments if the script language does not support them natively. + /// + /// + public bool EnableAutoHostVariables { get; set; } + /// /// Gets or sets a callback that can be used to halt script execution. /// @@ -1109,12 +1135,32 @@ internal void CheckReflection() internal virtual void HostInvoke(Action action) { - action(); + var previousEngine = currentEngine; + currentEngine = this; + + try + { + action(); + } + finally + { + currentEngine = previousEngine; + } } internal virtual T HostInvoke(Func func) { - return func(); + var previousEngine = currentEngine; + currentEngine = this; + + try + { + return func(); + } + finally + { + currentEngine = previousEngine; + } } #endregion @@ -1125,7 +1171,7 @@ internal virtual T HostInvoke(Func func) internal virtual void ScriptInvoke(Action action) { - var prevScriptFrame = CurrentScriptFrame; + var previousScriptFrame = CurrentScriptFrame; CurrentScriptFrame = new ScriptFrame(); try @@ -1134,13 +1180,13 @@ internal virtual void ScriptInvoke(Action action) } finally { - CurrentScriptFrame = prevScriptFrame; + CurrentScriptFrame = previousScriptFrame; } } internal virtual T ScriptInvoke(Func func) { - var prevScriptFrame = CurrentScriptFrame; + var previousScriptFrame = CurrentScriptFrame; CurrentScriptFrame = new ScriptFrame(); try @@ -1149,7 +1195,7 @@ internal virtual T ScriptInvoke(Func func) } finally { - CurrentScriptFrame = prevScriptFrame; + CurrentScriptFrame = previousScriptFrame; } } @@ -1235,7 +1281,7 @@ internal HostItem GetOrCreateHostItem(HostTarget target, HostItemFlags flags, Fu var hostObject = target as HostObject; if (hostObject != null) { - return GetOrCreateHostItemForHostTarget(hostObject, flags, createHostItem); + return GetOrCreateHostItemForHostObject(hostObject, hostObject.Target, flags, createHostItem); } var hostType = target as HostType; @@ -1247,21 +1293,27 @@ internal HostItem GetOrCreateHostItem(HostTarget target, HostItemFlags flags, Fu var hostMethod = target as HostMethod; if (hostMethod != null) { - return GetOrCreateHostItemForHostTarget(hostMethod, flags, createHostItem); + return GetOrCreateHostItemForHostObject(hostMethod, hostMethod, flags, createHostItem); + } + + var hostVariable = target as HostVariableBase; + if (hostVariable != null) + { + return GetOrCreateHostItemForHostObject(hostVariable, hostVariable, flags, createHostItem); } var hostIndexedProperty = target as HostIndexedProperty; if (hostIndexedProperty != null) { - return GetOrCreateHostItemForHostTarget(hostIndexedProperty, flags, createHostItem); + return GetOrCreateHostItemForHostObject(hostIndexedProperty, hostIndexedProperty, flags, createHostItem); } return createHostItem(this, target, flags); } - private HostItem GetOrCreateHostItemForHostTarget(HostTarget hostTarget, HostItemFlags flags, Func createHostItem) + private HostItem GetOrCreateHostItemForHostObject(HostTarget hostTarget, object target, HostItemFlags flags, Func createHostItem) { - var cacheEntry = hostObjectHostItemCache.GetOrCreateValue(hostTarget.Target); + var cacheEntry = hostObjectHostItemCache.GetOrCreateValue(target); List activeWeakRefs = null; var staleWeakRefCount = 0; diff --git a/ClearScript/ScriptItem.cs b/ClearScript/ScriptItem.cs index 162fac4e4..94614dc31 100644 --- a/ClearScript/ScriptItem.cs +++ b/ClearScript/ScriptItem.cs @@ -78,8 +78,6 @@ internal abstract class ScriptItem : DynamicObject, IExpando, IDynamic, IScriptM private static readonly MethodInfo clearLastScriptErrorMethod = typeof(ScriptItem).GetMethod("ClearLastScriptError"); [ThreadStatic] private static IScriptEngineException lastScriptError; - public abstract ScriptEngine Engine { get; } - public static void ThrowLastScriptError() { var scriptError = lastScriptError; @@ -106,12 +104,18 @@ protected virtual object[] AdjustInvokeArgs(object[] args) return args; } - private bool TryWrappedBindAndInvoke(DynamicMetaObjectBinder binder, object[] args, out object result) + private bool TryWrappedBindAndInvoke(DynamicMetaObjectBinder binder, object[] wrappedArgs, out object result) { + object[] args = null; + object[] savedArgs = null; + object tempResult = null; var succeeded = Engine.ScriptInvoke(() => { - if (!TryBindAndInvoke(binder, Engine.MarshalToScript(args), out tempResult)) + args = Engine.MarshalToScript(wrappedArgs); + savedArgs = (object[])args.Clone(); + + if (!TryBindAndInvoke(binder, args, out tempResult)) { if ((Engine.CurrentScriptFrame != null) && (lastScriptError == null)) { @@ -126,6 +130,15 @@ private bool TryWrappedBindAndInvoke(DynamicMetaObjectBinder binder, object[] ar if (succeeded) { + for (var index = 0; index < args.Length; index++) + { + var arg = args[index]; + if (!ReferenceEquals(arg, savedArgs[index])) + { + wrappedArgs[index] = Engine.MarshalToHost(args[index], false); + } + } + result = Engine.MarshalToHost(tempResult, false).ToDynamicResult(Engine); return true; } @@ -139,7 +152,7 @@ private bool TryWrappedInvokeOrInvokeMember(DynamicMetaObjectBinder binder, Para Type[] paramTypes = null; if ((parameters != null) && (parameters.Length >= args.Length)) { - paramTypes = parameters.Skip(parameters.Length - args.Length).Select(parameter => parameter.ParameterType).ToArray(); + paramTypes = parameters.Skip(parameters.Length - args.Length).Select(param => param.ParameterType).ToArray(); } if (paramTypes != null) @@ -190,9 +203,9 @@ private DynamicMetaObject PostProcessBindResult(DynamicMetaObject result) #region DynamicObject overrides - public override DynamicMetaObject GetMetaObject(Expression parameter) + public override DynamicMetaObject GetMetaObject(Expression param) { - return new MetaScriptItem(this, base.GetMetaObject(parameter)); + return new MetaScriptItem(this, base.GetMetaObject(param)); } public override IEnumerable GetDynamicMemberNames() @@ -225,7 +238,7 @@ public override bool TrySetIndex(SetIndexBinder binder, object[] indices, object public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) { ParameterInfo[] parameters = null; - if (binder.GetType().FullName.StartsWith("Microsoft.CSharp.", StringComparison.Ordinal)) + if (Engine.EnableAutoHostVariables) { parameters = new StackFrame(1, false).GetMethod().GetParameters(); } @@ -236,7 +249,7 @@ public override bool TryInvoke(InvokeBinder binder, object[] args, out object re public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { ParameterInfo[] parameters = null; - if (binder.GetType().FullName.StartsWith("Microsoft.CSharp.", StringComparison.Ordinal)) + if (Engine.EnableAutoHostVariables) { parameters = new StackFrame(1, false).GetMethod().GetParameters(); } @@ -406,6 +419,8 @@ public object GetProperty(string name, out bool isCacheable) #region IScriptMarshalWrapper implementation (abstract) + public abstract ScriptEngine Engine { get; } + public abstract object Unwrap(); #endregion @@ -426,6 +441,11 @@ public MetaScriptItem(ScriptItem scriptItem, DynamicMetaObject metaDynamic) #region DynamicMetaObject overrides + public override IEnumerable GetDynamicMemberNames() + { + return metaDynamic.GetDynamicMemberNames(); + } + public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) { return scriptItem.PostProcessBindResult(metaDynamic.BindBinaryOperation(binder, arg)); diff --git a/ClearScript/Util/COMDispatch.cs b/ClearScript/Util/COMDispatch.cs index 1b2613b3e..b3f9b3f31 100644 --- a/ClearScript/Util/COMDispatch.cs +++ b/ClearScript/Util/COMDispatch.cs @@ -61,7 +61,7 @@ using System; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.CustomMarshalers; +using System.Runtime.InteropServices.ComTypes; using DISPPARAMS = System.Runtime.InteropServices.ComTypes.DISPPARAMS; using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; @@ -138,7 +138,7 @@ [Out] out uint count int GetTypeInfo( [In] uint index, [In] int lcid, - [Out] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))] out Type typeInfo + [Out] [MarshalAs(UnmanagedType.Interface)] out ITypeInfo typeInfo ); void GetIDsOfNames( @@ -177,7 +177,7 @@ [Out] out uint count int GetTypeInfo( [In] uint index, [In] int lcid, - [Out] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))] out Type typeInfo + [Out] [MarshalAs(UnmanagedType.Interface)] out ITypeInfo typeInfo ); void GetIDsOfNames( diff --git a/ClearScript/Util/COMDispatchHelpers.cs b/ClearScript/Util/COMDispatchHelpers.cs index 148f76684..cf006edad 100644 --- a/ClearScript/Util/COMDispatchHelpers.cs +++ b/ClearScript/Util/COMDispatchHelpers.cs @@ -73,7 +73,7 @@ public static object GetProperty(this IDispatchEx dispatchEx, string name, bool int dispid; Marshal.ThrowExceptionForHR(dispatchEx.GetDispID(name, ignoreCase ? DispatchNameFlags.CaseInsensitive : DispatchNameFlags.CaseSensitive, out dispid)); - using (var argVariantArrayBlock = new CoTaskMemVariantArrayBlock(args)) + using (var argVariantArrayBlock = new CoTaskMemVariantArgsBlock(args)) { using (var resultVariantBlock = new CoTaskMemVariantBlock()) { @@ -100,7 +100,7 @@ public static void SetProperty(this IDispatchEx dispatchEx, string name, bool ig } Marshal.ThrowExceptionForHR(result); - using (var argVariantArrayBlock = new CoTaskMemVariantArrayBlock(args)) + using (var argVariantArrayBlock = new CoTaskMemVariantArgsBlock(args)) { using (var namedArgDispidBlock = new CoTaskMemBlock(sizeof(int))) { @@ -114,7 +114,7 @@ public static void SetProperty(this IDispatchEx dispatchEx, string name, bool ig public static object Invoke(this IDispatchEx dispatchEx, object[] args) { - using (var argVariantArrayBlock = new CoTaskMemVariantArrayBlock(args)) + using (var argVariantArrayBlock = new CoTaskMemVariantArgsByRefBlock(args)) { using (var resultVariantBlock = new CoTaskMemVariantBlock()) { @@ -131,7 +131,7 @@ public static object InvokeMethod(this IDispatchEx dispatchEx, string name, bool int dispid; Marshal.ThrowExceptionForHR(dispatchEx.GetDispID(name, ignoreCase ? DispatchNameFlags.CaseInsensitive : DispatchNameFlags.CaseSensitive, out dispid)); - using (var argVariantArrayBlock = new CoTaskMemVariantArrayBlock(args)) + using (var argVariantArrayBlock = new CoTaskMemVariantArgsByRefBlock(args)) { using (var resultVariantBlock = new CoTaskMemVariantBlock()) { diff --git a/ClearScript/Util/CoTaskMemBlock.cs b/ClearScript/Util/CoTaskMemBlock.cs index fbfa94d2a..5af59cd2b 100644 --- a/ClearScript/Util/CoTaskMemBlock.cs +++ b/ClearScript/Util/CoTaskMemBlock.cs @@ -156,21 +156,11 @@ protected override void Dispose(bool disposing) #endregion } - internal class CoTaskMemVariantArrayBlock : CoTaskMemBlock + internal class CoTaskMemVariantArgsBlock : CoTaskMemBlock { private readonly int length; - public CoTaskMemVariantArrayBlock(int length) - : base(RawCOMHelpers.VariantSize * length) - { - this.length = length; - for (var index = 0; index < length; index++) - { - NativeMethods.VariantInit(GetAddrInternal(index)); - } - } - - public CoTaskMemVariantArrayBlock(object[] args) + public CoTaskMemVariantArgsBlock(object[] args) : base(args.Length * RawCOMHelpers.VariantSize) { length = args.Length; @@ -192,7 +182,7 @@ public IntPtr GetAddr(int index) throw new ObjectDisposedException(ToString()); } - return GetAddrInternal(index); + return GetAddrInternal(length - 1 - index); } private IntPtr GetAddrInternal(int index) @@ -217,4 +207,63 @@ protected override void Dispose(bool disposing) #endregion } + + internal class CoTaskMemVariantArgsByRefBlock : CoTaskMemBlock + { + private readonly object[] args; + + public CoTaskMemVariantArgsByRefBlock(object[] args) + : base(args.Length * 2 * RawCOMHelpers.VariantSize) + { + this.args = args; + for (var index = 0; index < args.Length; index++) + { + var pArg = GetAddrInternal(args.Length + index); + Marshal.GetNativeVariantForObject(args[index], pArg); + + var pArgRef = GetAddrInternal(args.Length - 1 - index); + NativeMethods.VariantInit(pArgRef); + Marshal.WriteInt16(pArgRef, 0, 0x400C /*VT_BYREF | VT_VARIANT*/); + Marshal.WriteIntPtr(pArgRef, sizeof(ushort) * 4, pArg); + } + } + public IntPtr GetAddr(int index) + { + if ((index < 0) || (index >= args.Length)) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (Addr == IntPtr.Zero) + { + throw new ObjectDisposedException(ToString()); + } + + return GetAddrInternal(args.Length - 1 - index); + } + + private IntPtr GetAddrInternal(int index) + { + return Addr + (index * RawCOMHelpers.VariantSize); + } + + #region CoTaskMemBlock overrides + + protected override void Dispose(bool disposing) + { + if (Addr != IntPtr.Zero) + { + for (var index = 0; index < args.Length; index++) + { + var pArg = GetAddrInternal(args.Length + index); + args[index] = Marshal.GetObjectForNativeVariant(pArg); + NativeMethods.VariantClear(pArg); + } + } + + base.Dispose(disposing); + } + + #endregion + } } diff --git a/ClearScript/Util/DisposedFlag.cs b/ClearScript/Util/DisposedFlag.cs new file mode 100644 index 000000000..c5dd43345 --- /dev/null +++ b/ClearScript/Util/DisposedFlag.cs @@ -0,0 +1,80 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +using System.Threading; + +namespace Microsoft.ClearScript.Util +{ + internal struct DisposedFlag + { + private int disposed; + + public bool IsSet() + { + return disposed != 0; + } + + public bool Set() + { + return Interlocked.Exchange(ref disposed, 1) == 0; + } + } +} diff --git a/ClearScript/Util/IScriptMarshalWrapper.cs b/ClearScript/Util/IScriptMarshalWrapper.cs index c2f80d89a..787703226 100644 --- a/ClearScript/Util/IScriptMarshalWrapper.cs +++ b/ClearScript/Util/IScriptMarshalWrapper.cs @@ -63,6 +63,7 @@ namespace Microsoft.ClearScript.Util { internal interface IScriptMarshalWrapper { + ScriptEngine Engine { get; } object Unwrap(); } } diff --git a/ClearScript/Util/MiscHelpers.cs b/ClearScript/Util/MiscHelpers.cs index 1f374cfb6..10b420ca7 100644 --- a/ClearScript/Util/MiscHelpers.cs +++ b/ClearScript/Util/MiscHelpers.cs @@ -326,6 +326,33 @@ public static string GetDispIDName(int dispid) return FormatInvariant("[DISPID={0}]", dispid); } + public static bool Try(Action action) + { + try + { + action(); + return true; + } + catch (Exception) + { + return false; + } + } + + public static bool Try(out T result, Func func) + { + try + { + result = func(); + return true; + } + catch (Exception) + { + result = default(T); + return false; + } + } + #region Nested type: EmptyArray private static class EmptyArray diff --git a/ClearScript/Util/NativeMethods.cs b/ClearScript/Util/NativeMethods.cs index 9876647cd..254d36952 100644 --- a/ClearScript/Util/NativeMethods.cs +++ b/ClearScript/Util/NativeMethods.cs @@ -71,6 +71,10 @@ public static extern IntPtr LoadLibraryW( [In] [MarshalAs(UnmanagedType.LPWStr)] string path ); + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool FreeLibrary(IntPtr hLibrary); + [DllImport("ole32.dll", ExactSpelling = true)] public static extern uint CLSIDFromProgID( [In] [MarshalAs(UnmanagedType.LPWStr)] string progID, diff --git a/ClearScript/Util/ObjectHelpers.cs b/ClearScript/Util/ObjectHelpers.cs index cf8739a11..7c2089e8f 100644 --- a/ClearScript/Util/ObjectHelpers.cs +++ b/ClearScript/Util/ObjectHelpers.cs @@ -61,13 +61,19 @@ using System; using System.Linq; +using System.Reflection; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.CustomMarshalers; +using System.Runtime.InteropServices.ComTypes; +using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; +using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; namespace Microsoft.ClearScript.Util { internal static class ObjectHelpers { + // GUID_ManagedName (um\cor.h) + private static readonly Guid managedNameGuid = new Guid("{0f21f359-ab84-41e8-9a78-36d110e6d2f9}"); + public static Type GetTypeOrTypeInfo(this object value) { var type = value.GetType(); @@ -84,10 +90,10 @@ public static Type GetTypeOrTypeInfo(this object value) uint count; if (RawCOMHelpers.HResult.Succeeded(dispatch.GetTypeInfoCount(out count)) && (count > 0)) { - Type tempTypeInfo; + ITypeInfo tempTypeInfo; if (RawCOMHelpers.HResult.Succeeded(dispatch.GetTypeInfo(0, 0, out tempTypeInfo))) { - typeInfo = tempTypeInfo; + typeInfo = GetTypeForTypeInfo(tempTypeInfo); } } } @@ -97,10 +103,10 @@ public static Type GetTypeOrTypeInfo(this object value) var provideClassInfo = value as IProvideClassInfo; if (provideClassInfo != null) { - Type tempTypeInfo; + ITypeInfo tempTypeInfo; if (RawCOMHelpers.HResult.Succeeded(provideClassInfo.GetClassInfo(out tempTypeInfo))) { - typeInfo = tempTypeInfo; + typeInfo = GetTypeForTypeInfo(tempTypeInfo); } } } @@ -187,6 +193,176 @@ public static object ToDynamicResult(this object result, ScriptEngine engine) return result; } + private static Type GetTypeForTypeInfo(ITypeInfo typeInfo) + { + // ReSharper disable EmptyGeneralCatchClause + + try + { + ITypeLib typeLib; + int index; + typeInfo.GetContainingTypeLib(out typeLib, out index); + + var assembly = LoadPrimaryInteropAssembly(typeLib); + if (assembly != null) + { + var name = GetManagedTypeInfoName(typeInfo, typeLib); + var guid = GetTypeInfoGuid(typeInfo); + + var type = assembly.GetType(name, false /*throwOnError*/); + if ((type != null) && (type.GUID == guid)) + { + return type; + } + + var types = assembly.GetTypes(); + if ((index >= 0) && (index < types.Length)) + { + type = types[index]; + if ((type.GUID == guid) && (type.FullName == name)) + { + return type; + } + } + + type = types.FirstOrDefault(testType => (testType.GUID == guid) && (testType.FullName == name)); + if (type != null) + { + return type; + } + } + + var pTypeInfo = RawCOMHelpers.QueryInterface(Marshal.GetIUnknownForObject(typeInfo)); + try + { + return Marshal.GetTypeForITypeInfo(pTypeInfo); + } + finally + { + Marshal.Release(pTypeInfo); + } + } + catch (Exception) + { + } + + return null; + + // ReSharper restore EmptyGeneralCatchClause + } + + private static Assembly LoadPrimaryInteropAssembly(ITypeLib typeLib) + { + // ReSharper disable EmptyGeneralCatchClause + + try + { + IntPtr pAttr; + typeLib.GetLibAttr(out pAttr); + try + { + var attr = (TYPELIBATTR)Marshal.PtrToStructure(pAttr, typeof(TYPELIBATTR)); + + string name; + string codeBase; + if (new TypeLibConverter().GetPrimaryInteropAssembly(attr.guid, attr.wMajorVerNum, attr.wMinorVerNum, attr.lcid, out name, out codeBase)) + { + return Assembly.Load(new AssemblyName(name) { CodeBase = codeBase }); + } + } + finally + { + typeLib.ReleaseTLibAttr(pAttr); + } + } + catch (Exception) + { + } + + return null; + + // ReSharper restore EmptyGeneralCatchClause + } + + private static string GetManagedTypeInfoName(ITypeInfo typeInfo, ITypeLib typeLib) + { + var typeInfo2 = typeInfo as ITypeInfo2; + if (typeInfo2 != null) + { + // ReSharper disable EmptyGeneralCatchClause + + try + { + var guid = managedNameGuid; + object data; + typeInfo2.GetCustData(ref guid, out data); + + var name = data as string; + if (name != null) + { + return name.Trim(); + } + } + catch (Exception) + { + } + + // ReSharper restore EmptyGeneralCatchClause + } + + return GetManagedTypeLibName(typeLib) + "." + Marshal.GetTypeInfoName(typeInfo); + } + + private static string GetManagedTypeLibName(ITypeLib typeLib) + { + var typeLib2 = typeLib as ITypeLib2; + if (typeLib2 != null) + { + // ReSharper disable EmptyGeneralCatchClause + + try + { + var guid = managedNameGuid; + object data; + typeLib2.GetCustData(ref guid, out data); + + var name = data as string; + if (name != null) + { + name = name.Trim(); + if (name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || name.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) + { + return name.Substring(0, name.Length - 4); + } + + return name; + } + } + catch (Exception) + { + } + + // ReSharper restore EmptyGeneralCatchClause + } + + return Marshal.GetTypeLibName(typeLib); + } + + private static Guid GetTypeInfoGuid(ITypeInfo typeInfo) + { + IntPtr pAttr; + typeInfo.GetTypeAttr(out pAttr); + try + { + var attr = (TYPEATTR)Marshal.PtrToStructure(pAttr, typeof(TYPEATTR)); + return attr.guid; + } + finally + { + typeInfo.ReleaseTypeAttr(pAttr); + } + } + #region Nested type: IProvideClassInfo [ComImport] @@ -196,7 +372,7 @@ private interface IProvideClassInfo { [PreserveSig] int GetClassInfo( - [Out] [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(TypeToTypeInfoMarshaler))] out Type typeInfo + [Out] [MarshalAs(UnmanagedType.Interface)] out ITypeInfo typeInfo ); } diff --git a/ClearScript/Util/SocketHelpers.cs b/ClearScript/Util/SocketHelpers.cs new file mode 100644 index 000000000..2d8be22eb --- /dev/null +++ b/ClearScript/Util/SocketHelpers.cs @@ -0,0 +1,135 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +using System; +using System.Net.Sockets; + +namespace Microsoft.ClearScript.Util +{ + internal static class SocketHelpers + { + public static void SendBytesAsync(this Socket socket, byte[] bytes, Action callback) + { + socket.SendBytesAsync(bytes, 0, bytes.Length, callback); + } + + public static void SendBytesAsync(this Socket socket, byte[] bytes, int offset, int count, Action callback) + { + if (!MiscHelpers.Try(() => socket.BeginSend(bytes, offset, count, SocketFlags.None, result => socket.OnBytesSent(result, bytes, offset, count, callback), null))) + { + callback(false); + } + } + + public static void OnBytesSent(this Socket socket, IAsyncResult result, byte[] bytes, int offset, int count, Action callback) + { + int sentCount; + if (MiscHelpers.Try(out sentCount, () => socket.EndReceive(result)) && (sentCount > 0)) + { + if (sentCount >= count) + { + callback(true); + } + else + { + socket.SendBytesAsync(bytes, offset + sentCount, count - sentCount, callback); + } + } + else + { + callback(false); + } + } + + public static void ReceiveBytesAsync(this Socket socket, int count, Action callback) + { + socket.ReceiveBytesAsync(new byte[count], 0, count, callback); + } + + public static void ReceiveBytesAsync(this Socket socket, byte[] bytes, int offset, int count, Action callback) + { + if (!MiscHelpers.Try(() => socket.BeginReceive(bytes, offset, count, SocketFlags.None, result => socket.OnBytesReceived(result, bytes, offset, count, callback), null))) + { + callback(null); + } + } + + public static void OnBytesReceived(this Socket socket, IAsyncResult result, byte[] bytes, int offset, int count, Action callback) + { + int receivedCount; + if (MiscHelpers.Try(out receivedCount, () => socket.EndReceive(result)) && (receivedCount > 0)) + { + if (receivedCount >= count) + { + callback(bytes); + } + else + { + socket.ReceiveBytesAsync(bytes, offset + receivedCount, count - receivedCount, callback); + } + } + else + { + callback(null); + } + } + } +} diff --git a/ClearScript/Util/TypeHelpers.cs b/ClearScript/Util/TypeHelpers.cs index d288c7f17..d9fea1e37 100644 --- a/ClearScript/Util/TypeHelpers.cs +++ b/ClearScript/Util/TypeHelpers.cs @@ -338,18 +338,12 @@ public static IEnumerable GetScriptableProperties(this Type type, public static PropertyInfo GetScriptableProperty(this Type type, string name, BindingFlags bindFlags, object[] bindArgs) { - var properties = type.GetScriptableProperties(name, bindFlags).ToArray(); - + var properties = type.GetScriptableProperties(name, bindFlags).Distinct(PropertySignatureComparer.Instance).ToArray(); if (properties.Length < 1) { return null; } - if (bindArgs.Length < 1) - { - return properties.FirstOrDefault(property => property.GetIndexParameters().Length < 1); - } - var result = Type.DefaultBinder.SelectProperty(bindFlags, properties, null, bindArgs.Select(GetPropertyIndexType).ToArray(), null); if (result != null) { @@ -357,9 +351,13 @@ public static PropertyInfo GetScriptableProperty(this Type type, string name, Bi } // the default binder fails to bind to some COM properties because of by-ref parameter types - if ((properties.Length == 1) && (properties[0].GetIndexParameters().Length == bindArgs.Length)) + if (properties.Length == 1) { - return properties[0]; + var parameters = properties[0].GetIndexParameters(); + if ((bindArgs.Length == parameters.Length) || ((bindArgs.Length > 0) && (parameters.Length >= bindArgs.Length))) + { + return properties[0]; + } } return null; @@ -554,5 +552,40 @@ private static Type GetPropertyIndexType(object bindArg) throw new InvalidOperationException("Property index value must not be null"); } + + #region Nested type: PropertySignatureComparer + + private class PropertySignatureComparer : IEqualityComparer + { + private static readonly PropertySignatureComparer instance = new PropertySignatureComparer(); + + public static PropertySignatureComparer Instance { get { return instance; } } + + #region IEqualityComparer implementation + + public bool Equals(PropertyInfo first, PropertyInfo second) + { + var firstParamTypes = first.GetIndexParameters().Select(param => param.ParameterType); + var secondParamTypes = second.GetIndexParameters().Select(param => param.ParameterType); + return firstParamTypes.SequenceEqual(secondParamTypes); + } + + public int GetHashCode(PropertyInfo property) + { + var hashCode = 0; + + var parameters = property.GetIndexParameters(); + foreach (var param in parameters) + { + hashCode = unchecked((hashCode * 31) + param.ParameterType.GetHashCode()); + } + + return hashCode; + } + + #endregion + } + + #endregion } } diff --git a/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj b/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj index 8b800fe03..2a0b40301 100644 --- a/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj +++ b/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj @@ -29,12 +29,9 @@ true Unicode - + v120 - - v110 - @@ -115,6 +112,10 @@ + + false + false + false @@ -137,6 +138,7 @@ false + false @@ -203,6 +205,7 @@ + @@ -217,6 +220,7 @@ + diff --git a/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj.filters b/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj.filters index 8ea4f12c4..fecc7c3ea 100644 --- a/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj.filters +++ b/ClearScript/V8/ClearScriptV8/32/ClearScriptV8-32.vcxproj.filters @@ -52,6 +52,12 @@ Source Files + + Source Files + + + Source Files + @@ -175,5 +181,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj b/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj index ca1520161..5ba108ddc 100644 --- a/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj +++ b/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj @@ -29,12 +29,9 @@ true Unicode - + v120 - - v110 - @@ -116,6 +113,10 @@ + + false + false + false @@ -138,6 +139,7 @@ false + false @@ -204,6 +206,7 @@ + @@ -218,6 +221,7 @@ + diff --git a/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj.filters b/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj.filters index 41d03be66..d0d37bc8c 100644 --- a/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj.filters +++ b/ClearScript/V8/ClearScriptV8/64/ClearScriptV8-64.vcxproj.filters @@ -52,6 +52,12 @@ Source Files + + Source Files + + + Source Files + @@ -175,5 +181,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/ClearScript/V8/ClearScriptV8/ClearScriptV8Managed.h b/ClearScript/V8/ClearScriptV8/ClearScriptV8Managed.h index 41966b677..efdee757f 100644 --- a/ClearScript/V8/ClearScriptV8/ClearScriptV8Managed.h +++ b/ClearScript/V8/ClearScriptV8/ClearScriptV8Managed.h @@ -84,3 +84,4 @@ #include "V8ContextProxyImpl.h" #include "V8ObjectImpl.h" #include "V8ScriptImpl.h" +#include "V8DebugListenerImpl.h" diff --git a/ClearScript/V8/ClearScriptV8/ClearScriptV8Native.h b/ClearScript/V8/ClearScriptV8/ClearScriptV8Native.h index c2f43e31e..8aae6435d 100644 --- a/ClearScript/V8/ClearScriptV8/ClearScriptV8Native.h +++ b/ClearScript/V8/ClearScriptV8/ClearScriptV8Native.h @@ -88,3 +88,4 @@ #include "V8WeakContextBinding.h" #include "V8ObjectHolderImpl.h" #include "V8ScriptHolderImpl.h" +#include "HighResolutionClock.h" diff --git a/ClearScript/V8/ClearScriptV8/CommonPlatform.h b/ClearScript/V8/ClearScriptV8/CommonPlatform.h index e74a99a14..75b3502ab 100644 --- a/ClearScript/V8/ClearScriptV8/CommonPlatform.h +++ b/ClearScript/V8/ClearScriptV8/CommonPlatform.h @@ -124,3 +124,43 @@ } \ while (false) \ __pragma(warning(pop)) + +//----------------------------------------------------------------------------- +// PulseValueScope +//----------------------------------------------------------------------------- + +template +class PulseValueScope +{ + PROHIBIT_COPY(PulseValueScope) + PROHIBIT_HEAP(PulseValueScope) + +public: + + PulseValueScope(T* pValue, T&& value): + m_pValue(pValue), + m_OriginalValue(std::move(*pValue)) + { + *m_pValue = std::move(value); + } + + ~PulseValueScope() + { + *m_pValue = std::move(m_OriginalValue); + } + +private: + + T* m_pValue; + T m_OriginalValue; +}; + +//----------------------------------------------------------------------------- + +#define BEGIN_PULSE_VALUE_SCOPE(ADDRESS, VALUE) \ + { \ + PulseValueScope::type> t_PulseValueScope((ADDRESS), (VALUE)); + +#define END_PULSE_VALUE_SCOPE \ + IGNORE_UNUSED(t_PulseValueScope); \ + } diff --git a/ClearScript/V8/ClearScriptV8/HighResolutionClock.cpp b/ClearScript/V8/ClearScriptV8/HighResolutionClock.cpp new file mode 100644 index 000000000..49f186f6d --- /dev/null +++ b/ClearScript/V8/ClearScriptV8/HighResolutionClock.cpp @@ -0,0 +1,88 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +#include "ClearScriptV8Native.h" +#include + +//----------------------------------------------------------------------------- +// HighResolutionClock implementation +//----------------------------------------------------------------------------- + +static std::once_flag s_InitializationFlag; +static LARGE_INTEGER s_TicksPerSecond; + +//----------------------------------------------------------------------------- + +double HighResolutionClock::GetRelativeSeconds() +{ + std::call_once(s_InitializationFlag, [] + { + ASSERT_EVAL(::QueryPerformanceFrequency(&s_TicksPerSecond)); + }); + + LARGE_INTEGER tickCount; + ASSERT_EVAL(::QueryPerformanceCounter(&tickCount)); + + auto wholeSeconds = tickCount.QuadPart / s_TicksPerSecond.QuadPart; + auto remainingTicks = tickCount.QuadPart % s_TicksPerSecond.QuadPart; + + return wholeSeconds + (static_cast(remainingTicks) / s_TicksPerSecond.QuadPart); +} diff --git a/ClearScript/V8/ClearScriptV8/HighResolutionClock.h b/ClearScript/V8/ClearScriptV8/HighResolutionClock.h new file mode 100644 index 000000000..f2eb0dac6 --- /dev/null +++ b/ClearScript/V8/ClearScriptV8/HighResolutionClock.h @@ -0,0 +1,75 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +#pragma once + +//----------------------------------------------------------------------------- +// HighResolutionClock +//----------------------------------------------------------------------------- + +class HighResolutionClock +{ + PROHIBIT_CONSTRUCT(HighResolutionClock) + +public: + + static double GetRelativeSeconds(); +}; diff --git a/ClearScript/V8/ClearScriptV8/HostException.h b/ClearScript/V8/ClearScriptV8/HostException.h index a4a84a40b..05532a4c6 100644 --- a/ClearScript/V8/ClearScriptV8/HostException.h +++ b/ClearScript/V8/ClearScriptV8/HostException.h @@ -70,7 +70,7 @@ class HostException public: HostException(StdString&& message, V8Value&& exception): - m_Message(std::move(message)), + m_Message(std::move(message)), m_Exception(std::move(exception)) { } diff --git a/ClearScript/V8/ClearScriptV8/HostObjectHelpers.cpp b/ClearScript/V8/ClearScriptV8/HostObjectHelpers.cpp index 27701f71d..048691615 100644 --- a/ClearScript/V8/ClearScriptV8/HostObjectHelpers.cpp +++ b/ClearScript/V8/ClearScriptV8/HostObjectHelpers.cpp @@ -67,9 +67,9 @@ using namespace Microsoft::ClearScript::V8; // local helper functions //----------------------------------------------------------------------------- -static void DECLSPEC_NORETURN ThrowHostException(Exception^ gcException) +static void DECLSPEC_NORETURN ThrowHostException(void* pvSource, Exception^ gcException) { - throw HostException(StdString(gcException->Message), V8ContextProxyImpl::ImportValue(gcException)); + throw HostException(StdString(gcException->GetBaseException()->Message), V8ContextProxyImpl::ImportValue(V8ProxyHelpers::MarshalExceptionToScript(pvSource, gcException))); } //----------------------------------------------------------------------------- @@ -98,7 +98,7 @@ V8Value HostObjectHelpers::GetProperty(void* pvObject, const StdString& name) } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -112,7 +112,7 @@ V8Value HostObjectHelpers::GetProperty(void* pvObject, const StdString& name, bo } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -126,7 +126,7 @@ void HostObjectHelpers::SetProperty(void* pvObject, const StdString& name, const } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -140,7 +140,7 @@ bool HostObjectHelpers::DeleteProperty(void* pvObject, const StdString& name) } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -161,7 +161,7 @@ void HostObjectHelpers::GetPropertyNames(void* pvObject, std::vector& } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -175,7 +175,7 @@ V8Value HostObjectHelpers::GetProperty(void* pvObject, int index) } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -189,7 +189,7 @@ void HostObjectHelpers::SetProperty(void* pvObject, int index, const V8Value& va } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -203,7 +203,7 @@ bool HostObjectHelpers::DeleteProperty(void* pvObject, int index) } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -224,7 +224,7 @@ void HostObjectHelpers::GetPropertyIndices(void* pvObject, std::vector& ind } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -246,7 +246,7 @@ V8Value HostObjectHelpers::Invoke(void* pvObject, const std::vector& ar } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -268,7 +268,7 @@ V8Value HostObjectHelpers::InvokeMethod(void* pvObject, const StdString& name, c } catch (Exception^ gcException) { - ThrowHostException(gcException); + ThrowHostException(pvObject, gcException); } } @@ -316,6 +316,27 @@ bool HostObjectHelpers::RemoveV8ObjectCacheEntry(void* pvCache, void* pvObject) //----------------------------------------------------------------------------- +void* HostObjectHelpers::CreateDebugAgent(const StdString& name, const StdString& version, int port, DebugCallback&& callback) +{ + return V8ProxyHelpers::CreateDebugAgent(name.ToManagedString(), version.ToManagedString(), port, gcnew V8DebugListenerImpl(std::move(callback))); +} + +//----------------------------------------------------------------------------- + +void HostObjectHelpers::SendDebugMessage(void* pvAgent, const StdString& content) +{ + return V8ProxyHelpers::SendDebugMessage(pvAgent, content.ToManagedString()); +} + +//----------------------------------------------------------------------------- + +void HostObjectHelpers::DestroyDebugAgent(void* pvAgent) +{ + V8ProxyHelpers::DestroyDebugAgent(pvAgent); +} + +//----------------------------------------------------------------------------- + bool HostObjectHelpers::TryParseInt32(const StdString& text, int& result) { return Int32::TryParse(text.ToManagedString(), NumberStyles::Integer, CultureInfo::InvariantCulture, result); diff --git a/ClearScript/V8/ClearScriptV8/HostObjectHelpers.h b/ClearScript/V8/ClearScriptV8/HostObjectHelpers.h index f616c6083..0fc456f7b 100644 --- a/ClearScript/V8/ClearScriptV8/HostObjectHelpers.h +++ b/ClearScript/V8/ClearScriptV8/HostObjectHelpers.h @@ -94,5 +94,10 @@ class HostObjectHelpers static void GetAllCachedV8Objects(void* pvCache, std::vector& v8ObjectPtrs); static bool RemoveV8ObjectCacheEntry(void* pvCache, void* pvObject); + typedef std::function DebugCallback; + static void* CreateDebugAgent(const StdString& name, const StdString& version, int port, DebugCallback&& callback); + static void SendDebugMessage(void* pvAgent, const StdString& content); + static void DestroyDebugAgent(void* pvAgent); + static bool TryParseInt32(const StdString& text, int& result); }; diff --git a/ClearScript/V8/ClearScriptV8/V8ContextImpl.cpp b/ClearScript/V8/ClearScriptV8/V8ContextImpl.cpp index 727ed3683..54e493ee4 100644 --- a/ClearScript/V8/ClearScriptV8/V8ContextImpl.cpp +++ b/ClearScript/V8/ClearScriptV8/V8ContextImpl.cpp @@ -165,7 +165,8 @@ static void* UnwrapHostObject(const FunctionCallbackInfo& args) V8ContextImpl::V8ContextImpl(V8IsolateImpl* pIsolateImpl, const StdString& name, bool enableDebugging, bool disableGlobalMembers, int debugPort): m_Name(name), - m_spIsolateImpl(pIsolateImpl) + m_spIsolateImpl(pIsolateImpl), + m_AllowHostObjectFunctionCall(false) { VerifyNotOutOfMemory(); @@ -193,10 +194,11 @@ V8ContextImpl::V8ContextImpl(V8IsolateImpl* pIsolateImpl, const StdString& name, // Such a change will require a corresponding change in the V8ScriptEngine constructor. m_hHostObjectCookieName = CreatePersistent(CreateString(StdString(L"{c2cf47d3-916b-4a3f-be2a-6ff567425808}"))); - m_hInnerExceptionName = CreatePersistent(CreateString(StdString(L"inner"))); + m_hHostExceptionName = CreatePersistent(CreateString(StdString(L"hostException"))); m_hHostObjectTemplate = CreatePersistent(CreateFunctionTemplate()); m_hHostObjectTemplate->SetClassName(CreateString(StdString(L"HostObject"))); + m_hHostObjectTemplate->SetCallHandler(HostObjectFunctionCallHandler, Wrap()); m_hHostObjectTemplate->InstanceTemplate()->SetInternalFieldCount(1); m_hHostObjectTemplate->InstanceTemplate()->SetNamedPropertyHandler(GetHostObjectProperty, SetHostObjectProperty, QueryHostObjectProperty, DeleteHostObjectProperty, GetHostObjectPropertyNames, Wrap()); @@ -295,21 +297,18 @@ void V8ContextImpl::SetGlobalProperty(const StdString& name, const V8Value& valu { if (!hOldValue.IsEmpty() && hOldValue->IsObject()) { - for (auto it = m_GlobalMembersStack.begin(); it != m_GlobalMembersStack.end();) + for (auto it = m_GlobalMembersStack.begin(); it != m_GlobalMembersStack.end(); it++) { - if ((*it)->Equals(hOldValue)) + if (it->first == name) { - it->Dispose(); - it = m_GlobalMembersStack.erase(it); - } - else - { - it++; + it->second.Dispose(); + m_GlobalMembersStack.erase(it); + break; } } } - m_GlobalMembersStack.push_back(CreatePersistent(hValue->ToObject())); + m_GlobalMembersStack.emplace_back(name, CreatePersistent(hValue->ToObject())); } END_CONTEXT_SCOPE @@ -582,11 +581,11 @@ V8ContextImpl::~V8ContextImpl() for (auto it = m_GlobalMembersStack.rbegin(); it != m_GlobalMembersStack.rend(); it++) { - Dispose(*it); + Dispose(it->second); } Dispose(m_hHostObjectTemplate); - Dispose(m_hInnerExceptionName); + Dispose(m_hHostExceptionName); Dispose(m_hHostObjectCookieName); // As of V8 3.16.0, the global property getter for a disposed context @@ -600,7 +599,7 @@ V8ContextImpl::~V8ContextImpl() } Dispose(m_hContext); - V8::ContextDisposedNotification(); + ContextDisposedNotification(); END_ISOLATE_SCOPE } @@ -692,14 +691,14 @@ void V8ContextImpl::GetGlobalProperty(Local hName, const PropertyCallbac auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if ((stack.size() > 0) && !hName->Equals(pContextImpl->m_hHostObjectCookieName)) { for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->HasOwnProperty(hName)) + if (it->second->HasOwnProperty(hName)) { - CALLBACK_RETURN((*it)->Get(hName)); + CALLBACK_RETURN(it->second->Get(hName)); } } } @@ -716,14 +715,14 @@ void V8ContextImpl::SetGlobalProperty(Local hName, Local hValue, auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if ((stack.size() > 0) && !hName->Equals(pContextImpl->m_hHostObjectCookieName)) { for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->HasOwnProperty(hName)) + if (it->second->HasOwnProperty(hName)) { - (*it)->Set(hName, hValue); + it->second->Set(hName, hValue); CALLBACK_RETURN(hValue); } } @@ -741,14 +740,14 @@ void V8ContextImpl::QueryGlobalProperty(Local hName, const PropertyCallb auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if ((stack.size() > 0) && !hName->Equals(pContextImpl->m_hHostObjectCookieName)) { for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->HasOwnProperty(hName)) + if (it->second->HasOwnProperty(hName)) { - CALLBACK_RETURN((*it)->GetPropertyAttributes(hName)); + CALLBACK_RETURN(it->second->GetPropertyAttributes(hName)); } } } @@ -765,22 +764,22 @@ void V8ContextImpl::DeleteGlobalProperty(Local hName, const PropertyCall auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if (stack.size() > 0) { for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->HasOwnProperty(hName)) + if (it->second->HasOwnProperty(hName)) { // WORKAROUND: Object::Delete() crashes if a custom property deleter calls // ThrowException(). Interestingly, there is no crash if the same deleter is // invoked directly from script via the delete operator. - if ((*it)->HasOwnProperty(pContextImpl->m_hHostObjectCookieName)) + if (it->second->HasOwnProperty(pContextImpl->m_hHostObjectCookieName)) { try { - CALLBACK_RETURN(HostObjectHelpers::DeleteProperty(::GetHostObject(*it), StdString(hName))); + CALLBACK_RETURN(HostObjectHelpers::DeleteProperty(::GetHostObject(it->second), StdString(hName))); } catch (const HostException&) { @@ -788,7 +787,7 @@ void V8ContextImpl::DeleteGlobalProperty(Local hName, const PropertyCall } } - CALLBACK_RETURN((*it)->Delete(hName)); + CALLBACK_RETURN(it->second->Delete(hName)); } } } @@ -807,20 +806,20 @@ void V8ContextImpl::GetGlobalPropertyNames(const PropertyCallbackInfo& in { try { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if (stack.size() > 0) { std::vector names; for (auto it = stack.rbegin(); it != stack.rend(); it++) { std::vector tempNames; - if ((*it)->HasOwnProperty(pContextImpl->m_hHostObjectCookieName)) + if (it->second->HasOwnProperty(pContextImpl->m_hHostObjectCookieName)) { - HostObjectHelpers::GetPropertyNames(::GetHostObject(*it), tempNames); + HostObjectHelpers::GetPropertyNames(::GetHostObject(it->second), tempNames); } else { - pContextImpl->GetV8ObjectPropertyNames(*it, tempNames); + pContextImpl->GetV8ObjectPropertyNames(it->second, tempNames); } names.insert(names.end(), tempNames.begin(), tempNames.end()); @@ -856,14 +855,15 @@ void V8ContextImpl::GetGlobalProperty(unsigned __int32 index, const PropertyCall auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if (stack.size() > 0) { + auto hName = pContextImpl->CreateInteger(index)->ToString(); for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->Has(index)) + if (it->second->HasOwnProperty(hName)) { - CALLBACK_RETURN((*it)->Get(index)); + CALLBACK_RETURN(it->second->Get(index)); } } } @@ -880,14 +880,15 @@ void V8ContextImpl::SetGlobalProperty(unsigned __int32 index, Local hValu auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if (stack.size() > 0) { auto hName = pContextImpl->CreateInteger(index)->ToString(); for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->HasOwnProperty(hName) && (*it)->Set(index, hValue)) + if (it->second->HasOwnProperty(hName)) { + it->second->Set(index, hValue); CALLBACK_RETURN(hValue); } } @@ -905,14 +906,16 @@ void V8ContextImpl::QueryGlobalProperty(unsigned __int32 index, const PropertyCa auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if (stack.size() > 0) { + auto hIndex = pContextImpl->CreateInteger(index); + auto hName = hIndex->ToString(); for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->Has(index)) + if (it->second->HasOwnProperty(hName)) { - CALLBACK_RETURN((*it)->GetPropertyAttributes(pContextImpl->CreateInteger(index))); + CALLBACK_RETURN(it->second->GetPropertyAttributes(hIndex)); } } } @@ -929,15 +932,15 @@ void V8ContextImpl::DeleteGlobalProperty(unsigned __int32 index, const PropertyC auto pContextImpl = static_cast(hGlobal->GetAlignedPointerFromInternalField(0)); if (pContextImpl != nullptr) { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if (stack.size() > 0) { auto hName = pContextImpl->CreateInteger(index)->ToString(); for (auto it = stack.rbegin(); it != stack.rend(); it++) { - if ((*it)->HasOwnProperty(hName)) + if (it->second->HasOwnProperty(hName)) { - CALLBACK_RETURN((*it)->Delete(index)); + CALLBACK_RETURN(it->second->Delete(index)); } } } @@ -956,20 +959,20 @@ void V8ContextImpl::GetGlobalPropertyIndices(const PropertyCallbackInfo& { try { - const std::vector>& stack = pContextImpl->m_GlobalMembersStack; + const auto& stack = pContextImpl->m_GlobalMembersStack; if (stack.size() > 0) { std::vector indices; for (auto it = stack.rbegin(); it != stack.rend(); it++) { std::vector tempIndices; - if ((*it)->HasOwnProperty(pContextImpl->m_hHostObjectCookieName)) + if (it->second->HasOwnProperty(pContextImpl->m_hHostObjectCookieName)) { - HostObjectHelpers::GetPropertyIndices(::GetHostObject(*it), tempIndices); + HostObjectHelpers::GetPropertyIndices(::GetHostObject(it->second), tempIndices); } else { - pContextImpl->GetV8ObjectPropertyIndices(*it, tempIndices); + pContextImpl->GetV8ObjectPropertyIndices(it->second, tempIndices); } indices.insert(indices.end(), tempIndices.begin(), tempIndices.end()); @@ -997,6 +1000,15 @@ void V8ContextImpl::GetGlobalPropertyIndices(const PropertyCallbackInfo& //----------------------------------------------------------------------------- +void V8ContextImpl::HostObjectFunctionCallHandler(const FunctionCallbackInfo& info) +{ + auto pContextImpl = ::UnwrapContextImpl(info); + if (!pContextImpl->m_AllowHostObjectFunctionCall) + pContextImpl->ThrowException(Exception::Error(pContextImpl->CreateString(StdString(L"This function is for ClearScript internal use only")))); +} + +//----------------------------------------------------------------------------- + void V8ContextImpl::GetHostObjectProperty(Local hName, const PropertyCallbackInfo& info) { auto pContextImpl = ::UnwrapContextImpl(info); @@ -1327,10 +1339,14 @@ Handle V8ContextImpl::ImportValue(const V8Value& value) return CreateLocal(::ObjectHandleFromPtr(pvV8Object)); } + Local hObject; + BEGIN_PULSE_VALUE_SCOPE(&m_AllowHostObjectFunctionCall, true) + hObject = m_hHostObjectTemplate->InstanceTemplate()->NewInstance(); + END_PULSE_VALUE_SCOPE + // WARNING: Instantiation may fail during script interruption. Check NewInstance() // result to avoid access violations and V8 fatal errors in ::SetObjectHolder(). - auto hObject = m_hHostObjectTemplate->InstanceTemplate()->NewInstance(); if (!hObject.IsEmpty()) { ::SetHostObjectHolder(hObject, pHolder = pHolder->Clone()); @@ -1442,12 +1458,14 @@ void V8ContextImpl::Verify(const TryCatch& tryCatch) StdString message; bool stackOverflow; + bool allowGenericError; StdString value(hException); if (value.GetLength() > 0) { message = std::move(value); stackOverflow = (_wcsicmp(message.ToCString(), L"RangeError: Maximum call stack size exceeded") == 0); + allowGenericError = false; } else { @@ -1456,6 +1474,7 @@ void V8ContextImpl::Verify(const TryCatch& tryCatch) message = L"Unknown error; potential stack overflow detected"; stackOverflow = true; + allowGenericError = true; } #ifdef _DEBUG @@ -1469,13 +1488,14 @@ void V8ContextImpl::Verify(const TryCatch& tryCatch) // from robust. These sanity checks are intended to mitigate this fragility. _ASSERTE(hException->IsObject()); - _ASSERTE(StdString(hException->ToObject()->GetConstructorName()) == L"RangeError"); + StdString constructorName(hException->ToObject()->GetConstructorName()); + _ASSERTE((constructorName == L"RangeError") || (allowGenericError && (constructorName == L"Error"))); } #endif // _DEBUG StdString stackTrace; - V8Value innerException(V8Value::Undefined); + V8Value hostException(V8Value::Undefined); if (stackOverflow) { @@ -1598,11 +1618,11 @@ void V8ContextImpl::Verify(const TryCatch& tryCatch) if (hException->IsObject()) { - innerException = ExportValue(hException->ToObject()->Get(m_hInnerExceptionName)); + hostException = ExportValue(hException->ToObject()->Get(m_hHostExceptionName)); } } - throw V8Exception(V8Exception::Type_General, m_Name, std::move(message), std::move(stackTrace), std::move(innerException)); + throw V8Exception(V8Exception::Type_General, m_Name, std::move(message), std::move(stackTrace), std::move(hostException)); } } @@ -1622,10 +1642,10 @@ void V8ContextImpl::ThrowScriptException(const HostException& exception) { auto hException = Exception::Error(CreateString(exception.GetMessage()))->ToObject(); - auto hInnerException = ImportValue(exception.GetException()); - if (!hInnerException.IsEmpty() && hInnerException->IsObject()) + auto hHostException = ImportValue(exception.GetException()); + if (!hHostException.IsEmpty() && hHostException->IsObject()) { - hException->Set(m_hInnerExceptionName, hInnerException); + hException->Set(m_hHostExceptionName, hHostException); } ThrowException(hException); diff --git a/ClearScript/V8/ClearScriptV8/V8ContextImpl.h b/ClearScript/V8/ClearScriptV8/V8ContextImpl.h index 04f8fc742..29e8e07fc 100644 --- a/ClearScript/V8/ClearScriptV8/V8ContextImpl.h +++ b/ClearScript/V8/ClearScriptV8/V8ContextImpl.h @@ -257,6 +257,21 @@ class V8ContextImpl: public V8Context return m_spIsolateImpl->TerminateExecution(); } + int ContextDisposedNotification() + { + return m_spIsolateImpl->ContextDisposedNotification(); + } + + bool IdleNotification(int idleTimeInMilliseconds) + { + return m_spIsolateImpl->IdleNotification(idleTimeInMilliseconds); + } + + void LowMemoryNotification() + { + m_spIsolateImpl->LowMemoryNotification(); + } + template T Verify(const TryCatch& tryCatch, T result) { @@ -282,6 +297,8 @@ class V8ContextImpl: public V8Context static void DeleteGlobalProperty(unsigned __int32 index, const PropertyCallbackInfo& info); static void GetGlobalPropertyIndices(const PropertyCallbackInfo& info); + static void HostObjectFunctionCallHandler(const FunctionCallbackInfo& info); + static void GetHostObjectProperty(Local hName, const PropertyCallbackInfo& info); static void SetHostObjectProperty(Local hName, Local hValue, const PropertyCallbackInfo& info); static void QueryHostObjectProperty(Local hName, const PropertyCallbackInfo& info); @@ -309,10 +326,11 @@ class V8ContextImpl: public V8Context SharedPtr m_spIsolateImpl; Persistent m_hContext; Persistent m_hGlobal; - std::vector> m_GlobalMembersStack; + std::vector>> m_GlobalMembersStack; Persistent m_hHostObjectCookieName; - Persistent m_hInnerExceptionName; + Persistent m_hHostExceptionName; Persistent m_hHostObjectTemplate; SharedPtr m_spWeakBinding; void* m_pvV8ObjectCache; + bool m_AllowHostObjectFunctionCall; }; diff --git a/ClearScript/V8/ClearScriptV8/V8DebugListenerImpl.cpp b/ClearScript/V8/ClearScriptV8/V8DebugListenerImpl.cpp new file mode 100644 index 000000000..75ecca44d --- /dev/null +++ b/ClearScript/V8/ClearScriptV8/V8DebugListenerImpl.cpp @@ -0,0 +1,136 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +#include "ClearScriptV8Managed.h" + +namespace Microsoft { +namespace ClearScript { +namespace V8 { + + //------------------------------------------------------------------------- + // V8DebugListenerImpl implementation + //------------------------------------------------------------------------- + + V8DebugListenerImpl::V8DebugListenerImpl(HostObjectHelpers::DebugCallback&& callback): + m_gcLock(gcnew Object), + m_pspCallback(new SharedPtr(new HostObjectHelpers::DebugCallback(std::move(callback)))) + { + } + + //------------------------------------------------------------------------- + + void V8DebugListenerImpl::OnMessageReceived(String^ gcCommand) + { + SharedPtr spCallback; + if (TryGetCallback(spCallback)) + { + (*spCallback)(StdString(gcCommand)); + } + } + + //------------------------------------------------------------------------- + + V8DebugListenerImpl::~V8DebugListenerImpl() + { + SharedPtr spCallback; + + BEGIN_LOCK_SCOPE(m_gcLock) + + if (m_pspCallback != nullptr) + { + // hold callback for destruction outside lock scope + spCallback = *m_pspCallback; + delete m_pspCallback; + m_pspCallback = nullptr; + } + + END_LOCK_SCOPE + } + + //------------------------------------------------------------------------- + + V8DebugListenerImpl::!V8DebugListenerImpl() + { + if (m_pspCallback != nullptr) + { + delete m_pspCallback; + m_pspCallback = nullptr; + } + } + + //------------------------------------------------------------------------- + + bool V8DebugListenerImpl::TryGetCallback(SharedPtr& spCallback) + { + BEGIN_LOCK_SCOPE(m_gcLock) + + if (m_pspCallback == nullptr) + { + return false; + } + + spCallback = *m_pspCallback; + return true; + + END_LOCK_SCOPE + } + +}}} diff --git a/ClearScript/V8/ClearScriptV8/V8DebugListenerImpl.h b/ClearScript/V8/ClearScriptV8/V8DebugListenerImpl.h new file mode 100644 index 000000000..7d6d3ebd3 --- /dev/null +++ b/ClearScript/V8/ClearScriptV8/V8DebugListenerImpl.h @@ -0,0 +1,91 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +#pragma once + +namespace Microsoft { +namespace ClearScript { +namespace V8 { + + //------------------------------------------------------------------------- + // V8DebugListenerImpl + //------------------------------------------------------------------------- + + private ref class V8DebugListenerImpl : IV8DebugListener + { + public: + + V8DebugListenerImpl(HostObjectHelpers::DebugCallback&& callback); + + virtual void OnMessageReceived(String^ gcCommand); + + ~V8DebugListenerImpl(); + !V8DebugListenerImpl(); + + private: + + bool TryGetCallback(SharedPtr& spCallback); + + Object^ m_gcLock; + SharedPtr* m_pspCallback; + }; + +}}} diff --git a/ClearScript/V8/ClearScriptV8/V8Exception.cpp b/ClearScript/V8/ClearScriptV8/V8Exception.cpp index a7f6110dd..643af1ebd 100644 --- a/ClearScript/V8/ClearScriptV8/V8Exception.cpp +++ b/ClearScript/V8/ClearScriptV8/V8Exception.cpp @@ -73,7 +73,7 @@ void DECLSPEC_NORETURN V8Exception::ThrowScriptEngineException() const auto gcEngineName = m_EngineName.ToManagedString(); auto gcMessage = m_Message.ToManagedString(); auto gcStackTrace = m_StackTrace.ToManagedString(); - auto gcInnerException = dynamic_cast(V8ContextProxyImpl::ExportValue(m_InnerException)); + auto gcInnerException = V8ProxyHelpers::MarshalExceptionToHost(V8ContextProxyImpl::ExportValue(m_InnerException)); switch (m_Type) { diff --git a/ClearScript/V8/ClearScriptV8/V8Exception.h b/ClearScript/V8/ClearScriptV8/V8Exception.h index 78426ad72..1011c8b3e 100644 --- a/ClearScript/V8/ClearScriptV8/V8Exception.h +++ b/ClearScript/V8/ClearScriptV8/V8Exception.h @@ -78,17 +78,17 @@ class V8Exception V8Exception(Type type, const StdString& engineName, StdString&& message): m_Type(type), - m_EngineName(engineName), - m_Message(std::move(message)), + m_EngineName(engineName), + m_Message(std::move(message)), m_InnerException(V8Value::Undefined) { } V8Exception(Type type, const StdString& engineName, StdString&& message, StdString&& stackTrace, V8Value&& innerException): m_Type(type), - m_EngineName(engineName), - m_Message(std::move(message)), - m_StackTrace(std::move(stackTrace)), + m_EngineName(engineName), + m_Message(std::move(message)), + m_StackTrace(std::move(stackTrace)), m_InnerException(std::move(innerException)) { } diff --git a/ClearScript/V8/ClearScriptV8/V8IsolateImpl.cpp b/ClearScript/V8/ClearScriptV8/V8IsolateImpl.cpp index db8d229cd..3b1194ee5 100644 --- a/ClearScript/V8/ClearScriptV8/V8IsolateImpl.cpp +++ b/ClearScript/V8/ClearScriptV8/V8IsolateImpl.cpp @@ -61,6 +61,73 @@ #include "ClearScriptV8Native.h" +//----------------------------------------------------------------------------- +// V8Platform +//----------------------------------------------------------------------------- + +class V8Platform: public Platform +{ +public: + + static void EnsureInstalled(); + + virtual void CallOnBackgroundThread(Task* pTask, ExpectedRuntime runtime) override; + virtual void CallOnForegroundThread(Isolate* pIsolate, Task* pTask) override; + virtual double MonotonicallyIncreasingTime() override; + +private: + + V8Platform(); + + static V8Platform ms_Instance; + static std::once_flag ms_InstallationFlag; +}; + +//----------------------------------------------------------------------------- + +void V8Platform::EnsureInstalled() +{ + std::call_once(ms_InstallationFlag, [] + { + V8::InitializePlatform(&ms_Instance); + ASSERT_EVAL(V8::Initialize()); + }); +} + +//----------------------------------------------------------------------------- + +void V8Platform::CallOnBackgroundThread(Task* pTask, ExpectedRuntime /*runtime*/) +{ + std::shared_ptr spTask(pTask); + Concurrency::create_task([spTask] { spTask->Run(); }); +} + +//----------------------------------------------------------------------------- + +void V8Platform::CallOnForegroundThread(Isolate* /*pIsolate*/, Task* /*pTask*/) +{ + // unexpected call to unsupported method + std::terminate(); +} + +//----------------------------------------------------------------------------- + +double V8Platform::MonotonicallyIncreasingTime() +{ + return HighResolutionClock::GetRelativeSeconds(); +} + +//----------------------------------------------------------------------------- + +V8Platform::V8Platform() +{ +} + +//----------------------------------------------------------------------------- + +V8Platform V8Platform::ms_Instance; +std::once_flag V8Platform::ms_InstallationFlag; + //----------------------------------------------------------------------------- // V8ArrayBufferAllocator //----------------------------------------------------------------------------- @@ -71,9 +138,9 @@ class V8ArrayBufferAllocator: public ArrayBuffer::Allocator static void EnsureInstalled(); - void* Allocate(size_t size); - void* AllocateUninitialized(size_t size); - void Free(void* pvData, size_t size); + virtual void* Allocate(size_t size) override; + virtual void* AllocateUninitialized(size_t size) override; + virtual void Free(void* pvData, size_t size) override; private: @@ -97,21 +164,21 @@ void V8ArrayBufferAllocator::EnsureInstalled() void* V8ArrayBufferAllocator::Allocate(size_t size) { - return calloc(1, size); + return ::calloc(1, size); } //----------------------------------------------------------------------------- void* V8ArrayBufferAllocator::AllocateUninitialized(size_t size) { - return malloc(size); + return ::malloc(size); } //----------------------------------------------------------------------------- void V8ArrayBufferAllocator::Free(void* pvData, size_t /*size*/) { - free(pvData); + ::free(pvData); } //----------------------------------------------------------------------------- @@ -147,10 +214,6 @@ std::once_flag V8ArrayBufferAllocator::ms_InstallationFlag; //----------------------------------------------------------------------------- -DEFINE_CONCURRENT_CALLBACK_MANAGER(DebugMessageDispatcher, void()) - -//----------------------------------------------------------------------------- - static const size_t s_StackBreathingRoom = static_cast(16 * 1024); static size_t* const s_pMinStackLimit = reinterpret_cast(sizeof(size_t)); @@ -158,7 +221,6 @@ static size_t* const s_pMinStackLimit = reinterpret_cast(sizeof(size_t) V8IsolateImpl::V8IsolateImpl(const StdString& name, const V8IsolateConstraints* pConstraints, bool enableDebugging, int debugPort): m_Name(name), - m_pIsolate(Isolate::New()), m_DebuggingEnabled(false), m_DebugMessageDispatchCount(0), m_MaxHeapSize(0), @@ -168,8 +230,19 @@ V8IsolateImpl::V8IsolateImpl(const StdString& name, const V8IsolateConstraints* m_pStackLimit(nullptr), m_IsOutOfMemory(false) { + V8Platform::EnsureInstalled(); V8ArrayBufferAllocator::EnsureInstalled(); + Isolate::CreateParams params; + if (pConstraints != nullptr) + { + params.constraints.set_max_semi_space_size(pConstraints->GetMaxNewSpaceSize()); + params.constraints.set_max_old_space_size(pConstraints->GetMaxOldSpaceSize()); + params.constraints.set_max_executable_size(pConstraints->GetMaxExecutableSize()); + } + + m_pIsolate = Isolate::New(params); + BEGIN_ADDREF_SCOPE m_pIsolate->SetData(0, this); @@ -177,14 +250,6 @@ V8IsolateImpl::V8IsolateImpl(const StdString& name, const V8IsolateConstraints* BEGIN_ISOLATE_ENTRY_SCOPE V8::SetCaptureStackTraceForUncaughtExceptions(true, 64, StackTrace::kDetailed); - if (pConstraints != nullptr) - { - ResourceConstraints constraints; - constraints.set_max_new_space_size(pConstraints->GetMaxNewSpaceSize()); - constraints.set_max_old_space_size(pConstraints->GetMaxOldSpaceSize()); - constraints.set_max_executable_size(pConstraints->GetMaxExecutableSize()); - ASSERT_EVAL(SetResourceConstraints(m_pIsolate, &constraints)); - } END_ISOLATE_ENTRY_SCOPE @@ -237,28 +302,24 @@ void V8IsolateImpl::EnableDebugging(int debugPort) if (!m_DebuggingEnabled) { + StdString version(String::NewFromUtf8(m_pIsolate, V8::GetVersion())); if (debugPort < 1) { debugPort = 9222; } auto wrIsolate = CreateWeakRef(); - m_pDebugMessageDispatcher = CALLBACK_MANAGER(DebugMessageDispatcher)::Alloc([wrIsolate] + m_pvDebugAgent = HostObjectHelpers::CreateDebugAgent(m_Name, version, debugPort, [wrIsolate] (const StdString& command) { - Concurrency::create_task([wrIsolate] + auto spIsolate = wrIsolate.GetTarget(); + if (!spIsolate.IsEmpty()) { - auto spIsolate = wrIsolate.GetTarget(); - if (!spIsolate.IsEmpty()) - { - auto pIsolateImpl = static_cast(spIsolate.GetRawPtr()); - pIsolateImpl->DispatchDebugMessages(); - } - }); + auto pIsolateImpl = static_cast(spIsolate.GetRawPtr()); + pIsolateImpl->SendDebugCommand(command); + } }); - _ASSERTE(m_pDebugMessageDispatcher); - Debug::SetDebugMessageDispatchHandler(m_pDebugMessageDispatcher); - ASSERT_EVAL(Debug::EnableAgent(*String::Utf8Value(CreateString(m_Name)), debugPort)); + Debug::SetMessageHandler(OnDebugMessageShared); m_DebuggingEnabled = true; m_DebugPort = debugPort; @@ -274,13 +335,8 @@ void V8IsolateImpl::DisableDebugging() if (m_DebuggingEnabled) { - Debug::DisableAgent(); - Debug::SetDebugMessageDispatchHandler(nullptr); - - if (m_pDebugMessageDispatcher != nullptr) - { - ASSERT_EVAL(CALLBACK_MANAGER(DebugMessageDispatcher)::Free(m_pDebugMessageDispatcher)); - } + Debug::SetMessageHandler(nullptr); + HostObjectHelpers::DestroyDebugAgent(m_pvDebugAgent); m_DebuggingEnabled = false; m_DebugMessageDispatchCount = 0; @@ -370,11 +426,11 @@ void V8IsolateImpl::CollectGarbage(bool exhaustive) if (exhaustive) { - V8::LowMemoryNotification(); + LowMemoryNotification(); } else { - while (!V8::IdleNotification()); + while (!IdleNotification(100)); } END_ISOLATE_SCOPE @@ -447,6 +503,13 @@ V8IsolateImpl::~V8IsolateImpl() //----------------------------------------------------------------------------- +void V8IsolateImpl::OnInterruptShared(Isolate* pIsolate, void* /*pvData*/) +{ + static_cast(pIsolate->GetData(0))->OnInterrupt(); +} + +//----------------------------------------------------------------------------- + void V8IsolateImpl::OnInterrupt() { std::function callback; @@ -465,6 +528,41 @@ void V8IsolateImpl::OnInterrupt() //----------------------------------------------------------------------------- +void V8IsolateImpl::SendDebugCommand(const StdString& command) +{ + Debug::SendCommand(m_pIsolate, command.ToCString(), command.GetLength()); + + auto wrIsolate = CreateWeakRef(); + Concurrency::create_task([wrIsolate] + { + auto spIsolate = wrIsolate.GetTarget(); + if (!spIsolate.IsEmpty()) + { + auto pIsolateImpl = static_cast(spIsolate.GetRawPtr()); + pIsolateImpl->DispatchDebugMessages(); + } + }); +} + +//----------------------------------------------------------------------------- + +void V8IsolateImpl::OnDebugMessageShared(const Debug::Message& message) +{ + static_cast(message.GetIsolate()->GetData(0))->OnDebugMessage(message); +} + +//----------------------------------------------------------------------------- + +void V8IsolateImpl::OnDebugMessage(const Debug::Message& message) +{ + if (m_pvDebugAgent) + { + HostObjectHelpers::SendDebugMessage(m_pvDebugAgent, StdString(message.GetJSON())); + } +} + +//----------------------------------------------------------------------------- + void V8IsolateImpl::DispatchDebugMessages() { if (++m_DebugMessageDispatchCount == 1) @@ -546,9 +644,7 @@ void V8IsolateImpl::EnterExecutionScope(size_t* pStackMarker) } // set and record stack address limit - ResourceConstraints constraints; - constraints.set_stack_limit(reinterpret_cast(pStackLimit)); - ASSERT_EVAL(SetResourceConstraints(m_pIsolate, &constraints)); + m_pIsolate->SetStackLimit(reinterpret_cast(pStackLimit)); m_pStackLimit = pStackLimit; // enter outermost stack usage monitoring scope @@ -586,9 +682,7 @@ void V8IsolateImpl::ExitExecutionScope() if (m_pStackLimit != nullptr) { // V8 has no API for removing a stack address limit - ResourceConstraints constraints; - constraints.set_stack_limit(reinterpret_cast(s_pMinStackLimit)); - ASSERT_EVAL(SetResourceConstraints(m_pIsolate, &constraints)); + m_pIsolate->SetStackLimit(reinterpret_cast(s_pMinStackLimit)); m_pStackLimit = nullptr; } } @@ -653,7 +747,7 @@ void V8IsolateImpl::CheckHeapSize(size_t maxHeapSize) if (heapInfo.GetTotalHeapSize() > maxHeapSize) { // yes; collect garbage - V8::LowMemoryNotification(); + LowMemoryNotification(); // is the total heap size still over the limit? GetHeapInfo(heapInfo); diff --git a/ClearScript/V8/ClearScriptV8/V8IsolateImpl.h b/ClearScript/V8/ClearScriptV8/V8IsolateImpl.h index 83be49edf..0f54b38ed 100644 --- a/ClearScript/V8/ClearScriptV8/V8IsolateImpl.h +++ b/ClearScript/V8/ClearScriptV8/V8IsolateImpl.h @@ -245,16 +245,27 @@ class V8IsolateImpl: public V8Isolate V8::TerminateExecution(m_pIsolate); } + int ContextDisposedNotification() + { + return m_pIsolate->ContextDisposedNotification(); + } + + bool IdleNotification(int idleTimeInMilliseconds) + { + return m_pIsolate->IdleNotification(idleTimeInMilliseconds); + } + + void LowMemoryNotification() + { + m_pIsolate->LowMemoryNotification(); + } + void RequestInterrupt(std::function&& callback) { BEGIN_MUTEX_SCOPE(m_InterruptMutex) m_InterruptCallback = std::move(callback); - m_pIsolate->RequestInterrupt([] (Isolate* pIsolate, void* /*pvData*/) - { - static_cast(pIsolate->GetData(0))->OnInterrupt(); - - }, nullptr); + m_pIsolate->RequestInterrupt(OnInterruptShared, nullptr); END_MUTEX_SCOPE } @@ -304,8 +315,12 @@ class V8IsolateImpl: public V8Isolate private: + static void OnInterruptShared(Isolate* pIsolate, void* pvData); void OnInterrupt(); + void SendDebugCommand(const StdString& command); + static void OnDebugMessageShared(const Debug::Message& message); + void OnDebugMessage(const Debug::Message& message); void DispatchDebugMessages(); void ProcessDebugMessages(); @@ -324,7 +339,7 @@ class V8IsolateImpl: public V8Isolate bool m_DebuggingEnabled; int m_DebugPort; - Debug::DebugMessageDispatchHandler m_pDebugMessageDispatcher; + void* m_pvDebugAgent; std::atomic m_DebugMessageDispatchCount; std::atomic m_MaxHeapSize; diff --git a/ClearScript/V8/ClearScriptV8/V8Platform.h b/ClearScript/V8/ClearScriptV8/V8Platform.h index 8a48bb5f5..674a32838 100644 --- a/ClearScript/V8/ClearScriptV8/V8Platform.h +++ b/ClearScript/V8/ClearScriptV8/V8Platform.h @@ -68,6 +68,7 @@ #pragma warning(push, 3) #include "v8.h" +#include "v8-platform.h" #include "v8-debug.h" using namespace v8; @@ -107,29 +108,29 @@ class V8FastPersistent return V8FastPersistent(GetPtrAndClear(hTemp)); } - template - bool operator==(const Handle& hValue) - { - return AsPersistent() == hValue; - } + template + bool operator==(const Handle& hValue) + { + return AsPersistent() == hValue; + } - template - bool operator==(const V8FastPersistent& hValue) - { - return AsPersistent() == hValue.AsPersistent(); - } + template + bool operator==(const V8FastPersistent& hValue) + { + return AsPersistent() == hValue.AsPersistent(); + } - template - bool operator!=(const Handle& hValue) - { - return AsPersistent() != hValue; - } + template + bool operator!=(const Handle& hValue) + { + return AsPersistent() != hValue; + } - template - bool operator!=(const V8FastPersistent& hValue) - { - return AsPersistent() != hValue.AsPersistent(); - } + template + bool operator!=(const V8FastPersistent& hValue) + { + return AsPersistent() != hValue.AsPersistent(); + } bool IsEmpty() const { @@ -278,29 +279,29 @@ class V8SafePersistent return V8SafePersistent(new Persistent(pIsolate, hValue.GetImpl())); } - template - bool operator==(const Handle& hValue) - { - return GetImpl() == hValue; - } - - template - bool operator==(const V8SafePersistent& hValue) - { - return GetImpl() == hValue.GetImpl(); - } - - template - bool operator!=(const Handle& hValue) - { - return GetImpl() != hValue; - } - - template - bool operator!=(const V8SafePersistent& hValue) - { - return GetImpl() != hValue.GetImpl(); - } + template + bool operator==(const Handle& hValue) + { + return GetImpl() == hValue; + } + + template + bool operator==(const V8SafePersistent& hValue) + { + return GetImpl() == hValue.GetImpl(); + } + + template + bool operator!=(const Handle& hValue) + { + return GetImpl() != hValue; + } + + template + bool operator!=(const V8SafePersistent& hValue) + { + return GetImpl() != hValue.GetImpl(); + } bool IsEmpty() const { diff --git a/ClearScript/V8/ClearScriptV8/V8WeakContextBinding.h b/ClearScript/V8/ClearScriptV8/V8WeakContextBinding.h index 37241251f..523cc0458 100644 --- a/ClearScript/V8/ClearScriptV8/V8WeakContextBinding.h +++ b/ClearScript/V8/ClearScriptV8/V8WeakContextBinding.h @@ -111,7 +111,6 @@ class V8WeakContextBinding: public SharedPtrTarget throw V8Exception(V8Exception::Type_General, m_ContextName, StdString(L"The V8 script engine has been destroyed")); } - bool TryGetContextImpl(SharedPtr& spContextImpl) const { auto spContext = m_wrContext.GetTarget(); diff --git a/ClearScript/V8/IV8DebugListener.cs b/ClearScript/V8/IV8DebugListener.cs new file mode 100644 index 000000000..46e8cd3da --- /dev/null +++ b/ClearScript/V8/IV8DebugListener.cs @@ -0,0 +1,70 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +using System; + +namespace Microsoft.ClearScript.V8 +{ + internal interface IV8DebugListener: IDisposable + { + void OnMessageReceived(string command); + } +} diff --git a/ClearScript/V8/V8/LICENSE.v8 b/ClearScript/V8/V8/LICENSE.v8 new file mode 100644 index 000000000..933718a9e --- /dev/null +++ b/ClearScript/V8/V8/LICENSE.v8 @@ -0,0 +1,26 @@ +Copyright 2006-2011, the V8 project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ClearScript/V8/V8/V8Patch.txt b/ClearScript/V8/V8/V8Patch.txt index 4b8c5773e..46337def7 100644 --- a/ClearScript/V8/V8/V8Patch.txt +++ b/ClearScript/V8/V8/V8Patch.txt @@ -1,7 +1,59 @@ -Index: tools/gyp/v8.gyp -=================================================================== ---- tools/gyp/v8.gyp (revision 24646) -+++ tools/gyp/v8.gyp (working copy) +diff --git a/src/api.cc b/src/api.cc +index 239c7ac..be3cfc8 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -5171,10 +5171,6 @@ void v8::V8::SetReturnAddressLocationResolver( + + void v8::V8::SetArrayBufferAllocator( + ArrayBuffer::Allocator* allocator) { +- if (!Utils::ApiCheck(i::V8::ArrayBufferAllocator() == NULL, +- "v8::V8::SetArrayBufferAllocator", +- "ArrayBufferAllocator might only be set once")) +- return; + i::V8::SetArrayBufferAllocator(allocator); + } + +diff --git a/src/objects.cc b/src/objects.cc +index a4c3bea..e78888a 100644 +--- a/src/objects.cc ++++ b/src/objects.cc +@@ -6229,7 +6229,7 @@ MaybeHandle JSObject::DefineAccessor(Handle object, + if (is_observed) { + if (is_element) { + Maybe maybe = HasOwnElement(object, index); +- // Workaround for a GCC 4.4.3 bug which leads to "‘preexists’ may be used ++ // Workaround for a GCC 4.4.3 bug which leads to "'preexists' may be used + // uninitialized in this function". + if (!maybe.has_value) { + DCHECK(false); +diff --git a/src/v8.cc b/src/v8.cc +index 62c3da4..ab97c36 100644 +--- a/src/v8.cc ++++ b/src/v8.cc +@@ -100,7 +100,6 @@ void V8::InitializeOncePerProcess() { + + + void V8::InitializePlatform(v8::Platform* platform) { +- CHECK(!platform_); + CHECK(platform); + platform_ = platform; + } +diff --git a/src/v8.h b/src/v8.h +index 13c33e1..c89fc1d 100644 +--- a/src/v8.h ++++ b/src/v8.h +@@ -74,7 +74,6 @@ class V8 : public AllStatic { + } + + static void SetArrayBufferAllocator(v8::ArrayBuffer::Allocator *allocator) { +- CHECK_EQ(NULL, array_buffer_allocator_); + array_buffer_allocator_ = allocator; + } + +diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp +index 5874865..52f6039 100644 +--- a/tools/gyp/v8.gyp ++++ b/tools/gyp/v8.gyp @@ -35,6 +35,7 @@ 'targets': [ { diff --git a/ClearScript/V8/V8DebugAgent.cs b/ClearScript/V8/V8DebugAgent.cs new file mode 100644 index 000000000..3fc28ef35 --- /dev/null +++ b/ClearScript/V8/V8DebugAgent.cs @@ -0,0 +1,376 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Microsoft Public License (MS-PL) +// +// This license governs use of the accompanying software. If you use the +// software, you accept this license. If you do not accept the license, do not +// use the software. +// +// 1. Definitions +// +// The terms "reproduce," "reproduction," "derivative works," and +// "distribution" have the same meaning here as under U.S. copyright law. A +// "contribution" is the original software, or any additions or changes to +// the software. A "contributor" is any person that distributes its +// contribution under this license. "Licensed patents" are a contributor's +// patent claims that read directly on its contribution. +// +// 2. Grant of Rights +// +// (A) Copyright Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free copyright license +// to reproduce its contribution, prepare derivative works of its +// contribution, and distribute its contribution or any derivative works +// that you create. +// +// (B) Patent Grant- Subject to the terms of this license, including the +// license conditions and limitations in section 3, each contributor +// grants you a non-exclusive, worldwide, royalty-free license under its +// licensed patents to make, have made, use, sell, offer for sale, +// import, and/or otherwise dispose of its contribution in the software +// or derivative works of the contribution in the software. +// +// 3. Conditions and Limitations +// +// (A) No Trademark License- This license does not grant you rights to use +// any contributors' name, logo, or trademarks. +// +// (B) If you bring a patent claim against any contributor over patents that +// you claim are infringed by the software, your patent license from such +// contributor to the software ends automatically. +// +// (C) If you distribute any portion of the software, you must retain all +// copyright, patent, trademark, and attribution notices that are present +// in the software. +// +// (D) If you distribute any portion of the software in source code form, you +// may do so only under this license by including a complete copy of this +// license with your distribution. If you distribute any portion of the +// software in compiled or object code form, you may only do so under a +// license that complies with this license. +// +// (E) The software is licensed "as-is." You bear the risk of using it. The +// contributors give no express warranties, guarantees or conditions. You +// may have additional consumer rights under your local laws which this +// license cannot change. To the extent permitted under your local laws, +// the contributors exclude the implied warranties of merchantability, +// fitness for a particular purpose and non-infringement. +// + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using Microsoft.ClearScript.Util; + +namespace Microsoft.ClearScript.V8 +{ + internal sealed class V8DebugAgent : IDisposable + { + #region data + + private readonly string name; + private readonly string version; + private readonly IV8DebugListener listener; + + private TcpListener tcpListener; + private TcpClient tcpClient; + + private readonly ConcurrentQueue queue = new ConcurrentQueue(); + private readonly AutoResetEvent queueEvent = new AutoResetEvent(false); + private RegisteredWaitHandle queueWaitHandle; + + private DisposedFlag disposedFlag = new DisposedFlag(); + + #endregion + + #region constructors + + public V8DebugAgent(string name, string version, int port, IV8DebugListener listener) + { + this.name = name; + this.version = version; + this.listener = listener; + + RegisterWaitForQueueEvent(); + + MiscHelpers.Try(() => + { + tcpListener = new TcpListener(IPAddress.Loopback, port); + tcpListener.Start(); + tcpListener.BeginAcceptTcpClient(OnClientAccepted, null); + }); + } + + #endregion + + #region session management + + private void OnClientAccepted(IAsyncResult result) + { + if (!disposedFlag.IsSet()) + { + TcpClient tempTcpClient; + if (MiscHelpers.Try(out tempTcpClient, () => tcpListener.EndAcceptTcpClient(result))) + { + if (Interlocked.CompareExchange(ref tcpClient, tempTcpClient, null) == null) + { + ConnectClient(); + } + else + { + RejectClient(tempTcpClient); + } + + MiscHelpers.Try(() => tcpListener.BeginAcceptTcpClient(OnClientAccepted, null)); + } + } + } + + private void ConnectClient() + { + SendStringAsync(tcpClient, "Type:connect\r\nV8-Version:" + version + "\r\nProtocol-Version:1\r\nEmbedding-Host:" + name + "\r\nContent-Length:0\r\n\r\n", OnConnectionMessageSent); + } + + private void OnConnectionMessageSent(bool succeeded) + { + if (succeeded) + { + ReceiveMessage(); + } + else + { + DisconnectClient("Could not send connection message"); + } + } + + private static void RejectClient(TcpClient tcpClient) + { + SendStringAsync(tcpClient, "Remote debugging session already active\r\n", succeeded => MiscHelpers.Try(tcpClient.Close)); + } + + private void DisconnectClient(string errorMessage) + { + var tempTcpClient = Interlocked.Exchange(ref tcpClient, null); + if (tempTcpClient != null) + { + if (!string.IsNullOrWhiteSpace(errorMessage)) + { + Trace("Disconnecting debugger: " + errorMessage); + } + + MiscHelpers.Try(tempTcpClient.Close); + } + } + + #endregion + + #region inbound message processing + + private void ReceiveMessage() + { + ReceiveLineAsync(line => OnHeaderLineReceived(line, -1)); + } + + private void OnHeaderLineReceived(string line, int contentLength) + { + if (line != null) + { + if (!string.IsNullOrWhiteSpace(line)) + { + var segments = line.Split(':'); + if ((segments.Length == 2) && (segments[0] == "Content-Length")) + { + int length; + if (int.TryParse(segments[1], out length)) + { + contentLength = length; + } + } + + ReceiveLineAsync(nextLine => OnHeaderLineReceived(nextLine, contentLength)); + } + else if (contentLength > 0) + { + ReceiveStringAsync(contentLength, OnMessageReceived); + } + else if (contentLength == 0) + { + OnMessageReceived(string.Empty); + } + else + { + OnMessageReceivedInternal(null, "Message content length not specified"); + } + } + else + { + OnMessageReceivedInternal(null, "Could not receive message header"); + } + } + + void OnMessageReceived(string content) + { + OnMessageReceivedInternal(content, "Could not receive message content"); + } + + void OnMessageReceivedInternal(string content, string errorMessage) + { + bool disconnect; + if (content != null) + { + disconnect = content.Contains("\"type\":\"request\",\"command\":\"disconnect\"}"); + } + else + { + content = "{\"seq\":1,\"type\":\"request\",\"command\":\"disconnect\"}"; + disconnect = true; + } + + listener.OnMessageReceived(content); + + if (disconnect) + { + DisconnectClient(errorMessage); + } + else + { + ReceiveMessage(); + } + } + + #endregion + + #region outbound message queue + + public void SendMessage(string content) + { + if (!disposedFlag.IsSet()) + { + queue.Enqueue(content); + MiscHelpers.Try(() => queueEvent.Set()); + } + } + + private void RegisterWaitForQueueEvent() + { + var oldQueueWaitHandle = Interlocked.Exchange(ref queueWaitHandle, ThreadPool.RegisterWaitForSingleObject(queueEvent, OnQueueEvent, null, Timeout.Infinite, true)); + if (oldQueueWaitHandle != null) + { + oldQueueWaitHandle.Unregister(null); + } + } + + private void OnQueueEvent(object state, bool timedOut) + { + string content; + while (queue.TryDequeue(out content)) + { + var tempTcpClient = tcpClient; + if (tempTcpClient != null) + { + var contentBytes = Encoding.UTF8.GetBytes(content); + var headerBytes = Encoding.UTF8.GetBytes(MiscHelpers.FormatInvariant("Content-Length:{0}\r\n\r\n", contentBytes.Length)); + tempTcpClient.Client.SendBytesAsync(headerBytes.Concat(contentBytes).ToArray(), OnMessageSent); + return; + } + } + + if (!disposedFlag.IsSet()) + { + RegisterWaitForQueueEvent(); + } + } + + private void OnMessageSent(bool succeeded) + { + if (succeeded) + { + OnQueueEvent(null, false); + } + else + { + DisconnectClient("Could not send message"); + } + } + + #endregion + + #region protocol helpers + + private static void SendStringAsync(TcpClient tcpClient, string content, Action callback) + { + tcpClient.Client.SendBytesAsync(Encoding.UTF8.GetBytes(content), callback); + } + + private void ReceiveStringAsync(int sizeInBytes, Action callback) + { + tcpClient.Client.ReceiveBytesAsync(sizeInBytes, bytes => callback((bytes != null) ? Encoding.UTF8.GetString(bytes) : null)); + } + + private void ReceiveLineAsync(Action callback) + { + var lineBytes = new List(1024); + tcpClient.Client.ReceiveBytesAsync(1, bytes => OnLineBytesReceived(bytes, lineBytes, callback)); + } + + private void OnLineBytesReceived(byte[] bytes, List lineBytes, Action callback) + { + if (bytes != null) + { + var lastIndex = lineBytes.Count - 1; + if ((lastIndex >= 0) && (lineBytes[lastIndex] == Convert.ToByte('\r')) && (bytes[0] == Convert.ToByte('\n'))) + { + lineBytes.RemoveAt(lastIndex); + callback(Encoding.UTF8.GetString(lineBytes.ToArray())); + } + else + { + lineBytes.Add(bytes[0]); + tcpClient.Client.ReceiveBytesAsync(1, nextBytes => OnLineBytesReceived(nextBytes, lineBytes, callback)); + } + } + else + { + callback(null); + } + } + + #endregion + + #region diagnostics + + [Conditional("DEBUG")] + void Trace(string message) + { + Debug.WriteLine(message); + } + + #endregion + + #region IDisposable implementation + + public void Dispose() + { + if (disposedFlag.Set()) + { + MiscHelpers.Try(tcpListener.Stop); + DisconnectClient(null); + + queueWaitHandle.Unregister(null); + queueEvent.Close(); + + listener.Dispose(); + } + } + + #endregion + } +} diff --git a/ClearScript/V8/V8Proxy.cs b/ClearScript/V8/V8Proxy.cs index b41dddcd3..a67428a23 100644 --- a/ClearScript/V8/V8Proxy.cs +++ b/ClearScript/V8/V8Proxy.cs @@ -126,30 +126,36 @@ private static Assembly LoadAssembly() { } - LoadNativeLibrary(); - - var suffix = Environment.Is64BitProcess ? "64" : "32"; - var fileName = "ClearScriptV8-" + suffix + ".dll"; - var messageBuilder = new StringBuilder(); - - var paths = GetDirPaths().Select(dirPath => Path.Combine(dirPath, deploymentDirName, fileName)).Distinct(); - foreach (var path in paths) + var hLibrary = LoadNativeLibrary(); + try { - try - { - return Assembly.LoadFrom(path); - } - catch (Exception exception) + var suffix = Environment.Is64BitProcess ? "64" : "32"; + var fileName = "ClearScriptV8-" + suffix + ".dll"; + var messageBuilder = new StringBuilder(); + + var paths = GetDirPaths().Select(dirPath => Path.Combine(dirPath, deploymentDirName, fileName)).Distinct(); + foreach (var path in paths) { - messageBuilder.AppendInvariant("\n{0}: {1}", path, MiscHelpers.EnsureNonBlank(exception.Message, "Unknown error")); + try + { + return Assembly.LoadFrom(path); + } + catch (Exception exception) + { + messageBuilder.AppendInvariant("\n{0}: {1}", path, MiscHelpers.EnsureNonBlank(exception.Message, "Unknown error")); + } } - } - var message = MiscHelpers.FormatInvariant("Cannot load V8 interface assembly. Load failure information for {0}:{1}", fileName, messageBuilder); - throw new TypeLoadException(message); + var message = MiscHelpers.FormatInvariant("Cannot load V8 interface assembly. Load failure information for {0}:{1}", fileName, messageBuilder); + throw new TypeLoadException(message); + } + finally + { + NativeMethods.FreeLibrary(hLibrary); + } } - private static void LoadNativeLibrary() + private static IntPtr LoadNativeLibrary() { var suffix = Environment.Is64BitProcess ? "x64" : "ia32"; var fileName = "v8-" + suffix + ".dll"; @@ -161,7 +167,7 @@ private static void LoadNativeLibrary() var hLibrary = NativeMethods.LoadLibraryW(path); if (hLibrary != IntPtr.Zero) { - return; + return hLibrary; } var exception = new Win32Exception(Marshal.GetLastWin32Error()); diff --git a/ClearScript/V8/V8ProxyHelpers.cs b/ClearScript/V8/V8ProxyHelpers.cs index 0c136146b..661603445 100644 --- a/ClearScript/V8/V8ProxyHelpers.cs +++ b/ClearScript/V8/V8ProxyHelpers.cs @@ -221,37 +221,71 @@ public static object InvokeHostObjectMethod(object obj, string name, object[] ar #endregion + #region exception marshaling + + public static unsafe object MarshalExceptionToScript(void* pSource, Exception exception) + { + return MarshalExceptionToScript(GetHostObject(pSource), exception); + } + + public static object MarshalExceptionToScript(object source, Exception exception) + { + return ((IScriptMarshalWrapper)source).Engine.MarshalToScript(exception); + } + + public static Exception MarshalExceptionToHost(object exception) + { + return (exception != null) ? (Exception)((IScriptMarshalWrapper)exception).Engine.MarshalToHost(exception, false) : null; + } + + #endregion + #region V8 object cache public static unsafe void* CreateV8ObjectCache() { - var cache = new Dictionary(); - return AddRefHostObject(cache); + return AddRefHostObject(new Dictionary()); } - public static unsafe void CacheV8Object(void* pV8ObjectCache, void* pObject, void* pV8Object) + public static unsafe void CacheV8Object(void* pCache, void* pObject, void* pV8Object) { - var cache = (Dictionary)GetHostObject(pV8ObjectCache); - cache.Add(GetHostObject(pObject), (IntPtr)pV8Object); + ((Dictionary)GetHostObject(pCache)).Add(GetHostObject(pObject), (IntPtr)pV8Object); } - public static unsafe void* GetCachedV8Object(void* pV8ObjectCache, void* pObject) + public static unsafe void* GetCachedV8Object(void* pCache, void* pObject) { IntPtr pV8Object; - var cache = (Dictionary)GetHostObject(pV8ObjectCache); - return cache.TryGetValue(GetHostObject(pObject), out pV8Object) ? pV8Object.ToPointer() : null; + return ((Dictionary)GetHostObject(pCache)).TryGetValue(GetHostObject(pObject), out pV8Object) ? pV8Object.ToPointer() : null; + } + + public static unsafe IntPtr[] GetAllCachedV8Objects(void* pCache) + { + return ((Dictionary)GetHostObject(pCache)).Values.ToArray(); + } + + public static unsafe bool RemoveV8ObjectCacheEntry(void* pCache, void* pObject) + { + return ((Dictionary)GetHostObject(pCache)).Remove(GetHostObject(pObject)); + } + + #endregion + + #region V8 debug agent + + public static unsafe void* CreateDebugAgent(string name, string version, int port, IV8DebugListener listener) + { + return AddRefHostObject(new V8DebugAgent(name, version, port, listener)); } - public static unsafe IntPtr[] GetAllCachedV8Objects(void* pV8ObjectCache) + public static unsafe void SendDebugMessage(void* pAgent, string content) { - var cache = (Dictionary)GetHostObject(pV8ObjectCache); - return cache.Values.ToArray(); + ((V8DebugAgent)GetHostObject(pAgent)).SendMessage(content); } - public static unsafe bool RemoveV8ObjectCacheEntry(void* pV8ObjectCache, void* pObject) + public static unsafe void DestroyDebugAgent(void* pAgent) { - var cache = (Dictionary)GetHostObject(pV8ObjectCache); - return cache.Remove(GetHostObject(pObject)); + ((V8DebugAgent)GetHostObject(pAgent)).Dispose(); + ReleaseHostObject(pAgent); } #endregion diff --git a/ClearScript/V8/V8Runtime.cs b/ClearScript/V8/V8Runtime.cs index d7b2afdab..13291aed8 100644 --- a/ClearScript/V8/V8Runtime.cs +++ b/ClearScript/V8/V8Runtime.cs @@ -77,7 +77,7 @@ public sealed class V8Runtime : IDisposable private readonly IUniqueNameManager documentNameManager = new UniqueFileNameManager(); private readonly V8IsolateProxy proxy; - private bool disposed; + private DisposedFlag disposedFlag = new DisposedFlag(); #endregion @@ -477,7 +477,7 @@ internal V8IsolateProxy IsolateProxy private void VerifyNotDisposed() { - if (disposed) + if (disposedFlag.IsSet()) { throw new ObjectDisposedException(ToString()); } @@ -498,10 +498,9 @@ private void VerifyNotDisposed() /// public void Dispose() { - if (!disposed) + if (disposedFlag.Set()) { proxy.Dispose(); - disposed = true; } } diff --git a/ClearScript/V8/V8ScriptEngine.cs b/ClearScript/V8/V8ScriptEngine.cs index 7e3c73abf..b1528f10e 100644 --- a/ClearScript/V8/V8ScriptEngine.cs +++ b/ClearScript/V8/V8ScriptEngine.cs @@ -86,7 +86,7 @@ public sealed class V8ScriptEngine : ScriptEngine private readonly V8ScriptEngineFlags engineFlags; private readonly V8ContextProxy proxy; private readonly object script; - private bool disposed; + private DisposedFlag disposedFlag = new DisposedFlag(); private const int continuationInterval = 2000; private bool inContinuationTimerScope; @@ -507,7 +507,7 @@ private T BaseScriptInvoke(Func func) private void VerifyNotDisposed() { - if (disposed) + if (disposedFlag.IsSet()) { throw new ObjectDisposedException(ToString()); } @@ -846,15 +846,13 @@ internal override T ScriptInvoke(Func func) /// protected override void Dispose(bool disposing) { - if (!disposed) + if (disposedFlag.Set()) { if (disposing) { ((IDisposable)script).Dispose(); proxy.Dispose(); } - - disposed = true; } } diff --git a/ClearScript/V8/V8ScriptItem.cs b/ClearScript/V8/V8ScriptItem.cs index 4cdf23e16..18b2e1ced 100644 --- a/ClearScript/V8/V8ScriptItem.cs +++ b/ClearScript/V8/V8ScriptItem.cs @@ -71,7 +71,7 @@ internal class V8ScriptItem : ScriptItem, IDisposable private readonly V8ScriptEngine engine; private readonly IV8Object target; private V8ScriptItem holder; - private bool disposed; + private DisposedFlag disposedFlag = new DisposedFlag(); private V8ScriptItem(V8ScriptEngine engine, IV8Object target) { @@ -99,7 +99,7 @@ public static object Wrap(V8ScriptEngine engine, object obj) private void VerifyNotDisposed() { - if (disposed) + if (disposedFlag.IsSet()) { throw new ObjectDisposedException(ToString()); } @@ -107,11 +107,6 @@ private void VerifyNotDisposed() #region ScriptItem overrides - public override ScriptEngine Engine - { - get { return engine; } - } - protected override bool TryBindAndInvoke(DynamicMetaObjectBinder binder, object[] args, out object result) { VerifyNotDisposed(); @@ -293,6 +288,11 @@ public override object InvokeMethod(string name, object[] args) #region IScriptMarshalWrapper implementation + public override ScriptEngine Engine + { + get { return engine; } + } + public override object Unwrap() { return target; @@ -304,10 +304,9 @@ public override object Unwrap() public void Dispose() { - if (!disposed) + if (disposedFlag.Set()) { target.Dispose(); - disposed = true; } } diff --git a/ClearScript/Windows/WindowsScriptEngine.cs b/ClearScript/Windows/WindowsScriptEngine.cs index 24757ecf6..347415f16 100644 --- a/ClearScript/Windows/WindowsScriptEngine.cs +++ b/ClearScript/Windows/WindowsScriptEngine.cs @@ -102,7 +102,7 @@ public abstract partial class WindowsScriptEngine : ScriptEngine private uint nextSourceContext = 1; private readonly Dispatcher dispatcher = Dispatcher.CurrentDispatcher; - private bool disposed; + private DisposedFlag disposedFlag = new DisposedFlag(); #endregion @@ -307,7 +307,7 @@ private string GetStackTraceInternal() private void VerifyNotDisposed() { - if (disposed) + if (disposedFlag.IsSet()) { throw new ObjectDisposedException(ToString()); } @@ -761,7 +761,7 @@ internal override T SyncInvoke(Func func) /// protected override void Dispose(bool disposing) { - if (!disposed) + if (disposedFlag.Set()) { if (disposing) { @@ -779,8 +779,6 @@ protected override void Dispose(bool disposing) ((IDisposable)script).Dispose(); activeScript.Close(); } - - disposed = true; } } diff --git a/ClearScript/Windows/WindowsScriptItem.cs b/ClearScript/Windows/WindowsScriptItem.cs index 95148d8f7..87da269b7 100644 --- a/ClearScript/Windows/WindowsScriptItem.cs +++ b/ClearScript/Windows/WindowsScriptItem.cs @@ -76,7 +76,7 @@ internal class WindowsScriptItem : ScriptItem, IDisposable private readonly WindowsScriptEngine engine; private readonly IExpando target; private WindowsScriptItem holder; - private bool disposed; + private DisposedFlag disposedFlag = new DisposedFlag(); private WindowsScriptItem(WindowsScriptEngine engine, IExpando target) { @@ -176,7 +176,7 @@ private bool TryGetScriptError(Exception exception, out IScriptEngineException s private void VerifyNotDisposed() { - if (disposed) + if (disposedFlag.IsSet()) { throw new ObjectDisposedException(ToString()); } @@ -184,11 +184,6 @@ private void VerifyNotDisposed() #region ScriptItem overrides - public override ScriptEngine Engine - { - get { return engine; } - } - protected override bool TryBindAndInvoke(DynamicMetaObjectBinder binder, object[] args, out object result) { VerifyNotDisposed(); @@ -369,6 +364,11 @@ public override object InvokeMethod(string name, object[] args) #region IScriptMarshalWrapper implementation + public override ScriptEngine Engine + { + get { return engine; } + } + public override object Unwrap() { return target; @@ -380,10 +380,9 @@ public override object Unwrap() public void Dispose() { - if (!disposed) + if (disposedFlag.Set()) { Marshal.ReleaseComObject(target); - disposed = true; } } diff --git a/ClearScript/doc/Reference.chm b/ClearScript/doc/Reference.chm index 3d9f9df10..736402605 100644 Binary files a/ClearScript/doc/Reference.chm and b/ClearScript/doc/Reference.chm differ diff --git a/ClearScriptBenchmarks/Properties/AssemblyInfo.cs b/ClearScriptBenchmarks/Properties/AssemblyInfo.cs index 84c5ecebc..a97dedb9b 100644 --- a/ClearScriptBenchmarks/Properties/AssemblyInfo.cs +++ b/ClearScriptBenchmarks/Properties/AssemblyInfo.cs @@ -69,5 +69,5 @@ [assembly: AssemblyCopyright("(c) Microsoft Corporation")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("5.4.0.0")] -[assembly: AssemblyFileVersion("5.4.0.0")] +[assembly: AssemblyVersion("5.4.1.0")] +[assembly: AssemblyFileVersion("5.4.1.0")] diff --git a/ClearScriptConsole/Properties/AssemblyInfo.cs b/ClearScriptConsole/Properties/AssemblyInfo.cs index a08350658..764f724a6 100644 --- a/ClearScriptConsole/Properties/AssemblyInfo.cs +++ b/ClearScriptConsole/Properties/AssemblyInfo.cs @@ -69,5 +69,5 @@ [assembly: AssemblyCopyright("(c) Microsoft Corporation")] [assembly: ComVisible(false)] -[assembly: AssemblyVersion("5.4.0.0")] -[assembly: AssemblyFileVersion("5.4.0.0")] +[assembly: AssemblyVersion("5.4.1.0")] +[assembly: AssemblyFileVersion("5.4.1.0")] diff --git a/ClearScriptTest/BugFixTest.cs b/ClearScriptTest/BugFixTest.cs index 04939e413..0c5644259 100644 --- a/ClearScriptTest/BugFixTest.cs +++ b/ClearScriptTest/BugFixTest.cs @@ -60,7 +60,10 @@ // using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq; @@ -150,7 +153,7 @@ public void BugFix_JScript_CaseInsensitivity() } [TestMethod, TestCategory("BugFix")] - public void BugFix_V8_ScriptInterruptCrash() + public void BugFix_V8ScriptInterruptCrash() { // run the test several times to verify post-interrupt engine functionality @@ -791,7 +794,7 @@ public void BugFix_Resurrection_V8ScriptItem() } [TestMethod, TestCategory("BugFix")] - public void BugFix_V8_GlobalMembers_ReadOnlyPropertyCrash() + public void BugFix_V8GlobalMembers_ReadOnlyPropertyCrash() { // this test is for a crash that occurred only on debug V8 builds engine.AddHostObject("bag", HostItemFlags.GlobalMembers, new PropertyBag()); @@ -800,13 +803,337 @@ public void BugFix_V8_GlobalMembers_ReadOnlyPropertyCrash() } [TestMethod, TestCategory("BugFix")] - public void BugFix_V8_GlobalMembers_NativeFunctionHiding() + public void BugFix_V8GlobalMembers_ReadOnlyPropertyCrash_Index() + { + // this test is for a crash that occurred only on debug V8 builds + engine.AddHostObject("bag", HostItemFlags.GlobalMembers, new PropertyBag()); + engine.AddHostObject("test", HostItemFlags.GlobalMembers, new ReadOnlyCollection(new [] { 5, 4, 3, 2, 1 })); + TestUtil.AssertException(() => engine.Execute("this[2] = 123")); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_V8GlobalMembers_NativeFunctionHiding() { engine.Execute("function toString() { return 'ABC'; }"); engine.AddHostObject("bag", HostItemFlags.GlobalMembers, new PropertyBag()); Assert.AreEqual("ABC", engine.Evaluate("toString()")); } + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef() + { + engine.Dispose(); + engine = new VBScriptEngine(); + + var a = new object(); + var b = new object(); + var c = new object(); + var x = new object(); + var y = new object(); + var z = new object(); + + engine.Script.a = a; + engine.Script.b = b; + engine.Script.c = c; + engine.Script.x = x; + engine.Script.y = y; + engine.Script.z = z; + + engine.Execute("sub test(i, j, k) : i = x : j = y : k = z : end sub"); + engine.Script.test(ref a, out b, ref c); + + Assert.AreSame(x, a); + Assert.AreSame(y, b); + Assert.AreSame(z, c); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef_Scalar() + { + engine.Dispose(); + engine = new VBScriptEngine(); + + var a = 123; + var b = 456; + var c = 789; + const int x = 987; + const int y = 654; + const int z = 321; + + engine.Script.a = a; + engine.Script.b = b; + engine.Script.c = c; + engine.Script.x = x; + engine.Script.y = y; + engine.Script.z = z; + + engine.Execute("sub test(i, j, k) : i = x : j = y : k = z : end sub"); + engine.Script.test(ref a, out b, ref c); + + Assert.AreEqual(x, a); + Assert.AreEqual(y, b); + Assert.AreEqual(z, c); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef_Enum() + { + engine.Dispose(); + engine = new VBScriptEngine(); + + var a = DayOfWeek.Monday; + var b = DayOfWeek.Tuesday; + var c = DayOfWeek.Wednesday; + const DayOfWeek x = DayOfWeek.Sunday; + const DayOfWeek y = DayOfWeek.Saturday; + const DayOfWeek z = DayOfWeek.Friday; + + engine.Script.a = a; + engine.Script.b = b; + engine.Script.c = c; + engine.Script.x = x; + engine.Script.y = y; + engine.Script.z = z; + + engine.Execute("sub test(i, j, k) : i = x : j = y : k = z : end sub"); + engine.Script.test(ref a, out b, ref c); + + Assert.AreEqual(x, a); + Assert.AreEqual(y, b); + Assert.AreEqual(z, c); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef_Struct() + { + engine.Dispose(); + engine = new VBScriptEngine(); + + var random = new Random(); + var a = TimeSpan.FromMilliseconds(random.NextDouble() * 1000); + var b = TimeSpan.FromMilliseconds(random.NextDouble() * 1000); + var c = TimeSpan.FromMilliseconds(random.NextDouble() * 1000); + var x = TimeSpan.FromMilliseconds(random.NextDouble() * 1000); + var y = TimeSpan.FromMilliseconds(random.NextDouble() * 1000); + var z = TimeSpan.FromMilliseconds(random.NextDouble() * 1000); + + engine.Script.a = a; + engine.Script.b = b; + engine.Script.c = c; + engine.Script.x = x; + engine.Script.y = y; + engine.Script.z = z; + + engine.Execute("sub test(i, j, k) : i = x : j = y : k = z : end sub"); + engine.Script.test(ref a, out b, ref c); + + Assert.AreEqual(x, a); + Assert.AreEqual(y, b); + Assert.AreEqual(z, c); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef_VB() + { + TestUtil.InvokeVBTestSub(@" + Using engine As New VBScriptEngine + + Dim a = New Object + Dim b = New Object + Dim c = New Object + Dim x = New Object + Dim y = New Object + Dim z = New Object + + engine.Script.a = a + engine.Script.b = b + engine.Script.c = c + engine.Script.x = x + engine.Script.y = y + engine.Script.z = z + + engine.Execute(""sub test(i, j, k) : i = x : j = y : k = z : end sub"") + engine.Script.test(a, b, c) + + Assert.AreSame(x, a) + Assert.AreSame(y, b) + Assert.AreSame(z, c) + + End Using + "); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef_VB_Scalar() + { + TestUtil.InvokeVBTestSub(@" + Using engine As New VBScriptEngine + + Dim a = 123 + Dim b = 456 + Dim c = 789 + Dim x = 987 + Dim y = 654 + Dim z = 321 + + engine.Script.a = a + engine.Script.b = b + engine.Script.c = c + engine.Script.x = x + engine.Script.y = y + engine.Script.z = z + + engine.Execute(""sub test(i, j, k) : i = x : j = y : k = z : end sub"") + engine.Script.test(a, b, c) + + Assert.AreEqual(x, a) + Assert.AreEqual(y, b) + Assert.AreEqual(z, c) + + End Using + "); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef_VB_Enum() + { + TestUtil.InvokeVBTestSub(@" + Using engine As New VBScriptEngine + + Dim a = DayOfWeek.Monday + Dim b = DayOfWeek.Tuesday + Dim c = DayOfWeek.Wednesday + Dim x = DayOfWeek.Sunday + Dim y = DayOfWeek.Saturday + Dim z = DayOfWeek.Friday + + engine.Script.a = a + engine.Script.b = b + engine.Script.c = c + engine.Script.x = x + engine.Script.y = y + engine.Script.z = z + + engine.Execute(""sub test(i, j, k) : i = x : j = y : k = z : end sub"") + engine.Script.test(a, b, c) + + Assert.AreEqual(x, a) + Assert.AreEqual(y, b) + Assert.AreEqual(z, c) + + End Using + "); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_VBScriptItemArgsByRef_VB_Struct() + { + TestUtil.InvokeVBTestSub(@" + Using engine As New VBScriptEngine + + Dim random = New Random + Dim a = TimeSpan.FromMilliseconds(random.NextDouble() * 1000) + Dim b = TimeSpan.FromMilliseconds(random.NextDouble() * 1000) + Dim c = TimeSpan.FromMilliseconds(random.NextDouble() * 1000) + Dim x = TimeSpan.FromMilliseconds(random.NextDouble() * 1000) + Dim y = TimeSpan.FromMilliseconds(random.NextDouble() * 1000) + Dim z = TimeSpan.FromMilliseconds(random.NextDouble() * 1000) + + engine.Script.a = a + engine.Script.b = b + engine.Script.c = c + engine.Script.x = x + engine.Script.y = y + engine.Script.z = z + + engine.Execute(""sub test(i, j, k) : i = x : j = y : k = z : end sub"") + engine.Script.test(a, b, c) + + Assert.AreEqual(x, a) + Assert.AreEqual(y, b) + Assert.AreEqual(z, c) + + End Using + "); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_CallHostObjectFunctionAsConstructor() + { + engine.Script.random = new Random(); + engine.AddHostType("Random", typeof(Random)); + var result = engine.Evaluate(@" + (function () { + var x = new Random().NextDouble(); + try { + return new random.constructor(); + } + catch (ex) { + return new Random().NextDouble() * x; + } + return false; + })() + "); + Assert.IsInstanceOfType(result, typeof(double)); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_HostItemCachingForHostVariables() + { + var foo = new HostFunctions().newVar(new object()); + engine.Script.foo1 = foo; + engine.Script.foo2 = foo; + Assert.IsTrue(Convert.ToBoolean(engine.Evaluate("foo1 === foo2"))); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_MetaScriptItem_GetDynamicMemberNames() + { + var dmop = (IDynamicMetaObjectProvider)engine.Evaluate("({ foo: 123, bar: 456, baz: 789 })"); + var dmo = dmop.GetMetaObject(Expression.Constant(dmop)); + var names = dmo.GetDynamicMemberNames().ToArray(); + Assert.IsTrue(names.Contains("foo")); + Assert.IsTrue(names.Contains("bar")); + Assert.IsTrue(names.Contains("baz")); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_AmbiguousIndexer() + { + IAmbiguousIndexer indexer = new AmbiguousIndexer(); + engine.AddRestrictedHostObject("indexer", indexer); + engine.AddHostType("DayOfWeek", typeof(DayOfWeek)); + + engine.Execute("indexer.Item.set(123, 456)"); + Assert.AreEqual(456, engine.Evaluate("indexer.Item(123)")); + Assert.IsNull(engine.Evaluate("indexer.Item(789)")); + + engine.Execute("indexer.Item.set(DayOfWeek.Thursday, DayOfWeek.Sunday)"); + Assert.AreEqual(DayOfWeek.Sunday, engine.Evaluate("indexer.Item(DayOfWeek.Thursday)")); + Assert.IsNull(engine.Evaluate("indexer.Item(DayOfWeek.Tuesday)")); + } + + [TestMethod, TestCategory("BugFix")] + public void BugFix_AmbiguousIndexer_ADODB() + { + engine.Dispose(); + engine = new VBScriptEngine(WindowsScriptEngineFlags.EnableDebugging); + + var recordSet = new ADODB.Recordset(); + recordSet.Fields.Append("foo", ADODB.DataTypeEnum.adVarChar, 20); + recordSet.Open(Missing.Value, Missing.Value, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockOptimistic, 0); + recordSet.AddNew(Missing.Value, Missing.Value); + recordSet.Fields["foo"].Value = "bar"; + + engine.AddHostObject("recordSet", recordSet); + Assert.AreEqual("bar", engine.Evaluate("recordSet.Fields.Item(\"foo\").Value")); + + engine.Execute("recordSet.Fields.Item(\"foo\").Value = \"qux\""); + Assert.AreEqual("qux", engine.Evaluate("recordSet.Fields.Item(\"foo\").Value")); + + TestUtil.AssertException(() => engine.Evaluate("recordSet.Fields.Item(\"baz\")")); + } + // ReSharper restore InconsistentNaming #endregion @@ -870,6 +1197,40 @@ public ResurrectionTestWrapper(IDisposable target) } } + public interface IAmbiguousIndexerBase1 + { + object this[int i] { get; set; } + } + + public interface IAmbiguousIndexerBase2 + { + object this[int i] { get; set; } + object this[DayOfWeek d] { get; set; } + } + + public interface IAmbiguousIndexer : IAmbiguousIndexerBase1, IAmbiguousIndexerBase2 + { + new object this[int i] { get; set; } + } + + public class AmbiguousIndexer : IAmbiguousIndexer + { + private readonly IDictionary byInteger = new ListDictionary(); + private readonly IDictionary byDayOfWeek = new ListDictionary(); + + public object this[int key] + { + get { return byInteger[key]; } + set { byInteger[key] = value; } + } + + public object this[DayOfWeek key] + { + get { return byDayOfWeek[key]; } + set { byDayOfWeek[key] = value; } + } + } + #endregion } } diff --git a/ClearScriptTest/ClearScriptTest.csproj b/ClearScriptTest/ClearScriptTest.csproj index 58347682b..c8a339c81 100644 --- a/ClearScriptTest/ClearScriptTest.csproj +++ b/ClearScriptTest/ClearScriptTest.csproj @@ -109,6 +109,17 @@ + + + {B691E011-1797-432E-907A-4D8C69339129} + 6 + 0 + 0 + tlbimp + False + False + +