diff --git a/src/Topaz.Test/BasicObjectTests.cs b/src/Topaz.Test/BasicObjectTests.cs index 3a97b1a..971f1c6 100644 --- a/src/Topaz.Test/BasicObjectTests.cs +++ b/src/Topaz.Test/BasicObjectTests.cs @@ -55,7 +55,7 @@ public void TestObject1(bool useThreadSafeJsObjects) Assert.AreEqual(6, values.Count); Assert.AreEqual(js, deserialized); } - + Assert.AreEqual(1, js.a1); Assert.AreEqual(2, js.a2); Assert.AreEqual(3, js.a3); @@ -63,4 +63,19 @@ public void TestObject1(bool useThreadSafeJsObjects) Assert.AreEqual(5, js[null]); Assert.IsNull(model.p1); } + + [TestCase] + public void TestInQuery() + { + var engine = new TopazEngine(); + engine.ExecuteScript("let obj = { a: { b: '123' }}"); + Assert.That(engine.ExecuteExpression("'b' in obj.a"), Is.True); + Assert.That(engine.ExecuteExpression("obj.a.Contains('b')"), Is.True); + Assert.That(engine.ExecuteExpression("obj.a.hasOwn('b')"), Is.True); + Assert.That(engine.ExecuteExpression("obj.a.hasOwnProperty('b')"), Is.True); + Assert.That(engine.ExecuteExpression("'c' in obj.a"), Is.False); + Assert.That(engine.ExecuteExpression("obj.a.Contains('c')"), Is.False); + Assert.That(engine.ExecuteExpression("obj.a.hasOwn('c')"), Is.False); + Assert.That(engine.ExecuteExpression("obj.a.hasOwnProperty('c')"), Is.False); + } } \ No newline at end of file diff --git a/src/Topaz.Test/DelegateArgumentConversionTests.cs b/src/Topaz.Test/DelegateArgumentConversionTests.cs index a544739..49649c9 100644 --- a/src/Topaz.Test/DelegateArgumentConversionTests.cs +++ b/src/Topaz.Test/DelegateArgumentConversionTests.cs @@ -111,4 +111,14 @@ public void TestActionConstruction() Assert.AreEqual(37, model.c); Assert.AreEqual(81, model.d); } + + [Test] + public void TestLambdaFunction() + { + var engine = new TopazEngine(); + bool isCalled = false; + engine.SetValue("print", (string text) => { isCalled = true; }); + engine.ExecuteExpression("print('text')"); + Assert.IsTrue(isCalled); + } } \ No newline at end of file diff --git a/src/Topaz/API/object/concurrent/ConcurrentJsObject.cs b/src/Topaz/API/object/concurrent/ConcurrentJsObject.cs index b25a3f2..27ac391 100644 --- a/src/Topaz/API/object/concurrent/ConcurrentJsObject.cs +++ b/src/Topaz/API/object/concurrent/ConcurrentJsObject.cs @@ -140,4 +140,15 @@ protected virtual bool IsPrototypePropertyInternal(string memberName) { return false; } + +#pragma warning disable IDE1006 // Naming Styles + public virtual string toString() + { + return "[object Object]"; + } + + public bool hasOwnProperty(object key) => Contains(key); + + public bool hasOwn(object key) => Contains(key); +#pragma warning restore IDE1006 // Naming Styles } diff --git a/src/Topaz/API/object/normal/JsObject.cs b/src/Topaz/API/object/normal/JsObject.cs index e959c7b..e31b867 100644 --- a/src/Topaz/API/object/normal/JsObject.cs +++ b/src/Topaz/API/object/normal/JsObject.cs @@ -12,7 +12,8 @@ public partial class JsObject : IJsObject, IDictionary readonly DictionarySlim dictionary = new(); - public object this[object key] { + public object this[object key] + { get { if (key == null) @@ -45,8 +46,10 @@ public ICollection Keys } } - public ICollection Values { - get { + public ICollection Values + { + get + { var list = new List(dictionary.Count); foreach (var entry in dictionary) { @@ -158,8 +161,14 @@ protected virtual bool IsPrototypePropertyInternal(string memberName) return false; } +#pragma warning disable IDE1006 // Naming Styles public virtual string toString() { return "[object Object]"; } + + public bool hasOwnProperty(object key) => Contains(key); + + public bool hasOwn(object key) => Contains(key); +#pragma warning restore IDE1006 // Naming Styles } diff --git a/src/Topaz/AstHandlers/Expressions/BinaryExpressionHandler.cs b/src/Topaz/AstHandlers/Expressions/BinaryExpressionHandler.cs index 5651090..18ae0a5 100644 --- a/src/Topaz/AstHandlers/Expressions/BinaryExpressionHandler.cs +++ b/src/Topaz/AstHandlers/Expressions/BinaryExpressionHandler.cs @@ -1,5 +1,6 @@ using Esprima.Ast; using System; +using System.Collections; using System.Threading; using Tenray.Topaz.Core; using Tenray.Topaz.ErrorHandling; @@ -221,6 +222,13 @@ private static object ExecuteBinaryOperatorString(BinaryOperator binaryOperator, ScriptExecutor scriptExecutor, BinaryOperator @operator, object left, object right) { + if (@operator == BinaryOperator.In) + { + if (right is IDictionary dic) + return dic.Contains(left); + Exceptions.ThrowCannotUseInOperatorToSearchForIn(left, right); + } + // Try to avoid slow dynamic operator execution by type casting if (left is double d1) { @@ -314,12 +322,7 @@ private static object ExecuteBinaryOperatorString(BinaryOperator binaryOperator, BinaryOperator.RightShift => allowNumeric ? Convert.ToInt32(left ?? 0) >> Convert.ToInt32(right ?? 0) : 0, BinaryOperator.UnsignedRightShift => allowNumeric ? Convert.ToInt32(left ?? 0) >> Convert.ToInt32(right ?? 0) : 0, BinaryOperator.InstanceOf => left?.GetType().FullName == right?.ToString(), - BinaryOperator.In => - JavascriptTypeUtility.HasObjectMethod(right, "ContainsKey") ? - right.ContainsKey(left) : - JavascriptTypeUtility.HasObjectMethod(right, "Contains") ? - right.Contains(left) : - Exceptions.ThrowCannotUseInOperatorToSearchForIn(left, right), + BinaryOperator.In => Exceptions.ThrowCannotUseInOperatorToSearchForIn(left, right), BinaryOperator.LogicalAnd => JavascriptTypeUtility.AndLogicalOperator(left, right), BinaryOperator.LogicalOr =>