// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using Microsoft.ClearScript.V8;
using Microsoft.ClearScript.Util;
using Microsoft.ClearScript.Windows;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UIAutomationClient;
namespace Microsoft.ClearScript.Test
{
public partial class BugFixTest
{
#region test methods
// ReSharper disable InconsistentNaming
[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_FloatParameterBinding_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
BugFix_FloatParameterBinding();
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_UInt32RoundTrip_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
BugFix_UInt32RoundTrip();
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_IDispatchExArgLeak_SetProperty_JScript()
{
// ReSharper disable RedundantAssignment
engine.Dispose();
engine = new JScriptEngine();
WeakReference wr = null;
new Action(() =>
{
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);
})();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Assert.IsFalse(wr.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(() =>
{
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);
})();
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_TooManyDebugApplications()
{
var engines = Enumerable.Range(0, 2048).Select(_ => new JScriptEngine(WindowsScriptEngineFlags.EnableDebugging)).ToArray();
Array.ForEach(engines, tempEngine => tempEngine.Dispose());
}
[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_Iteration_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
var x = new PropertyBag { ["foo"] = 123, ["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_Iteration_JScript_GlobalRenaming()
{
using (Scope.Create(() => HostSettings.CustomAttributeLoader, loader => HostSettings.CustomAttributeLoader = loader))
{
HostSettings.CustomAttributeLoader = new CamelCaseAttributeLoader();
engine.Dispose();
engine = new JScriptEngine();
var x = new PropertyBag { ["foo"] = 123, ["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_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 { ["foo"] = 123, ["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 { ["foo"] = 123, ["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_ScriptObjectInHostVariable_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
BugFix_ScriptObjectInHostVariable();
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_DoubleExecution_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
BugFix_DoubleExecution();
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_DoubleExecution_Delegate_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
BugFix_DoubleExecution_Delegate();
}
[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_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_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_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_InteropMethodCallWithInteropArg_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
BugFix_InteropMethodCallWithInteropArg();
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_JScript_TargetInvocationException()
{
engine.Dispose();
engine = new JScriptEngine(WindowsScriptEngineFlags.EnableDebugging);
engine.Script.foo = new Action(() => throw new Exception("bar"));
engine.Execute("function test() { foo(); }");
try
{
engine.Invoke("test");
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("bar\n"));
Assert.IsTrue(exception.ErrorDetails.Contains("(Script:0:18) -> foo"));
}
}
[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_IDispatchExArgLeak_SetProperty_VBScript()
{
// ReSharper disable RedundantAssignment
engine.Dispose();
engine = new VBScriptEngine();
WeakReference wr = null;
new Action(() =>
{
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);
})();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Assert.IsFalse(wr.IsAlive);
// ReSharper restore RedundantAssignment
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_IDispatchExArgLeak_GetProperty_VBScript()
{
engine.Dispose();
engine = new VBScriptEngine();
WeakReference wr1 = null;
WeakReference wr2 = null;
new Action(() =>
{
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
");
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 var result));
Assert.AreEqual(x1.ToString() + x2, result);
})();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Assert.IsFalse(wr1.IsAlive);
Assert.IsFalse(wr2.IsAlive);
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_IDispatchExArgLeak_InvokeMethod_VBScript()
{
engine.Dispose();
engine = new VBScriptEngine();
WeakReference wr1 = null;
WeakReference wr2 = null;
new Action(() =>
{
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);
})();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Assert.IsFalse(wr1.IsAlive);
Assert.IsFalse(wr2.IsAlive);
}
[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_PropertyBag_Iteration_VBScript()
{
engine.Dispose();
engine = new VBScriptEngine();
var x = new PropertyBag { ["foo"] = 123, ["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_PropertyBag_Iteration_VBScript_GlobalRenaming()
{
using (Scope.Create(() => HostSettings.CustomAttributeLoader, loader => HostSettings.CustomAttributeLoader = loader))
{
HostSettings.CustomAttributeLoader = new CamelCaseAttributeLoader();
engine.Dispose();
engine = new VBScriptEngine();
var x = new PropertyBag { ["foo"] = 123, ["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_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_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_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_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_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\")"));
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_DefaultPropertyEmulation()
{
engine.Dispose();
engine = new VBScriptEngine(WindowsScriptEngineFlags.EnableDebugging);
engine.AddHostObject("foo", new Variable { Value = 1, IsValid = true });
engine.AddHostObject("bar", new Variable { Value = 2, IsValid = false });
engine.Script.baz = new Variable { Value = 3, IsValid = false };
engine.Execute(@"
class CQux
private v
public sub Class_Initialize
v = 123
end sub
public default property get Value
Value = v
end property
end class
class CQuux
end class
set qux = new CQux
set quux = new CQuux
");
engine.Execute("foo = 456");
Assert.AreEqual(456, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
engine.Execute("foo = bar");
Assert.AreEqual(2, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
engine.Execute("foo = foo + 789");
Assert.AreEqual(791, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
engine.Execute("foo = bar + baz");
Assert.AreEqual(5, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
engine.Execute("foo = qux");
Assert.AreEqual(123, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
engine.Execute("foo = bar + qux");
Assert.AreEqual(125, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
engine.Execute("foo = bar + baz + qux");
Assert.AreEqual(128, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
TestUtil.AssertException(() => engine.Execute("foo = quux"));
Assert.AreEqual(128, engine.Evaluate("foo.Value"));
Assert.AreEqual(true, engine.Evaluate("foo.IsValid"));
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_ErrorDetails()
{
engine.Dispose();
engine = new VBScriptEngine(WindowsScriptEngineFlags.EnableDebugging);
engine.AddHostObject("foo", new Variable());
engine.Execute("sub test\nfoo = \"xyz\"\nend sub");
try
{
engine.Invoke("test");
Assert.Fail();
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("at test (Script:1:0) -> foo = \"xyz\""));
}
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_ErrorDetails_NoDebugger()
{
engine.Dispose();
engine = new VBScriptEngine();
engine.AddHostObject("foo", new Variable());
engine.Execute("sub test\nfoo = \"xyz\"\nend sub");
try
{
engine.Invoke("test");
Assert.Fail();
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("at ([unknown]:1:0)"));
}
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_ErrorDetails_DirectAccess()
{
engine.Dispose();
engine = new VBScriptEngine(WindowsScriptEngineFlags.EnableDebugging);
engine.AddHostObject("foo", HostItemFlags.DirectAccess, new Variable());
engine.Execute("sub test\nfoo = \"xyz\"\nend sub");
try
{
engine.Invoke("test");
Assert.Fail();
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("at test (Script:1:0) -> foo = \"xyz\""));
}
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_ErrorDetails_DirectAccess_NoDebugger()
{
engine.Dispose();
engine = new VBScriptEngine();
engine.AddHostObject("foo", HostItemFlags.DirectAccess, new Variable());
engine.Execute("sub test\nfoo = \"xyz\"\nend sub");
try
{
engine.Invoke("test");
Assert.Fail();
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("at ([unknown]:1:0)"));
}
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_ErrorDetails_DirectAccess_ReadOnlyProperty()
{
engine.Dispose();
engine = new VBScriptEngine(WindowsScriptEngineFlags.EnableDebugging);
engine.AddHostObject("foo", HostItemFlags.DirectAccess, new Variable());
engine.Execute("sub test\nfoo.Description = \"bogus\"\nend sub");
try
{
engine.Invoke("test");
Assert.Fail();
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("at test (Script:1:0) -> foo.Description = \"bogus\""));
}
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_ErrorDetails_DirectAccess_ReadOnlyProperty_NoDebugger()
{
engine.Dispose();
engine = new VBScriptEngine();
engine.AddHostObject("foo", HostItemFlags.DirectAccess, new Variable());
engine.Execute("sub test\nfoo.Description = \"bogus\"\nend sub");
try
{
engine.Invoke("test");
Assert.Fail();
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("at ([unknown]:1:0)"));
}
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_VBScript_TargetInvocationException()
{
engine.Dispose();
engine = new VBScriptEngine(WindowsScriptEngineFlags.EnableDebugging);
engine.Script.foo = new Action(() => throw new Exception("bar"));
engine.Execute("sub test\nfoo\nend sub");
try
{
engine.Invoke("test");
}
catch (ScriptEngineException exception)
{
Assert.IsTrue(exception.ErrorDetails.Contains("bar\n at test (Script:1:0) -> foo"));
}
}
[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_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_V8ArrayBufferLeak()
{
TestUtil.InvokeConsoleTest("BugFix_V8ArrayBufferLeak");
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_DefaultArgs_Indexer_JScript()
{
engine.Dispose();
engine = new JScriptEngine();
engine.Script.test = new DefaultArgsTestObject();
engine.Execute("test.Item.set(Math.PI)");
Assert.AreEqual(Math.PI, engine.Evaluate("test.Item()"));
Assert.AreEqual(Math.PI, engine.Evaluate("test.Item.get()"));
engine.Execute("test.Item.set(456, Math.E)");
Assert.AreEqual(Math.E, engine.Evaluate("test.Item(456)"));
Assert.AreEqual(Math.E, engine.Evaluate("test.Item.get(456)"));
engine.Execute("test.Item.set(789, 'bar', Math.PI * Math.E)");
Assert.AreEqual(Math.PI * Math.E, engine.Evaluate("test.Item(789, 'bar')"));
Assert.AreEqual(Math.PI * Math.E, engine.Evaluate("test.Item.get(789, 'bar')"));
engine.Execute("test.Item = Math.sqrt(Math.PI)");
Assert.AreEqual(Math.Sqrt(Math.PI), engine.Evaluate("test.Item()"));
Assert.AreEqual(Math.Sqrt(Math.PI), engine.Evaluate("test.Item.get()"));
engine.Execute("test.Item(456) = Math.sqrt(Math.E)");
Assert.AreEqual(Math.Sqrt(Math.E), engine.Evaluate("test.Item(456)"));
Assert.AreEqual(Math.Sqrt(Math.E), engine.Evaluate("test.Item.get(456)"));
engine.Execute("test.Item(789, 'bar') = Math.sqrt(Math.PI * Math.E)");
Assert.AreEqual(Math.Sqrt(Math.PI * Math.E), engine.Evaluate("test.Item(789, 'bar')"));
Assert.AreEqual(Math.Sqrt(Math.PI * Math.E), engine.Evaluate("test.Item.get(789, 'bar')"));
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_DefaultArgs_Indexer_VBScript()
{
engine.Dispose();
engine = new VBScriptEngine();
engine.Script.test = new DefaultArgsTestObject();
engine.Script.pi = Math.PI;
engine.Script.e = Math.E;
engine.Execute("test.Item.set pi");
Assert.AreEqual(Math.PI, engine.Evaluate("test.Item()"));
Assert.AreEqual(Math.PI, engine.Evaluate("test.Item.get()"));
engine.Execute("test.Item.set 456, e");
Assert.AreEqual(Math.E, engine.Evaluate("test.Item(456)"));
Assert.AreEqual(Math.E, engine.Evaluate("test.Item.get(456)"));
engine.Execute("test.Item.set 789, \"bar\", pi * e");
Assert.AreEqual(Math.PI * Math.E, engine.Evaluate("test.Item(789, \"bar\")"));
Assert.AreEqual(Math.PI * Math.E, engine.Evaluate("test.Item.get(789, \"bar\")"));
engine.Execute("test.Item = sqr(pi)");
Assert.AreEqual(Math.Sqrt(Math.PI), engine.Evaluate("test.Item()"));
Assert.AreEqual(Math.Sqrt(Math.PI), engine.Evaluate("test.Item.get()"));
engine.Execute("test.Item(456) = sqr(e)");
Assert.AreEqual(Math.Sqrt(Math.E), engine.Evaluate("test.Item(456)"));
Assert.AreEqual(Math.Sqrt(Math.E), engine.Evaluate("test.Item.get(456)"));
engine.Execute("test.Item(789, \"bar\") = sqr(pi * e)");
Assert.AreEqual(Math.Sqrt(Math.PI * Math.E), engine.Evaluate("test.Item(789, \"bar\")"));
Assert.AreEqual(Math.Sqrt(Math.PI * Math.E), engine.Evaluate("test.Item.get(789, \"bar\")"));
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_MultidimensionalArray_VBScript()
{
engine.Dispose();
engine = new VBScriptEngine();
engine.AddHostObject("host", new HostFunctions());
engine.Script.x = new int[2];
engine.Script.y = new int[3, 4];
Assert.AreEqual(0, engine.Evaluate("x(1)"));
engine.Execute("x(1) = 123");
Assert.AreEqual(123, engine.Evaluate("x(1)"));
Assert.AreEqual(0, engine.Evaluate("y(2, 3)"));
engine.Execute("y(2, 3) = 456");
Assert.AreEqual(456, engine.Evaluate("y(2, 3)"));
engine.Execute(@"
x = host.newVar(x)
x(1) = 0
y = host.newVar(y)
y(2, 3) = 0
");
Assert.AreEqual(0, engine.Evaluate("x(1)"));
engine.Execute("x(1) = 123");
Assert.AreEqual(123, engine.Evaluate("x(1)"));
Assert.AreEqual(0, engine.Evaluate("y(2, 3)"));
engine.Execute("y(2, 3) = 456");
Assert.AreEqual(456, engine.Evaluate("y(2, 3)"));
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_XMLDOM_Enumeration_JScript()
{
var document = new MSXML2.DOMDocument60Class();
document.loadXML(@"
");
engine.Dispose();
engine = new JScriptEngine();
engine.AddHostObject("document", document);
engine.Execute(@"
allPages = document.getElementsByTagName('page');
count = 0;
for (var e = new Enumerator(allPages); !e.atEnd(); e.moveNext()) {
++count;
}
");
Assert.AreEqual(11, engine.Script.count);
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_XMLDOM_Enumeration_VBScript()
{
var document = new MSXML2.DOMDocument60Class();
document.loadXML(@"
");
engine.Dispose();
engine = new VBScriptEngine();
engine.AddHostObject("document", document);
engine.Execute(@"
set allPages = document.getElementsByTagName(""page"")
count = 0
for each page in allPages
count = count + 1
next
");
Assert.AreEqual(11, engine.Script.count);
}
[TestMethod, TestCategory("BugFix")]
public void BugFix_SparseArgumentBinding()
{
engine.Dispose();
engine = new VBScriptEngine();
engine.Script.test = new SparseArgumentTest();
engine.UseReflectionBindFallback = true;
Assert.AreEqual("'foo' 'bar' 789 456", engine.Evaluate("test.go(\"foo\", \"bar\", 789)"));
Assert.AreEqual("'foo' 'bar' 123 456", engine.Evaluate("test.go(\"foo\", \"bar\")"));
Assert.AreEqual("'foo' 'xyz' 789 456", engine.Evaluate("test.go(\"foo\", , 789)"));
Assert.AreEqual("'foo' 'xyz' 123 789", engine.Evaluate("test.go(\"foo\", , , 789)"));
Assert.AreEqual("'foo' 'xyz' 123 456", engine.Evaluate("test.go(\"foo\")"));
}
// ReSharper restore InconsistentNaming
#endregion
#region miscellaneous
public sealed class SparseArgumentTest
{
public string Go(string first, string second = "xyz", int third = 123, int fourth = 456)
{
return $"'{first}' '{second}' {third} {fourth}";
}
}
private static void VariantClearTestHelper(object x)
{
using (var engine = new JScriptEngine())
{
engine.AddHostObject("x", x);
engine.Evaluate("x");
}
}
private sealed class CamelCaseAttributeLoader : CustomAttributeLoader
{
public override T[] LoadCustomAttributes(ICustomAttributeProvider resource, bool inherit)
{
if (typeof(T) == typeof(ScriptMemberAttribute) && (resource is MemberInfo member))
{
var name = char.ToLowerInvariant(member.Name[0]) + member.Name.Substring(1);
return new[] { new ScriptMemberAttribute(name) } as T[];
}
return base.LoadCustomAttributes(resource, inherit);
}
}
#endregion
}
}