// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. 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; using System.Linq.Expressions; using System.Reflection; using System.Threading; using System.Xml.Linq; using Microsoft.ClearScript.V8; using Microsoft.ClearScript.Util; using Microsoft.ClearScript.Windows; using Microsoft.CSharp.RuntimeBinder; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using UIAutomationClient; // ReSharper disable HeuristicUnreachableCode namespace Microsoft.ClearScript.Test { [TestClass] [DeploymentItem("ClearScriptV8-64.dll")] [DeploymentItem("ClearScriptV8-32.dll")] [DeploymentItem("v8-x64.dll")] [DeploymentItem("v8-ia32.dll")] [DeploymentItem("v8-base-x64.dll")] [DeploymentItem("v8-base-ia32.dll")] [DeploymentItem("v8-zlib-x64.dll")] [DeploymentItem("v8-zlib-ia32.dll")] [DeploymentItem("ClearScriptConsole.exe")] [DeploymentItem("Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll")] [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Test classes use TestCleanupAttribute for deterministic teardown.")] [SuppressMessage("ReSharper", "StringLiteralTypo")] [SuppressMessage("ReSharper", "IdentifierTypo")] public partial class BugFixTest : ClearScriptTest { #region setup / teardown private ScriptEngine engine; [TestInitialize] public void TestInitialize() { engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging); } [TestCleanup] public void TestCleanup() { engine.Dispose(); BaseTestCleanup(); } #endregion #region test methods // ReSharper disable InconsistentNaming [TestMethod, TestCategory("BugFix")] public void BugFix_NullArgBinding() { var value = 123.456 as IConvertible; engine.AddRestrictedHostObject("value", value); Assert.AreEqual(value.ToString(null), engine.Evaluate("value.ToString(null)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NullArgBinding_Ambiguous() { engine.AddHostObject("lib", new HostTypeCollection("mscorlib")); TestUtil.AssertException(() => engine.Execute("lib.System.Console.WriteLine(null)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_DelegateConstructionSyntax() { engine.AddHostObject("lib", HostItemFlags.GlobalMembers, new HostTypeCollection("mscorlib")); var func = (Func)engine.Evaluate("new (System.Func(System.Int32, System.Int32))(function (x) {return x * x; })"); Assert.AreEqual(25, func(5)); } [TestMethod, TestCategory("BugFix")] public void BugFix_DelegateConstructionSyntax_DoubleWrap() { engine.AddHostObject("lib", HostItemFlags.GlobalMembers, new HostTypeCollection("mscorlib")); engine.Execute("cb = new (System.Func(System.Int32, System.Int32))(function (x) {return x * x; })"); var func = (Func)engine.Evaluate("new (System.Func(System.Int32, System.Int32))(cb)"); Assert.AreEqual(25, func(5)); } [TestMethod, TestCategory("BugFix")] public void BugFix_JScript_CaseInsensitivity() { engine.Dispose(); engine = new JScriptEngine(); engine.Execute("abc = 1; ABC = 2; function foo() { return 3; } function FOO() { return 4; }"); Assert.AreEqual(1, engine.Script.abc); Assert.AreEqual(2, engine.Script.ABC); Assert.AreEqual(3, engine.Script.foo()); Assert.AreEqual(4, engine.Script.FOO()); } [TestMethod, TestCategory("BugFix")] public void BugFix_V8ScriptInterruptCrash() { // run the test several times to verify post-interrupt engine functionality for (var iteration = 0; iteration < 16; iteration++) { var context = new PropertyBag(); engine.AddHostObject("context", context); using (var startEvent = new ManualResetEventSlim(false)) { context["startEvent"] = startEvent; var interrupted = false; var thread = new Thread(() => { try { engine.Execute("while (true) { context.startEvent.Set(); }"); } catch (ScriptInterruptedException) { interrupted = true; } }); thread.Start(); startEvent.Wait(); engine.Interrupt(); thread.Join(); Assert.IsTrue(interrupted); } } } [TestMethod, TestCategory("BugFix")] public void BugFix_InheritedInterfaceMethod() { var list = new List { 123 }; var enumerator = list.AsEnumerable().GetEnumerator(); engine.AddRestrictedHostObject("enumerator", enumerator); Assert.IsTrue((bool)engine.Evaluate("enumerator.MoveNext()")); Assert.AreEqual(123, engine.Evaluate("enumerator.Current")); Assert.IsFalse((bool)engine.Evaluate("enumerator.MoveNext()")); engine.Execute("enumerator.Dispose()"); } [TestMethod, TestCategory("BugFix")] public void BugFix_EnumEquality() { engine.AddHostObject("host", new HostFunctions()); engine.AddHostType("DayOfWeek", typeof(DayOfWeek)); engine.AddHostType("BindingFlags", typeof(BindingFlags)); Assert.IsTrue((bool)engine.Evaluate("DayOfWeek.Wednesday == DayOfWeek.Wednesday")); Assert.IsTrue((bool)engine.Evaluate("host.flags(BindingFlags.Public, BindingFlags.Instance) == host.flags(BindingFlags.Public, BindingFlags.Instance)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_FloatParameterBinding() { engine.AddHostType("Convert", typeof(Convert)); engine.Script.list = new List(); const float floatPi = (float)Math.PI; engine.Execute("list.Add(Convert.ToSingle(Math.PI))"); Assert.AreEqual(floatPi, engine.Script.list[0]); } [TestMethod, TestCategory("BugFix")] public void BugFix_FloatParameterBinding_JScript() { engine.Dispose(); engine = new JScriptEngine(); BugFix_FloatParameterBinding(); } [TestMethod, TestCategory("BugFix")] public void BugFix_UInt32RoundTrip() { engine.AddHostType("UInt32", typeof(uint)); Assert.AreEqual((long)uint.MaxValue, engine.Evaluate("UInt32.MaxValue")); } [TestMethod, TestCategory("BugFix")] public void BugFix_UInt32RoundTrip_JScript() { engine.Dispose(); engine = new JScriptEngine(); BugFix_UInt32RoundTrip(); } [TestMethod, TestCategory("BugFix")] public void BugFix_AutoInt64FromDouble() { engine.AddHostType("Int32", typeof(int)); Assert.AreEqual((long)int.MaxValue * 123 + 1, engine.Evaluate("Int32.MaxValue * 123 + 1")); } [TestMethod, TestCategory("BugFix")] public void BugFix_CompiledScriptResult() { engine.Script.host = new HostFunctions(); using (var script = ((V8ScriptEngine)engine).Compile("host")) { Assert.IsInstanceOfType(((V8ScriptEngine)engine).Evaluate(script), typeof(HostFunctions)); } } [TestMethod, TestCategory("BugFix")] public void BugFix_GenericDelegateConstructorWithArgument() { engine.AddHostType("Func", "System.Func"); engine.AddHostType("StringT", typeof(string)); engine.AddHostType("IntT", typeof(int)); var func = (Func)engine.Evaluate("new Func(StringT, IntT, function (s) { return s.length; })"); const string testString = "floccinaucinihilipilification"; Assert.AreEqual(testString.Length, func(testString)); } [TestMethod, TestCategory("BugFix")] public void BugFix_HostMethodAsArgument() { var testObject = new TestObject(); engine.Script.testObject = testObject; engine.Script.expando = new ExpandoObject(); engine.AddHostType("DateTime", typeof(DateTime)); engine.Execute("expando.method = testObject.Method"); Assert.AreEqual(testObject.Method("foo", 123), engine.Evaluate("expando.method('foo', 123)")); Assert.AreEqual(testObject.Method(456), engine.Evaluate("expando.method(DateTime, 456)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_HostIndexedPropertyAsArgument() { var testObject = new TestObject(); engine.Script.testObject = testObject; engine.Script.expando = new ExpandoObject(); engine.Execute("expando.property = testObject.Item"); Assert.AreEqual("foo", engine.Evaluate("expando.property.set(123, 'foo')")); Assert.AreEqual("foo", engine.Evaluate("expando.property.get(123)")); Assert.AreEqual("foo", engine.Evaluate("expando.property(123)")); Assert.AreEqual(456, engine.Evaluate("expando.property.set('bar', 456)")); Assert.AreEqual(456, engine.Evaluate("expando.property.get('bar')")); Assert.AreEqual(456, engine.Evaluate("expando.property('bar')")); } [TestMethod, TestCategory("BugFix")] public void BugFix_Nullable_Field() { engine.Script.test = new NullableTest(); Assert.IsNull(engine.Evaluate("test.Field")); Assert.IsTrue((bool)engine.Evaluate("test.Field === null")); engine.Execute("test.Field = 123"); Assert.AreEqual(123, engine.Evaluate("test.Field")); Assert.IsTrue((bool)engine.Evaluate("test.Field === 123")); engine.Execute("test.Field = null"); Assert.IsNull(engine.Evaluate("test.Field")); Assert.IsTrue((bool)engine.Evaluate("test.Field === null")); } [TestMethod, TestCategory("BugFix")] public void BugFix_Nullable_Property() { engine.Script.test = new NullableTest(); Assert.IsNull(engine.Evaluate("test.Property")); Assert.IsTrue((bool)engine.Evaluate("test.Property === null")); engine.Execute("test.Property = 123"); Assert.AreEqual(123, engine.Evaluate("test.Property")); Assert.IsTrue((bool)engine.Evaluate("test.Property === 123")); engine.Execute("test.Property = null"); Assert.IsNull(engine.Evaluate("test.Property")); Assert.IsTrue((bool)engine.Evaluate("test.Property === null")); } [TestMethod, TestCategory("BugFix")] public void BugFix_Nullable_ArgBinding() { var test = new NullableTest(); engine.Script.test = test; Assert.IsNull(engine.Evaluate("test.Method(null)")); Assert.AreEqual(test.Method(5), engine.Evaluate("test.Method(5)")); Assert.AreEqual(test.Method(5.1), engine.Evaluate("test.Method(5.1)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_GlobalObjectCrash() { engine.AddHostObject("random", HostItemFlags.GlobalMembers, new Random()); Assert.AreEqual("[object Object]", engine.ExecuteCommand("this")); } [TestMethod, TestCategory("BugFix")] public void BugFix_VariantClear() { // ReSharper disable RedundantAssignment WeakReference wr = null; new Action(() => { var x = new object(); wr = new WeakReference(x); VariantClearTestHelper(x); x = null; })(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr.IsAlive); // ReSharper restore RedundantAssignment } [TestMethod, TestCategory("BugFix")] public void BugFix_VBCallToParameterlessVBScriptSub() { TestUtil.InvokeVBTestSub(@" Using engine As New VBScriptEngine engine.Execute(""sub main : end sub"") engine.Script.main() End Using "); } [TestMethod, TestCategory("BugFix")] public void BugFix_VBDynamicArgMarshaling_Numeric() { var result = TestUtil.InvokeVBTestFunction(@" Using engine As New V8ScriptEngine engine.Execute(""data = [5, 4, 'qux', 2, 1]; function getElement(i1, i2, i3) { return data[i1 + i2 - i3]; }"") TestFunction = engine.Script.getElement(CShort(1), CLng(99), CStr(98)) End Using "); Assert.AreEqual("qux", result); } [TestMethod, TestCategory("BugFix")] public void BugFix_VBDynamicArgMarshaling_String() { var result = TestUtil.InvokeVBTestFunction(@" Using engine As New V8ScriptEngine engine.Execute(""data = { foo26: 123, bar97: 456.789 }; function getElement(i1, i2) { return data[i1 + i2]; }"") TestFunction = engine.Script.getElement(""bar"", CLng(97)) End Using "); Assert.AreEqual(456.789, result); } [TestMethod, TestCategory("BugFix")] public void BugFix_AmbiguousAttribute() { engine.Script.foo = new AmbiguousAttributeTest(); engine.Execute("foo.Foo()"); } [TestMethod, TestCategory("BugFix")] public void BugFix_CoreBindCache() { HostItem.ResetCoreBindCache(); engine.Dispose(); for (var i = 0; i < 10; i++) { using (engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging)) { engine.Script.host = new HostFunctions(); Assert.AreEqual(' ', engine.Evaluate("host.toChar(32)")); Assert.AreEqual('A', engine.Evaluate("host.toChar(65)")); Assert.AreEqual(0.0F, engine.Evaluate("host.toSingle(0)")); Assert.AreEqual(1.0F, engine.Evaluate("host.toSingle(1)")); } } Assert.AreEqual(2L, HostItem.GetCoreBindCount()); } [TestMethod, TestCategory("BugFix")] public void BugFix_StringWithNullChar() { const string value = "abc\0def"; const string value2 = "ghi\0jkl"; dynamic func = engine.Evaluate("(function (x) { return x.length; }).valueOf()"); Assert.AreEqual(value.Length, func(value)); Assert.AreEqual(value, engine.Evaluate(@"'abc\0def'")); dynamic obj = engine.Evaluate(@"({ 'abc\0def': 'ghi\0jkl' })"); Assert.IsTrue(((DynamicObject)obj).GetDynamicMemberNames().Contains(value)); Assert.AreEqual(value2, obj[value]); } [TestMethod, TestCategory("BugFix")] public void BugFix_V8ScriptItemWeakBinding() { // This test verifies that V8 script items no longer prevent their isolates from being // destroyed. Previously it exhausted address space and crashed in 32-bit mode. for (var i = 0; i < 128; i++) { using (var tempEngine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging)) { tempEngine.Evaluate("(function () {}).valueOf()"); } } } [TestMethod, TestCategory("BugFix")] public void BugFix_V8ScriptItemDispose() { dynamic item1; using (var tempEngine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging)) { item1 = tempEngine.Evaluate("(function () { return 123; }).valueOf()"); Assert.AreEqual(123, item1()); dynamic item2 = tempEngine.Evaluate("(function () { return 456; }).valueOf()"); using (item2 as IDisposable) { Assert.AreEqual(456, item2()); } } using (item1 as IDisposable) { } } [TestMethod, TestCategory("BugFix")] public void BugFix_V8CompiledScriptWeakBinding() { // This test verifies that V8 compiled scripts no longer prevent their isolates from // being destroyed. Previously it exhausted address space and crashed in 32-bit mode. for (var i = 0; i < 128; i++) { using (var tempEngine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging)) { tempEngine.Compile("(function () {}).valueOf()"); } } } [TestMethod, TestCategory("BugFix")] public void BugFix_V8CompiledScriptDispose() { V8Script script1; using (var tempEngine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging)) { script1 = tempEngine.Compile("(function () { return 123; })()"); Assert.AreEqual(123, tempEngine.Evaluate(script1)); V8Script script2 = tempEngine.Compile("(function () { return 456; })()"); using (script2) { Assert.AreEqual(456, tempEngine.Evaluate(script2)); } } using (script1) { } } [TestMethod, TestCategory("BugFix")] public void BugFix_V8ArrayBufferAllocator() { engine.Execute("buffer = new ArrayBuffer(12345)"); Assert.AreEqual(12345, engine.Script.buffer.byteLength); } [TestMethod, TestCategory("BugFix")] public void BugFix_IDispatchExArgLeak_SetProperty_JScript() { // ReSharper disable RedundantAssignment engine.Dispose(); engine = new JScriptEngine(); WeakReference wr = null; new Action(() => { // ReSharper disable AccessToModifiedClosure object x = Guid.NewGuid(); wr = new WeakReference(x); var result = x.ToString(); engine.Script.x = x; x = null; GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.AreEqual(result, engine.Evaluate("x.ToString()")); engine.Script.x = null; engine.CollectGarbage(true); // ReSharper restore AccessToModifiedClosure })(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr.IsAlive); // ReSharper restore RedundantAssignment } [TestMethod, TestCategory("BugFix")] public void BugFix_IDispatchExArgLeak_SetProperty_VBScript() { // ReSharper disable RedundantAssignment engine.Dispose(); engine = new VBScriptEngine(); WeakReference wr = null; new Action(() => { // ReSharper disable AccessToModifiedClosure object x = Guid.NewGuid(); wr = new WeakReference(x); var result = x.ToString(); engine.Script.x = x; x = null; GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.AreEqual(result, engine.Evaluate("x.ToString()")); engine.Script.x = null; engine.CollectGarbage(true); // ReSharper restore AccessToModifiedClosure })(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr.IsAlive); // ReSharper restore RedundantAssignment } [TestMethod, TestCategory("BugFix")] public void BugFix_IDispatchExArgLeak_GetProperty_VBScript() { // ReSharper disable RedundantAssignment engine.Dispose(); engine = new VBScriptEngine(); WeakReference wr1 = null; WeakReference wr2 = null; new Action(() => { // ReSharper disable AccessToModifiedClosure object x1 = Guid.NewGuid(); wr1 = new WeakReference(x1); object x2 = Guid.NewGuid(); wr2 = new WeakReference(x2); engine.Execute(@" class MyClass public property get foo(x1, x2) foo = x1.ToString() & x2.ToString() end property end class set bar = new MyClass "); object result; var bar = (DynamicObject)engine.Script.bar; var args = new[] { "foo", HostItem.Wrap(engine, x1), HostItem.Wrap(engine, x2) }; Assert.IsTrue(bar.GetMetaObject(Expression.Constant(bar)).TryGetIndex(args, out result)); Assert.AreEqual(x1.ToString() + x2, result); // ReSharper restore AccessToModifiedClosure })(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr1.IsAlive); Assert.IsFalse(wr2.IsAlive); // ReSharper restore RedundantAssignment } [TestMethod, TestCategory("BugFix")] public void BugFix_IDispatchExArgLeak_InvokeMethod_JScript() { // ReSharper disable RedundantAssignment engine.Dispose(); engine = new JScriptEngine(); WeakReference wr1 = null; WeakReference wr2 = null; new Action(() => { // ReSharper disable AccessToModifiedClosure object x1 = Guid.NewGuid(); wr1 = new WeakReference(x1); object x2 = Guid.NewGuid(); wr2 = new WeakReference(x2); engine.Execute("function foo(x1, x2) { return x1.ToString() + x2.ToString(); }"); Assert.AreEqual(x1.ToString() + x2, engine.Script.foo(x1, x2)); engine.CollectGarbage(true); // ReSharper restore AccessToModifiedClosure })(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr1.IsAlive); Assert.IsFalse(wr2.IsAlive); // ReSharper restore RedundantAssignment } [TestMethod, TestCategory("BugFix")] public void BugFix_IDispatchExArgLeak_InvokeMethod_VBScript() { // ReSharper disable RedundantAssignment engine.Dispose(); engine = new VBScriptEngine(); WeakReference wr1 = null; WeakReference wr2 = null; new Action(() => { // ReSharper disable AccessToModifiedClosure object x1 = Guid.NewGuid(); wr1 = new WeakReference(x1); object x2 = Guid.NewGuid(); wr2 = new WeakReference(x2); engine.Execute("function foo(x1, x2):foo = x1.ToString() & x2.ToString():end function"); Assert.AreEqual(x1.ToString() + x2, engine.Script.foo(x1, x2)); engine.CollectGarbage(true); // ReSharper restore AccessToModifiedClosure })(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr1.IsAlive); Assert.IsFalse(wr2.IsAlive); // ReSharper restore RedundantAssignment } [TestMethod, TestCategory("BugFix")] public void BugFix_V8CachedObjectLeak() { WeakReference wr = null; new Action(() => { // ReSharper disable RedundantAssignment object x = null; using (var tempEngine = new V8ScriptEngine()) { tempEngine.AddHostType("Action", typeof(Action)); x = tempEngine.Evaluate("action = new Action(function () {})"); wr = new WeakReference(tempEngine); } Assert.IsInstanceOfType(x, typeof(Action)); TestUtil.AssertException((Action)x); x = null; // ReSharper restore RedundantAssignment })(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr.IsAlive); } [TestMethod, TestCategory("BugFix")] public void BugFix_Resurrection_V8ScriptEngine() { for (var index = 0; index < 256; index++) { // ReSharper disable UnusedVariable var wrapper = new ResurrectionTestWrapper(new V8ScriptEngine()); GC.Collect(); // ReSharper restore UnusedVariable } } [TestMethod, TestCategory("BugFix")] public void BugFix_Resurrection_V8Runtime() { for (var index = 0; index < 256; index++) { // ReSharper disable UnusedVariable var wrapper = new ResurrectionTestWrapper(new V8Runtime()); GC.Collect(); // ReSharper restore UnusedVariable } } [TestMethod, TestCategory("BugFix")] public void BugFix_Resurrection_V8Script() { for (var index = 0; index < 256; index++) { // ReSharper disable UnusedVariable var tempEngine = new V8ScriptEngine(); var wrapper = new ResurrectionTestWrapper(tempEngine.Compile("function foo() {}")); GC.Collect(); // ReSharper restore UnusedVariable } } [TestMethod, TestCategory("BugFix")] public void BugFix_Resurrection_V8ScriptItem() { for (var index = 0; index < 256; index++) { // ReSharper disable UnusedVariable var tempEngine = new V8ScriptEngine(); var wrapper = new ResurrectionTestWrapper(tempEngine.Script.Math); GC.Collect(); // ReSharper restore UnusedVariable } } [TestMethod, TestCategory("BugFix")] 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()); engine.AddHostObject("test", HostItemFlags.GlobalMembers, new { foo = 123 }); TestUtil.AssertException(() => engine.Execute("foo = 456")); } [TestMethod, TestCategory("BugFix")] 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_InaccessiblePropertyAccessors() { engine.Script.foo = new InaccessiblePropertyAccessors(); TestUtil.AssertException(() => engine.Evaluate("foo.NoGetter")); Assert.AreEqual(123, engine.Evaluate("foo.NoGetter = 123")); TestUtil.AssertException(() => engine.Evaluate("foo.PrivateGetter")); Assert.AreEqual(456, engine.Evaluate("foo.PrivateGetter = 456")); Assert.AreEqual(456, engine.Evaluate("foo.NoSetter")); TestUtil.AssertException(() => engine.Evaluate("foo.NoSetter = 789")); Assert.AreEqual(456, engine.Evaluate("foo.PrivateSetter")); TestUtil.AssertException(() => engine.Evaluate("foo.PrivateSetter = 789")); } [TestMethod, TestCategory("BugFix")] public void BugFix_InaccessiblePropertyAccessors_Static() { engine.AddHostType("foo", typeof(InaccessiblePropertyAccessorsStatic)); TestUtil.AssertException(() => engine.Evaluate("foo.NoGetter")); Assert.AreEqual(123, engine.Evaluate("foo.NoGetter = 123")); TestUtil.AssertException(() => engine.Evaluate("foo.PrivateGetter")); Assert.AreEqual(456, engine.Evaluate("foo.PrivateGetter = 456")); Assert.AreEqual(456, engine.Evaluate("foo.NoSetter")); TestUtil.AssertException(() => engine.Evaluate("foo.NoSetter = 789")); Assert.AreEqual(456, engine.Evaluate("foo.PrivateSetter")); TestUtil.AssertException(() => engine.Evaluate("foo.PrivateSetter = 789")); } [TestMethod, TestCategory("BugFix")] public void BugFix_V8RuntimeConstraintScale() { const int maxNewSpaceSize = 16; const int maxOldSpaceSize = 512; const double tolerance = .05; var constraints = new V8RuntimeConstraints { MaxNewSpaceSize = maxNewSpaceSize, MaxOldSpaceSize = maxOldSpaceSize }; using (var tempEngine = new V8ScriptEngine(constraints)) { Assert.AreEqual(Math.PI, tempEngine.Evaluate("Math.PI")); var expected = Convert.ToDouble(maxNewSpaceSize * 2 + maxOldSpaceSize); var actual = Convert.ToDouble(tempEngine.GetRuntimeHeapInfo().HeapSizeLimit / (1024 * 1024)); var ratio = actual / expected; Assert.IsTrue((ratio >= 1 - tolerance) && (ratio <= 1 + tolerance)); } constraints = new V8RuntimeConstraints { MaxNewSpaceSize = maxNewSpaceSize * 1024 * 1024, MaxOldSpaceSize = maxOldSpaceSize * 1024 * 1024 }; using (var tempEngine = new V8ScriptEngine(constraints)) { Assert.AreEqual(Math.E, tempEngine.Evaluate("Math.E")); var expected = Convert.ToDouble(maxNewSpaceSize * 2 + maxOldSpaceSize); var actual = Convert.ToDouble(tempEngine.GetRuntimeHeapInfo().HeapSizeLimit / (1024 * 1024)); var ratio = actual / expected; Assert.IsTrue((ratio >= 1 - tolerance) && (ratio <= 1 + tolerance)); } } [TestMethod, TestCategory("BugFix")] public void BugFix_TooManyDebugApplications() { var engines = Enumerable.Range(0, 2048).Select(index => new JScriptEngine(WindowsScriptEngineFlags.EnableDebugging)).ToArray(); Array.ForEach(engines, tempEngine => tempEngine.Dispose()); } [TestMethod, TestCategory("BugFix")] public void BugFix_AssemblyHelpersClass() { Assert.IsTrue(typeof(AssemblyHelpers).IsStatic()); Assert.IsNull(typeof(AssemblyHelpers).TypeInitializer); } [TestMethod, TestCategory("BugFix")] public void BugFix_FinalizerHang_V8ScriptItem() { engine.Script.foo = new Action(arg => {}); engine.Script.bar = new Action(() => { GC.Collect(); GC.WaitForPendingFinalizers(); }); engine.Execute(@" for (var i = 0; i < 100; i++) { foo({ index: i }); } bar(); "); } [TestMethod, TestCategory("BugFix")] public void BugFix_FinalizerHang_V8Script() { engine.Script.bar = new Action(() => { GC.Collect(); GC.WaitForPendingFinalizers(); }); for (var index = 0; index < 100; index++) { ((V8ScriptEngine)engine).Compile("function foo() { return " + index + "; }"); } engine.Execute("bar()"); } [TestMethod, TestCategory("BugFix")] public void BugFix_FinalizerHang_V8ScriptEngine() { engine.Dispose(); using (var runtime = new V8Runtime(V8RuntimeFlags.EnableDebugging)) { engine = runtime.CreateScriptEngine(); engine.Script.bar = new Action(() => { GC.Collect(); GC.WaitForPendingFinalizers(); }); for (var index = 0; index < 100; index++) { runtime.CreateScriptEngine(); } engine.Execute("bar()"); } } [TestMethod, TestCategory("BugFix")] public void BugFix_DynamicMethodArgs() { engine.Script.foo = new DynamicMethodArgTest(); Assert.AreEqual("123 456.789 hello", engine.Evaluate("foo.RunTest(123, 456.789, 'hello')")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NestedInterrupt() { var context = new PropertyBag(); engine.AddHostObject("context", context); using (var startEvent = new ManualResetEventSlim(false)) { object result = null; var interruptedInner = false; var interruptedOuter = false; context["startEvent"] = startEvent; context["foo"] = new Action(() => { try { engine.Execute("while (true) { context.startEvent.Set(); }"); } catch (ScriptInterruptedException) { interruptedInner = true; } }); var thread = new Thread(() => { try { result = engine.Evaluate("context.foo(); 123"); } catch (ScriptInterruptedException) { interruptedOuter = true; } }); thread.Start(); startEvent.Wait(); engine.Interrupt(); thread.Join(); Assert.IsTrue(interruptedInner); Assert.IsFalse(interruptedOuter); Assert.AreEqual(123, result); } } [TestMethod, TestCategory("BugFix")] public void BugFix_NestedInterrupt_JScript() { engine.Dispose(); try { using (var startEvent = new ManualResetEventSlim(false)) { object result = null; var interruptedInner = false; var interruptedOuter = false; var thread = new Thread(() => { using (engine = new JScriptEngine(WindowsScriptEngineFlags.EnableDebugging)) { var context = new PropertyBag(); engine.AddHostObject("context", context); // ReSharper disable once AccessToDisposedClosure context["startEvent"] = startEvent; context["foo"] = new Action(() => { try { engine.Execute("while (true) { context.startEvent.Set(); }"); } catch (ScriptInterruptedException) { interruptedInner = true; } }); try { result = engine.Evaluate("context.foo(); 123"); } catch (ScriptInterruptedException) { interruptedOuter = true; } } }); thread.Start(); startEvent.Wait(); engine.Interrupt(); thread.Join(); Assert.IsTrue(interruptedInner); Assert.IsFalse(interruptedOuter); Assert.AreEqual(123, result); } } finally { engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableDebugging); } } [TestMethod, TestCategory("BugFix")] public void BugFix_PropertyBag_NativeEnumerator_JScript() { engine.Dispose(); engine = new JScriptEngine(); var x = new PropertyBag(); x["foo"] = 123; x["bar"] = "blah"; engine.Script.x = x; var result = (string)engine.Evaluate(@" var result = ''; for (var e = new Enumerator(x); !e.atEnd(); e.moveNext()) { result += e.item().Value; } result "); Assert.AreEqual(7, result.Length); Assert.IsTrue(result.IndexOf("123", StringComparison.Ordinal) >= 0); Assert.IsTrue(result.IndexOf("blah", StringComparison.Ordinal) >= 0); } [TestMethod, TestCategory("BugFix")] public void BugFix_PropertyBag_NativeEnumerator_VBScript() { engine.Dispose(); engine = new VBScriptEngine(); var x = new PropertyBag(); x["foo"] = 123; x["bar"] = "blah"; engine.Script.x = x; engine.Execute(@" function getResult(arg) dim result result = """" for each item in arg result = result & item.Value next getResult = result end function "); var result = (string)engine.Evaluate("getResult(x)"); Assert.AreEqual(7, result.Length); Assert.IsTrue(result.IndexOf("123", StringComparison.Ordinal) >= 0); Assert.IsTrue(result.IndexOf("blah", StringComparison.Ordinal) >= 0); } [TestMethod, TestCategory("BugFix")] public void BugFix_JScriptStandardsMode_PropertyAccess() { engine.Dispose(); engine = new JScriptEngine(WindowsScriptEngineFlags.EnableStandardsMode); engine.Script.x = new { foo = 123 }; Assert.AreEqual(123, engine.Evaluate("x.foo")); } [TestMethod, TestCategory("BugFix")] public void BugFix_JScriptStandardsMode_MemberEnumeration() { engine.Dispose(); engine = new JScriptEngine(WindowsScriptEngineFlags.EnableStandardsMode); engine.Script.x = new { foo = 123, bar = "blah" }; var result = (string)engine.Evaluate(@" var result = ''; for (var i in x) { if ((i == 'foo') || (i == 'bar')) { result += x[i]; } } result "); Assert.AreEqual(7, result.Length); Assert.IsTrue(result.IndexOf("123", StringComparison.Ordinal) >= 0); Assert.IsTrue(result.IndexOf("blah", StringComparison.Ordinal) >= 0); } [TestMethod, TestCategory("BugFix")] public void BugFix_JScriptStandardsMode_MemberEnumeration_PropertyBag() { engine.Dispose(); engine = new JScriptEngine(WindowsScriptEngineFlags.EnableStandardsMode); var x = new PropertyBag(); x["foo"] = 123; x["bar"] = "blah"; engine.Script.x = x; var result = (string)engine.Evaluate(@" var result = ''; for (var i in x) { result += x[i]; } result "); Assert.AreEqual(7, result.Length); Assert.IsTrue(result.IndexOf("123", StringComparison.Ordinal) >= 0); Assert.IsTrue(result.IndexOf("blah", StringComparison.Ordinal) >= 0); } [TestMethod, TestCategory("BugFix")] public void BugFix_JScriptStandardsMode_MemberEnumeration_Dynamic() { engine.Dispose(); engine = new JScriptEngine(WindowsScriptEngineFlags.EnableStandardsMode); dynamic x = new ExpandoObject(); x.foo = 123; x.bar = "blah"; engine.Script.x = x; var result = (string)engine.Evaluate(@" var result = ''; for (var i in x) { if ((i == 'foo') || (i == 'bar')) { result += x[i]; } } result "); Assert.AreEqual(7, result.Length); Assert.IsTrue(result.IndexOf("123", StringComparison.Ordinal) >= 0); Assert.IsTrue(result.IndexOf("blah", StringComparison.Ordinal) >= 0); } [TestMethod, TestCategory("BugFix")] public void BugFix_JScriptStandardsMode_MemberDeletion_PropertyBag() { engine.Dispose(); engine = new JScriptEngine(WindowsScriptEngineFlags.EnableStandardsMode); var x = new PropertyBag(); x["foo"] = 123; x["bar"] = "blah"; engine.Script.x = x; Assert.AreEqual(123, engine.Evaluate("x.foo")); Assert.AreEqual("blah", engine.Evaluate("x.bar")); Assert.AreEqual(true, engine.Evaluate("delete x.foo")); Assert.IsInstanceOfType(engine.Evaluate("x.foo"), typeof(Undefined)); } [TestMethod, TestCategory("BugFix")] public void BugFix_JScriptStandardsMode_MemberDeletion_Dynamic() { engine.Dispose(); engine = new JScriptEngine(WindowsScriptEngineFlags.EnableStandardsMode); dynamic x = new ExpandoObject(); x.foo = 123; x.bar = "blah"; engine.Script.x = x; Assert.AreEqual(123, engine.Evaluate("x.foo")); Assert.AreEqual("blah", engine.Evaluate("x.bar")); Assert.AreEqual(true, engine.Evaluate("delete x.foo")); Assert.IsInstanceOfType(engine.Evaluate("x.foo"), typeof(Undefined)); } [TestMethod, TestCategory("BugFix")] public void BugFix_NumericArgConversion_Delegate() { engine.Script.host = new HostFunctions(); engine.Script.sbyteFunc = new Func(arg => arg); engine.Script.nullableSByteFunc = new Func(arg => arg); engine.Script.floatFunc = new Func(arg => arg); engine.Script.nullableFloatFunc = new Func(arg => arg); engine.Script.doubleFunc = new Func(arg => arg); engine.Script.nullableDoubleFunc = new Func(arg => arg); engine.Script.decimalFunc = new Func(arg => arg); engine.Script.nullableDecimalFunc = new Func(arg => arg); Assert.AreEqual(123, engine.Evaluate("sbyteFunc(123)")); TestUtil.AssertException(() => engine.Execute("sbyteFunc(234)")); TestUtil.AssertException(() => engine.Execute("sbyteFunc(123.5)")); TestUtil.AssertException(() => engine.Execute("sbyteFunc(Math.PI)")); TestUtil.AssertException(() => engine.Execute("sbyteFunc(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("nullableSByteFunc(123)")); TestUtil.AssertException(() => engine.Execute("nullableSByteFunc(234)")); TestUtil.AssertException(() => engine.Execute("nullableSByteFunc(123.5)")); TestUtil.AssertException(() => engine.Execute("nullableSByteFunc(Math.PI)")); TestUtil.AssertException(() => engine.Execute("nullableSByteFunc(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("nullableSByteFunc(null)")); Assert.AreEqual(123, engine.Evaluate("floatFunc(123)")); Assert.AreEqual(123.5f, engine.Evaluate("floatFunc(123.5)")); Assert.AreEqual((float)Math.PI, engine.Evaluate("floatFunc(Math.PI)")); Assert.AreEqual((float)Math.PI, engine.Evaluate("floatFunc(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("nullableFloatFunc(123)")); Assert.AreEqual(123.5f, engine.Evaluate("nullableFloatFunc(123.5)")); Assert.AreEqual((float)Math.PI, engine.Evaluate("nullableFloatFunc(Math.PI)")); Assert.AreEqual((float)Math.PI, engine.Evaluate("nullableFloatFunc(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("nullableFloatFunc(null)")); Assert.AreEqual(123, engine.Evaluate("doubleFunc(123)")); Assert.AreEqual(123.5f, engine.Evaluate("doubleFunc(123.5)")); Assert.AreEqual(Math.PI, engine.Evaluate("doubleFunc(Math.PI)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("doubleFunc(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("nullableDoubleFunc(123)")); Assert.AreEqual(123.5f, engine.Evaluate("nullableDoubleFunc(123.5)")); Assert.AreEqual(Math.PI, engine.Evaluate("nullableDoubleFunc(Math.PI)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("nullableDoubleFunc(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("nullableDoubleFunc(null)")); Assert.AreEqual(123, engine.Evaluate("decimalFunc(123)")); Assert.AreEqual(123.5f, engine.Evaluate("decimalFunc(123.5)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("decimalFunc(Math.PI)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("decimalFunc(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("nullableDecimalFunc(123)")); Assert.AreEqual(123.5f, engine.Evaluate("nullableDecimalFunc(123.5)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("nullableDecimalFunc(Math.PI)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("nullableDecimalFunc(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("nullableDecimalFunc(null)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NumericArgConversion_Field() { engine.Script.host = new HostFunctions(); engine.Script.test = new NumericArgConversionTest(); Assert.AreEqual(123, engine.Evaluate("test.SByteField = 123; test.SByteField")); TestUtil.AssertException(() => engine.Execute("test.SByteField = 234")); TestUtil.AssertException(() => engine.Execute("test.SByteField = 123.5")); TestUtil.AssertException(() => engine.Execute("test.SByteField = Math.PI")); TestUtil.AssertException(() => engine.Execute("test.SByteField = host.toDecimal(Math.PI)")); Assert.AreEqual(123, engine.Evaluate("test.NullableSByteField = 123; test.NullableSByteField")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteField = 234")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteField = 123.5")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteField = Math.PI")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteField = host.toDecimal(Math.PI)")); Assert.IsNull(engine.Evaluate("test.NullableSByteField = null; test.NullableSByteField")); Assert.AreEqual(123, engine.Evaluate("test.FloatField = 123; test.FloatField")); Assert.AreEqual(123.5f, engine.Evaluate("test.FloatField = 123.5; test.FloatField")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.FloatField = Math.PI; test.FloatField")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.FloatField = host.toDecimal(Math.PI); test.FloatField")); Assert.AreEqual(123, engine.Evaluate("test.NullableFloatField = 123; test.NullableFloatField")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableFloatField = 123.5; test.NullableFloatField")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.NullableFloatField = Math.PI; test.NullableFloatField")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.NullableFloatField = host.toDecimal(Math.PI); test.NullableFloatField")); Assert.IsNull(engine.Evaluate("test.NullableFloatField = null; test.NullableFloatField")); Assert.AreEqual(123, engine.Evaluate("test.DoubleField = 123; test.DoubleField")); Assert.AreEqual(123.5f, engine.Evaluate("test.DoubleField = 123.5; test.DoubleField")); Assert.AreEqual(Math.PI, engine.Evaluate("test.DoubleField = Math.PI; test.DoubleField")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.DoubleField = host.toDecimal(Math.PI); test.DoubleField")); Assert.AreEqual(123, engine.Evaluate("test.NullableDoubleField = 123; test.NullableDoubleField")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableDoubleField = 123.5; test.NullableDoubleField")); Assert.AreEqual(Math.PI, engine.Evaluate("test.NullableDoubleField = Math.PI; test.NullableDoubleField")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.NullableDoubleField = host.toDecimal(Math.PI); test.NullableDoubleField")); Assert.IsNull(engine.Evaluate("test.NullableDoubleField = null; test.NullableDoubleField")); Assert.AreEqual(123, engine.Evaluate("test.DecimalField = 123; test.DecimalField")); Assert.AreEqual(123.5f, engine.Evaluate("test.DecimalField = 123.5; test.DecimalField")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.DecimalField = Math.PI; test.DecimalField")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.DecimalField = host.toDecimal(Math.PI); test.DecimalField")); Assert.AreEqual(123, engine.Evaluate("test.NullableDecimalField = 123; test.NullableDecimalField")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableDecimalField = 123.5; test.NullableDecimalField")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.NullableDecimalField = Math.PI; test.NullableDecimalField")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.NullableDecimalField = host.toDecimal(Math.PI); test.NullableDecimalField")); Assert.IsNull(engine.Evaluate("test.NullableDecimalField = null; test.NullableDecimalField")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NumericArgConversion_Method() { engine.Script.host = new HostFunctions(); engine.Script.test = new NumericArgConversionTest(); Assert.AreEqual(123, engine.Evaluate("test.SByteMethod(123)")); TestUtil.AssertException(() => engine.Execute("test.SByteMethod(234)")); TestUtil.AssertException(() => engine.Execute("test.SByteMethod(123.5)")); TestUtil.AssertException(() => engine.Execute("test.SByteMethod(Math.PI)")); TestUtil.AssertException(() => engine.Execute("test.SByteMethod(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("test.NullableSByteMethod(123)")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteMethod(234)")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteMethod(123.5)")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteMethod(Math.PI)")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteMethod(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("test.NullableSByteMethod(null)")); Assert.AreEqual(123, engine.Evaluate("test.FloatMethod(123)")); Assert.AreEqual(123.5f, engine.Evaluate("test.FloatMethod(123.5)")); TestUtil.AssertException(() => engine.Execute("test.FloatMethod(Math.PI)")); TestUtil.AssertException(() => engine.Execute("test.FloatMethod(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("test.NullableFloatMethod(123)")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableFloatMethod(123.5)")); TestUtil.AssertException(() => engine.Execute("test.NullableFloatMethod(Math.PI)")); TestUtil.AssertException(() => engine.Execute("test.NullableFloatMethod(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("test.NullableFloatMethod(null)")); Assert.AreEqual(123, engine.Evaluate("test.DoubleMethod(123)")); Assert.AreEqual(123.5f, engine.Evaluate("test.DoubleMethod(123.5)")); Assert.AreEqual(Math.PI, engine.Evaluate("test.DoubleMethod(Math.PI)")); TestUtil.AssertException(() => engine.Execute("test.DoubleMethod(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("test.NullableDoubleMethod(123)")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableDoubleMethod(123.5)")); Assert.AreEqual(Math.PI, engine.Evaluate("test.NullableDoubleMethod(Math.PI)")); TestUtil.AssertException(() => engine.Execute("test.NullableDoubleMethod(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("test.NullableDoubleMethod(null)")); Assert.AreEqual(123, engine.Evaluate("test.DecimalMethod(123)")); TestUtil.AssertException(() => engine.Execute("test.DecimalMethod(123.5)")); TestUtil.AssertException(() => engine.Execute("test.DecimalMethod(Math.PI)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.DecimalMethod(host.toDecimal(Math.PI))")); Assert.AreEqual(123, engine.Evaluate("test.NullableDecimalMethod(123)")); TestUtil.AssertException(() => engine.Execute("test.NullableDecimalMethod(123.5)")); TestUtil.AssertException(() => engine.Execute("test.NullableDecimalMethod(Math.PI)")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.NullableDecimalMethod(host.toDecimal(Math.PI))")); Assert.IsNull(engine.Evaluate("test.NullableDecimalMethod(null)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NumericArgConversion_Property() { engine.Script.host = new HostFunctions(); engine.Script.test = new NumericArgConversionTest(); Assert.AreEqual(123, engine.Evaluate("test.SByteProperty = 123; test.SByteProperty")); TestUtil.AssertException(() => engine.Execute("test.SByteProperty = 234")); TestUtil.AssertException(() => engine.Execute("test.SByteProperty = 123.5")); TestUtil.AssertException(() => engine.Execute("test.SByteProperty = Math.PI")); TestUtil.AssertException(() => engine.Execute("test.SByteProperty = host.toDecimal(Math.PI)")); Assert.AreEqual(123, engine.Evaluate("test.NullableSByteProperty = 123; test.NullableSByteProperty")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteProperty = 234")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteProperty = 123.5")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteProperty = Math.PI")); TestUtil.AssertException(() => engine.Execute("test.NullableSByteProperty = host.toDecimal(Math.PI)")); Assert.IsNull(engine.Evaluate("test.NullableSByteProperty = null; test.NullableSByteProperty")); Assert.AreEqual(123, engine.Evaluate("test.FloatProperty = 123; test.FloatProperty")); Assert.AreEqual(123.5f, engine.Evaluate("test.FloatProperty = 123.5; test.FloatProperty")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.FloatProperty = Math.PI; test.FloatProperty")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.FloatProperty = host.toDecimal(Math.PI); test.FloatProperty")); Assert.AreEqual(123, engine.Evaluate("test.NullableFloatProperty = 123; test.NullableFloatProperty")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableFloatProperty = 123.5; test.NullableFloatProperty")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.NullableFloatProperty = Math.PI; test.NullableFloatProperty")); Assert.AreEqual((float)Math.PI, engine.Evaluate("test.NullableFloatProperty = host.toDecimal(Math.PI); test.NullableFloatProperty")); Assert.IsNull(engine.Evaluate("test.NullableFloatProperty = null; test.NullableFloatProperty")); Assert.AreEqual(123, engine.Evaluate("test.DoubleProperty = 123; test.DoubleProperty")); Assert.AreEqual(123.5f, engine.Evaluate("test.DoubleProperty = 123.5; test.DoubleProperty")); Assert.AreEqual(Math.PI, engine.Evaluate("test.DoubleProperty = Math.PI; test.DoubleProperty")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.DoubleProperty = host.toDecimal(Math.PI); test.DoubleProperty")); Assert.AreEqual(123, engine.Evaluate("test.NullableDoubleProperty = 123; test.NullableDoubleProperty")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableDoubleProperty = 123.5; test.NullableDoubleProperty")); Assert.AreEqual(Math.PI, engine.Evaluate("test.NullableDoubleProperty = Math.PI; test.NullableDoubleProperty")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.NullableDoubleProperty = host.toDecimal(Math.PI); test.NullableDoubleProperty")); Assert.IsNull(engine.Evaluate("test.NullableDoubleProperty = null; test.NullableDoubleProperty")); Assert.AreEqual(123, engine.Evaluate("test.DecimalProperty = 123; test.DecimalProperty")); Assert.AreEqual(123.5f, engine.Evaluate("test.DecimalProperty = 123.5; test.DecimalProperty")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.DecimalProperty = Math.PI; test.DecimalProperty")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.DecimalProperty = host.toDecimal(Math.PI); test.DecimalProperty")); Assert.AreEqual(123, engine.Evaluate("test.NullableDecimalProperty = 123; test.NullableDecimalProperty")); Assert.AreEqual(123.5f, engine.Evaluate("test.NullableDecimalProperty = 123.5; test.NullableDecimalProperty")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.NullableDecimalProperty = Math.PI; test.NullableDecimalProperty")); Assert.AreEqual((double)(decimal)Math.PI, engine.Evaluate("test.NullableDecimalProperty = host.toDecimal(Math.PI); test.NullableDecimalProperty")); Assert.IsNull(engine.Evaluate("test.NullableDecimalProperty = null; test.NullableDecimalProperty")); } [TestMethod, TestCategory("BugFix")] public void BugFix_ScriptObjectInHostVariable() { engine.Script.host = new HostFunctions(); var documentInfo = new DocumentInfo("Expression") { Flags = DocumentFlags.IsTransient }; Assert.IsTrue(engine.Evaluate(documentInfo.MakeUnique(engine), "host.newVar({})", false).ToString().StartsWith("[HostVariable:", StringComparison.Ordinal)); } [TestMethod, TestCategory("BugFix")] public void BugFix_ScriptObjectInHostVariable_JScript() { engine.Dispose(); engine = new JScriptEngine(); BugFix_ScriptObjectInHostVariable(); } [TestMethod, TestCategory("BugFix")] public void BugFix_V8HostMethodClobbering() { engine.Script.host = new HostFunctions(); Assert.IsInstanceOfType(engine.Evaluate("host.newObj()"), typeof(PropertyBag)); TestUtil.AssertException(() => engine.Execute("host.newObj = 123")); Assert.IsInstanceOfType(engine.Evaluate("host.newObj()"), typeof(PropertyBag)); } [TestMethod, TestCategory("BugFix")] public void BugFix_V8NativePropertyHiding() { var foo = new { toString = new Func(() => "testValue") }; engine.Script.foo = foo; Assert.AreEqual(foo.toString(), engine.Evaluate("foo.toString()")); } [TestMethod, TestCategory("BugFix")] public void BugFix_V8NativePropertyHiding_Method() { var foo = new HideNativeJavaScriptMethod(); engine.Script.foo = foo; Assert.AreEqual(foo.toString(), engine.Evaluate("foo.toString()")); } [TestMethod, TestCategory("BugFix")] public void BugFix_EquatableComparison_DateTime() { engine.Script.x = new DateTime(2007, 5, 22); engine.Script.y = new DateTime(2007, 5, 22); engine.Script.z = new DateTime(2008, 5, 22); Assert.IsTrue((bool)engine.Evaluate("(x == y) && (x === y)")); Assert.IsFalse((bool)engine.Evaluate("(x != y) || (x !== y)")); Assert.IsTrue((bool)engine.Evaluate("(x != z) && (x !== z)")); Assert.IsFalse((bool)engine.Evaluate("(x == z) || (x === z)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_EquatableComparison_TimeSpan() { engine.Script.x = TimeSpan.FromSeconds(3.5); engine.Script.y = TimeSpan.FromSeconds(3.5); engine.Script.z = TimeSpan.FromSeconds(3.6); Assert.IsTrue((bool)engine.Evaluate("(x == y) && (x === y)")); Assert.IsFalse((bool)engine.Evaluate("(x != y) || (x !== y)")); Assert.IsTrue((bool)engine.Evaluate("(x != z) && (x !== z)")); Assert.IsFalse((bool)engine.Evaluate("(x == z) || (x === z)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_EquatableComparison_WrappedNumeric() { var host = new HostFunctions(); engine.Script.x = host.toUInt16(25); engine.Script.y = host.toUInt16(25); engine.Script.z = host.toUInt16(26); Assert.IsTrue((bool)engine.Evaluate("(x == y) && (x === y)")); Assert.IsFalse((bool)engine.Evaluate("(x != y) || (x !== y)")); Assert.IsTrue((bool)engine.Evaluate("(x != z) && (x !== z)")); Assert.IsFalse((bool)engine.Evaluate("(x == z) || (x === z)")); } [TestMethod, TestCategory("BugFix")] public void BugFix_VBScript_PropertyPut() { using (var vbEngine = new VBScriptEngine()) { vbEngine.Execute(@" class Test private myFoo public property get foo foo = myFoo end property public property let foo(value) myFoo = value end property end class set myTest = new Test myTest.foo = 123 "); Assert.AreEqual(123, Convert.ToInt32(vbEngine.Script.myTest.foo)); vbEngine.Script.myTest.foo = 456; Assert.AreEqual(456, Convert.ToInt32(vbEngine.Script.myTest.foo)); vbEngine.Script.myTest.foo = "blah"; Assert.AreEqual("blah", vbEngine.Script.myTest.foo); vbEngine.Script.myTest.foo = new DateTime(2007, 5, 22); Assert.AreEqual(new DateTime(2007, 5, 22), vbEngine.Script.myTest.foo); } } [TestMethod, TestCategory("BugFix")] public void BugFix_VBScript_PropertyPut_CrossEngine() { using (var vbEngine = new VBScriptEngine()) { vbEngine.Execute(@" class Test private myFoo public property get foo foo = myFoo end property public property let foo(value) myFoo = value end property end class set myTest = new Test myTest.foo = 123 "); engine.Script.test = vbEngine.Script.myTest; Assert.AreEqual(123, Convert.ToInt32(engine.Evaluate("test.foo"))); engine.Execute("test.foo = 456"); Assert.AreEqual(456, Convert.ToInt32(engine.Evaluate("test.foo"))); engine.Execute("test.foo = \"blah\""); Assert.AreEqual("blah", engine.Evaluate("test.foo")); engine.AddHostObject("bar", new DateTime(2007, 5, 22)); engine.Execute("test.foo = bar"); Assert.AreEqual(new DateTime(2007, 5, 22), engine.Evaluate("test.foo")); } } [TestMethod, TestCategory("BugFix")] public void BugFix_VBScript_PropertyPut_CrossEngine_VBScript() { engine.Dispose(); engine = new VBScriptEngine(); BugFix_VBScript_PropertyPut_CrossEngine(); } [TestMethod, TestCategory("BugFix")] public void BugFix_AddHostType_AssemblyQualifiedName() { engine.AddHostType("Random", typeof(Random).AssemblyQualifiedName); } [TestMethod, TestCategory("BugFix")] public void BugFix_DoubleExecution() { engine.Execute(@" var num = 0; function foo() { num += 1; throw new Error(); } "); try { engine.Script.foo(); } catch (ScriptEngineException) { } Assert.AreEqual(1, engine.Script.num); engine.Script.num = 0; dynamic foo = engine.Script.foo; try { foo(); } catch (ScriptEngineException) { } Assert.AreEqual(1, engine.Script.num); } [TestMethod, TestCategory("BugFix")] public void BugFix_DoubleExecution_JScript() { engine.Dispose(); engine = new JScriptEngine(); BugFix_DoubleExecution(); } [TestMethod, TestCategory("BugFix")] public void BugFix_DoubleExecution_CrossEngine() { using (var vbEngine = new VBScriptEngine()) { vbEngine.Execute(@" class Test public sub foo count = count + 1 err.raise 1, ""vb"", ""Bogus"" end sub end class set myTest = new Test count = 0 "); engine.Execute(@" function foo() { vbTest.foo(); } "); engine.Script.vbTest = vbEngine.Script.myTest; var message = string.Empty; try { engine.Script.foo(); } catch (ScriptEngineException exception) { message = exception.Message; } Assert.AreEqual("Error: Bogus", message); Assert.AreEqual(1, vbEngine.Script.count); } } [TestMethod, TestCategory("BugFix")] public void BugFix_DoubleExecution_CrossEngine_VBScript() { engine.Dispose(); engine = new VBScriptEngine(); using (var vbEngine = new VBScriptEngine()) { vbEngine.Execute(@" class Test public sub foo count = count + 1 err.raise 1, ""vb"", ""Bogus"" end sub end class set myTest = new Test count = 0 "); engine.Execute(@" sub foo vbTest.foo end sub "); engine.Script.vbTest = vbEngine.Script.myTest; var message = string.Empty; try { engine.Script.foo(); } catch (ScriptEngineException exception) { message = exception.Message; } Assert.AreEqual("Bogus", message); Assert.AreEqual(1, vbEngine.Script.count); } } [TestMethod, TestCategory("BugFix")] public void BugFix_DoubleExecution_Delegate() { // ReSharper disable EmptyGeneralCatchClause var num = 0; engine.Script.foo = new Action(() => { // ReSharper disable AccessToModifiedClosure num += 1; throw new Exception(); // ReSharper restore AccessToModifiedClosure }); try { engine.Script.foo(); } catch (ScriptEngineException) { } Assert.AreEqual(1, num); num = 0; dynamic foo = engine.Script.foo; try { foo(); } catch (Exception) { } Assert.AreEqual(1, num); // ReSharper restore EmptyGeneralCatchClause } [TestMethod, TestCategory("BugFix")] public void BugFix_DoubleExecution_Delegate_JScript() { engine.Dispose(); engine = new JScriptEngine(); BugFix_DoubleExecution_Delegate(); } [TestMethod, TestCategory("BugFix")] public void BugFix_SharedV8RuntimeLeak() { using (var runtime = new V8Runtime()) { using (runtime.CreateScriptEngine()) { } runtime.CollectGarbage(true); var heapSize = runtime.GetHeapInfo().TotalHeapSize; for (var i = 0; i < 500; i++) { using (runtime.CreateScriptEngine()) { } if ((i % 25) == 24) { runtime.CollectGarbage(true); } } runtime.CollectGarbage(true); Assert.IsFalse(runtime.GetHeapInfo().TotalHeapSize > (heapSize * 1.75)); } } [TestMethod, TestCategory("BugFix")] public void BugFix_V8ExceptionWhileInterrupting() { var context = new PropertyBag(); engine.Script.context = context; var startEvent = new ManualResetEventSlim(); var waitEvent = new ManualResetEventSlim(); var tokenSource = new CancellationTokenSource(); context["count"] = 0; context["startEvent"] = startEvent; context["waitForCancel"] = new Action(() => waitEvent.Wait(tokenSource.Token)); var thread = new Thread(() => { try { engine.Execute(@" context.count = 1; context.startEvent.Set(); context.waitForCancel(); context.count = 2; "); } catch (ScriptEngineException) { } catch (ScriptInterruptedException) { } }); thread.Start(); startEvent.Wait(Timeout.Infinite); tokenSource.Cancel(); engine.Interrupt(); thread.Join(); Assert.AreEqual(1, context["count"]); } [TestMethod, TestCategory("BugFix")] public void BugFix_NestedType() { engine.AddHostType(typeof(NestedTypeTest)); Assert.AreEqual(NestedTypeTest.NestedType.Foo, engine.Evaluate("NestedTypeTest.NestedType.Foo")); Assert.AreEqual(NestedTypeTest.NestedType.Bar, engine.Evaluate("NestedTypeTest.NestedType.Bar")); Assert.AreEqual(NestedTypeTest.NestedType.Baz, engine.Evaluate("NestedTypeTest.NestedType.Baz")); Assert.AreEqual(NestedTypeTest.NestedType.Qux, engine.Evaluate("NestedTypeTest.NestedType.Qux")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NestedType_JScript() { engine.Dispose(); engine = new JScriptEngine(); engine.AddHostType(typeof(NestedTypeTest)); Assert.AreEqual(NestedTypeTest.NestedType.Foo, engine.Evaluate("NestedTypeTest.NestedType.Foo")); Assert.AreEqual(NestedTypeTest.NestedType.Bar, engine.Evaluate("NestedTypeTest.NestedType.Bar")); Assert.AreEqual(NestedTypeTest.NestedType.Baz, engine.Evaluate("NestedTypeTest.NestedType.Baz")); Assert.AreEqual(NestedTypeTest.NestedType.Qux, engine.Evaluate("NestedTypeTest.NestedType.Qux")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NestedType_VBScript() { engine.Dispose(); engine = new VBScriptEngine(); engine.AddHostType(typeof(NestedTypeTest)); Assert.AreEqual(NestedTypeTest.NestedType.Foo, engine.Evaluate("NestedTypeTest.NestedType.Foo")); Assert.AreEqual(NestedTypeTest.NestedType.Bar, engine.Evaluate("NestedTypeTest.NestedType.Bar")); Assert.AreEqual(NestedTypeTest.NestedType.Baz, engine.Evaluate("NestedTypeTest.NestedType.Baz")); Assert.AreEqual(NestedTypeTest.NestedType.Qux, engine.Evaluate("NestedTypeTest.NestedType.Qux")); } [TestMethod, TestCategory("BugFix")] public void BugFix_NestedType_VBScript_VB() { TestUtil.InvokeVBTestSub(@" Using engine As New VBScriptEngine engine.AddHostType(GetType(BugFixTest.NestedTypeTest)) Assert.AreEqual(BugFixTest.NestedTypeTest.NestedType.Foo, engine.Evaluate(""NestedTypeTest.NestedType.Foo"")) Assert.AreEqual(BugFixTest.NestedTypeTest.NestedType.Bar, engine.Evaluate(""NestedTypeTest.NestedType.Bar"")) Assert.AreEqual(BugFixTest.NestedTypeTest.NestedType.Baz, engine.Evaluate(""NestedTypeTest.NestedType.Baz"")) Assert.AreEqual(BugFixTest.NestedTypeTest.NestedType.Qux, engine.Evaluate(""NestedTypeTest.NestedType.Qux"")) End Using "); } [TestMethod, TestCategory("BugFix")] public void BugFix_V8_Date_now() { var value = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; var scriptValue = Convert.ToDouble(engine.Evaluate("Date.now()")); Assert.IsTrue(Math.Abs(scriptValue - value) < 25.0); } [TestMethod, TestCategory("BugFix")] public void BugFix_NonEnumerablePropertyAccess() { engine.Script.dump = new Action((obj, value) => { Assert.AreEqual(value, obj.message); }); engine.Execute(@" message = 'hello'; dump({ message: message }, message); message = 'world'; dump(new Error(message), message); "); } [TestMethod, TestCategory("BugFix")] public void BugFix_NonEnumerablePropertyAccess_JScript() { engine.Dispose(); engine = new JScriptEngine(); engine.Script.dump = new Action((obj, value) => { Assert.AreEqual(value, obj.message); }); engine.Execute(@" message = 'hello'; dump({ message: message }, message); message = 'world'; dump(new Error(message), message); "); } [TestMethod, TestCategory("BugFix")] public void BugFix_NonEnumerablePropertyAccess_VB() { TestUtil.InvokeVBTestSub(@" Using engine As New V8ScriptEngine engine.Script.dump = Sub(obj As Object, value As String) Assert.AreEqual(value, obj.message) End Sub engine.Execute( ""message = 'hello';"" & _ ""dump({ message: message }, message);"" & _ ""message = 'world';"" & _ ""dump(new Error(message), message);"" ) End Using "); } [TestMethod, TestCategory("BugFix")] public void BugFix_NonEnumerablePropertyAccess_JScript_VB() { TestUtil.InvokeVBTestSub(@" Using engine As New JScriptEngine engine.Script.dump = Sub(obj As Object, value As String) Assert.AreEqual(value, obj.message) End Sub engine.Execute( ""message = 'hello';"" & _ ""dump({ message: message }, message);"" & _ ""message = 'world';"" & _ ""dump(new Error(message), message);"" ) End Using "); } [TestMethod, TestCategory("BugFix")] public void BugFix_NonexistentPropertyAccess_VB() { TestUtil.InvokeVBTestSub(@" Using engine As New V8ScriptEngine engine.Script.dump = Sub(obj As Object, message As Object, stack As Object) Assert.AreEqual(message, obj.message) Assert.AreEqual(stack, obj.stack) End Sub engine.Execute( ""message = 'hello';"" & _ ""stack = 'world';"" & _ ""dump({ message: message, stack: stack }, message, stack);"" & _ ""dump({ message: message }, message, undefined);"" ) End Using "); } [TestMethod, TestCategory("BugFix")] public void BugFix_NonexistentPropertyAccess_JScript_VB() { TestUtil.InvokeVBTestSub(@" Using engine As New JScriptEngine engine.Script.dump = Sub(obj As Object, message As Object, stack As Object) Assert.AreEqual(message, obj.message) Assert.AreEqual(stack, obj.stack) End Sub engine.Execute( ""message = 'hello';"" & _ ""stack = 'world';"" & _ ""dump({ message: message, stack: stack }, message, stack);"" & _ ""dump({ message: message }, message, undefined);"" ) End Using "); } [TestMethod, TestCategory("BugFix")] public void BugFix_JsonDotNetSerialization() { var obj = engine.Evaluate("({foo:123,bar:'baz'})"); Assert.AreEqual("{\"foo\":123,\"bar\":\"baz\"}", JsonConvert.SerializeObject(obj)); } [TestMethod, TestCategory("BugFix")] public void BugFix_RuntimeCompileException() { using (var runtime = new V8Runtime()) { // ReSharper disable once AccessToDisposedClosure TestUtil.AssertException(() => runtime.Compile("function foo bar () {}")); } } [TestMethod, TestCategory("BugFix")] public void BugFix_PropertyAccessorScriptability() { engine.Script.testObject = new PropertyAccessorScriptability(); engine.Execute("testObject.Foo = 123"); Assert.AreEqual(123, engine.Evaluate("testObject.Foo")); TestUtil.AssertException(() => engine.Execute("testObject.Bar = 123")); TestUtil.AssertException(() => engine.Evaluate("testObject.Bar")); } [TestMethod, TestCategory("BugFix")] public void BugFix_PropertyAccessorScriptability_Static() { engine.AddHostType("TestObject", typeof(PropertyAccessorScriptabilityStatic)); engine.Execute("TestObject.Foo = 123"); Assert.AreEqual(123, engine.Evaluate("TestObject.Foo")); TestUtil.AssertException(() => engine.Execute("TestObject.Bar = 123")); TestUtil.AssertException(() => engine.Evaluate("TestObject.Bar")); } [TestMethod, TestCategory("BugFix")] public void BugFix_PublicOverrideInInternalClass() { engine.Script.foo = new PublicOverrideTest(); Assert.AreEqual(789, engine.Evaluate("foo.AbstractMethod()")); Assert.AreEqual(456, engine.Evaluate("foo.VirtualMethod()")); Assert.AreEqual("baz", engine.Evaluate("foo.AbstractProperty")); Assert.AreEqual("bar", engine.Evaluate("foo.VirtualProperty")); } [TestMethod, TestCategory("BugFix")] public void BugFix_ImplicitConversionOperators() { engine.Script.doc = XDocument.Parse("barqux"); engine.AddHostType(typeof(Enumerable)); engine.AddHostType(typeof(XmlName)); Assert.AreEqual(1, engine.Evaluate("doc.Root.Elements('foo').Count()")); Assert.AreEqual("bar", engine.Evaluate("doc.Root.Elements('foo').First().ToString()")); Assert.AreEqual(1, engine.Evaluate("doc.Root.Elements(new XmlName('foo')).Count()")); Assert.AreEqual("bar", engine.Evaluate("doc.Root.Elements(new XmlName('foo')).First().ToString()")); } [TestMethod, TestCategory("BugFix")] public void BugFix_TextDigest() { const string allChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var random = new Random(); var proxy = V8TestProxy.Create(); for (var i = 0; i < 1024; i++) { var chars = new char[random.Next(8, 256)]; for (var j = 0; j < chars.Length; j++) { chars[j] = allChars[random.Next(allChars.Length - 1)]; } var value = new string(chars); Assert.AreEqual(value.GetDigest(), proxy.GetNativeDigest(value)); } } [TestMethod, TestCategory("BugFix")] public void BugFix_WindowsScriptItem_SetPropertyLeak() { WeakReference wr = null; var proc = new Action(() => { using (var vbs = new VBScriptEngine()) { wr = new WeakReference(vbs); vbs.AddHostType(typeof(Console)); vbs.Script["foo"] = "bar"; } }); proc(); GC.Collect(); GC.WaitForPendingFinalizers(); Assert.IsFalse(wr.IsAlive); } [TestMethod, TestCategory("BugFix")] public void BugFix_InteropMethodCallWithInteropArg() { engine.AddHostObject("host", new HostFunctions()); engine.AddHostType("Automation", typeof(CUIAutomationClass)); engine.AddHostType(typeof(UIA_PropertyIds)); engine.AddHostType(typeof(UIA_ControlTypeIds)); engine.AddHostType(typeof(IUIAutomationCondition)); engine.Execute(@" automation = new Automation(); condition1 = automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_CustomControlTypeId); condition2 = automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_CustomControlTypeId); condition3 = automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_CustomControlTypeId); conditions = host.newArr(IUIAutomationCondition, 3); conditions[0] = condition1; conditions[1] = condition2; conditions[2] = condition3; andCondition = automation.CreateAndCondition(condition1, condition2); andAndCondition = automation.CreateAndConditionFromArray(conditions); "); } [TestMethod, TestCategory("BugFix")] public void BugFix_InteropMethodCallWithInteropArg_JScript() { engine.Dispose(); engine = new JScriptEngine(); BugFix_InteropMethodCallWithInteropArg(); } [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 #region miscellaneous private static void VariantClearTestHelper(object x) { using (var engine = new JScriptEngine()) { engine.AddHostObject("x", x); engine.Evaluate("x"); } } public class NullableTest { public int? Field; public double? Property; public object Method(int? value) { return value + 1; } public object Method(double? value) { return value * 2.0; } } public abstract class AmbiguousAttributeTestBase { public abstract object this[int index] { get; } public abstract object this[string key] { get; } } public class AmbiguousAttributeTest : AmbiguousAttributeTestBase { public override object this[int index] { get { return null; } } public override object this[string key] { get { return null; } } public void Foo() {} } public class ResurrectionTestWrapper { private readonly IDisposable target; public ResurrectionTestWrapper(IDisposable target) { this.target = target; } ~ResurrectionTestWrapper() { target.Dispose(); } } public interface IAmbiguousIndexerBase1 { object this[int i] { get; set; } } public interface IAmbiguousIndexerBase2 { object this[int i] { get; set; } object this[DayOfWeek d] { get; set; } } // ReSharper disable PossibleInterfaceMemberAmbiguity public interface IAmbiguousIndexer : IAmbiguousIndexerBase1, IAmbiguousIndexerBase2 { new object this[int i] { get; set; } } // ReSharper restore PossibleInterfaceMemberAmbiguity 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; } } } public class InaccessiblePropertyAccessors { // ReSharper disable UnusedMember.Local private int dummy; public int NoGetter { set { dummy = value; } } public int PrivateGetter { private get { return dummy; } set { dummy = value; } } public int NoSetter { get { return dummy; } } public int PrivateSetter { get { return dummy; } private set { dummy = value; } } // ReSharper restore UnusedMember.Local } public static class InaccessiblePropertyAccessorsStatic { // ReSharper disable UnusedMember.Local private static int dummy = 12345; public static int NoGetter { set { dummy = value; } } public static int PrivateGetter { private get { return dummy; } set { dummy = value; } } public static int NoSetter { get { return dummy; } } public static int PrivateSetter { get { return dummy; } private set { dummy = value; } } // ReSharper restore UnusedMember.Local } [NoDefaultScriptAccess] public class PropertyAccessorScriptability { [ScriptMember] public int Foo { get; set; } [ScriptMember] public int Bar { [NoScriptAccess] get; [NoScriptAccess] set; } } [NoDefaultScriptAccess] public static class PropertyAccessorScriptabilityStatic { [ScriptMember] public static int Foo { get; set; } [ScriptMember] public static int Bar { [NoScriptAccess] get; [NoScriptAccess] set; } } public class DynamicMethodArgTest : DynamicObject { public override IEnumerable GetDynamicMemberNames() { yield return "RunTest"; } // ReSharper disable RedundantOverridenMember public override bool TryGetMember(GetMemberBinder binder, out object result) { // this override is redundant, but required for the test return base.TryGetMember(binder, out result); } // ReSharper restore RedundantOverridenMember public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (binder.Name == "RunTest") { result = string.Join(" ", args); return true; } return base.TryInvokeMember(binder, args, out result); } } public class NumericArgConversionTest { public sbyte SByteField; public sbyte? NullableSByteField; public float FloatField; public float? NullableFloatField; public double DoubleField; public double? NullableDoubleField; public decimal DecimalField; public decimal? NullableDecimalField; public sbyte SByteMethod(sbyte arg) { return arg; } public sbyte? NullableSByteMethod(sbyte? arg) { return arg; } public float FloatMethod(float arg) { return arg; } public float? NullableFloatMethod(float? arg) { return arg; } public double DoubleMethod(double arg) { return arg; } public double? NullableDoubleMethod(double? arg) { return arg; } public decimal DecimalMethod(decimal arg) { return arg; } public decimal? NullableDecimalMethod(decimal? arg) { return arg; } public sbyte SByteProperty { get; set; } public sbyte? NullableSByteProperty { get; set; } public float FloatProperty { get; set; } public float? NullableFloatProperty { get; set; } public double DoubleProperty { get; set; } public double? NullableDoubleProperty { get; set; } public decimal DecimalProperty { get; set; } public decimal? NullableDecimalProperty { get; set; } } public class HideNativeJavaScriptMethod { // ReSharper disable InconsistentNaming public string toString() { return ToString(); } // ReSharper restore InconsistentNaming } public class NestedTypeTest { public enum NestedType { Foo, Bar, Baz, Qux } } public abstract class PublicOverrideTestBase { public abstract int AbstractMethod(); public virtual int VirtualMethod() { return 123; } public abstract string AbstractProperty { get; } public virtual string VirtualProperty { get { return "foo"; } } } internal class PublicOverrideTest : PublicOverrideTestBase { public override int AbstractMethod() { return 789; } public override int VirtualMethod() { return 456; } public override string AbstractProperty { get { return "baz"; } } public override string VirtualProperty { get { return "bar"; } } } public class XmlName { private readonly string name; public XmlName(string name) { this.name = name; } public static implicit operator XName(XmlName xmlName) { return xmlName.name; } } #endregion } }