Skip to content

Commit

Permalink
Don't match explicit indexer properties when finding accessor (#886)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed May 5, 2021
1 parent d47f552 commit a8d89b2
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 33 deletions.
67 changes: 41 additions & 26 deletions Jint.Tests/Runtime/InteropTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private void RunTest(string source)
{
_engine.Execute(source);
}

public class Foo
{
public static Bar GetBar() => new Bar();
Expand Down Expand Up @@ -99,7 +99,7 @@ public void EngineShouldRoundtripParsedJSONBackToStringCorrectly()
const string json = "{\"foo\":5,\"bar\":\"A string\"}";
var parsed = engine.Execute($"JSON.parse('{json}')").GetCompletionValue().ToObject();
engine.SetValue(nameof(parsed), parsed);

var result = engine.Execute($"JSON.stringify({nameof(parsed)})").GetCompletionValue().AsString();
Assert.Equal(json, result);
}
Expand All @@ -117,7 +117,7 @@ public void PrimitiveTypesCanBeSet()
assert(z === 'foo');
");
}

[Fact]
public void TypePropertyAccess()
{
Expand All @@ -128,7 +128,7 @@ public void TypePropertyAccess()
.Execute("userclass.TypeProperty.Name;")
.GetCompletionValue()
.AsString();

Assert.Equal("Person", result);
}

Expand Down Expand Up @@ -807,7 +807,7 @@ IEnumerator IEnumerable.GetEnumerator()

public Person this[int index] => _data[index];
}

[Fact]
public void CanAddArrayPrototypeForArrayLikeClrObjects()
{
Expand All @@ -829,7 +829,7 @@ public void CanAddArrayPrototypeForArrayLikeClrObjects()
Age = 12,
Name = "John"
};

dynamic obj = new
{
values = new ReadOnlyList(person)
Expand All @@ -840,7 +840,7 @@ public void CanAddArrayPrototypeForArrayLikeClrObjects()
var name = e.Execute("o.values.filter(x => x.age == 12)[0].name").GetCompletionValue().ToString();
Assert.Equal("John", name);
}

[Fact]
public void CanAccessExpandoObject()
{
Expand Down Expand Up @@ -2144,7 +2144,7 @@ public void ShouldBeAbleToPlusAssignStringProperty()
[Fact]
public void ShouldNotResolveToPrimitiveSymbol()
{
var engine = new Engine(options =>
var engine = new Engine(options =>
options.AllowClr(typeof(FloatIndexer).GetTypeInfo().Assembly));
var c = engine.Execute(@"
var domain = importNamespace('Jint.Tests.Runtime.Domain');
Expand Down Expand Up @@ -2205,9 +2205,9 @@ public void ShouldSupportSpreadForDictionary()
.ToObject();

Assert.Equal("S1", result["supplier"]);
Assert.Equal("42", result["number"]);
Assert.Equal("42", result["number"]);
}

[Fact]
public void ShouldSupportSpreadForDictionary2()
{
Expand All @@ -2222,10 +2222,10 @@ public void ShouldSupportSpreadForDictionary2()
.Execute("function getValue() { return {supplier: 'S1', ...state.invoice}; }")
.Invoke("getValue")
.ToObject();

Assert.Equal("S1", result["supplier"]);
Assert.Equal("42", result["number"]);
}
Assert.Equal("42", result["number"]);
}

[Fact]
public void ShouldSupportSpreadForObject()
Expand All @@ -2244,8 +2244,8 @@ public void ShouldSupportSpreadForObject()
.ToObject();

Assert.Equal("S1", result["supplier"]);
Assert.Equal("Mike", result["Name"]);
Assert.Equal(20d, result["Age"]);
Assert.Equal("Mike", result["Name"]);
Assert.Equal(20d, result["Age"]);
}

[Fact]
Expand Down Expand Up @@ -2321,7 +2321,7 @@ public void ShouldOverrideMembers()
return null;
}));

engine.SetValue("m", new HiddenMembers());

Assert.Equal("Orange", engine.Execute("m.Member1").GetCompletionValue().ToString());
Expand Down Expand Up @@ -2362,30 +2362,30 @@ public void AccessingJArrayViaIntegerIndexShouldWork()
Assert.True(_engine.Execute("return o[0] == 'item1'").GetCompletionValue().AsBoolean());
Assert.True(_engine.Execute("return o[1] == 'item2'").GetCompletionValue().AsBoolean());
}

[Fact]
public void DictionaryLikeShouldCheckIndexerAndFallBackToProperty()
{
const string json = @"{ ""Type"": ""Cat"" }";
var jObjectWithTypeProperty = JObject.Parse(json);

_engine.SetValue("o", jObjectWithTypeProperty);

var typeResult = _engine.Execute("o.Type").GetCompletionValue();

// JToken requires conversion
Assert.Equal("Cat", TypeConverter.ToString(typeResult));

// weak equality does conversions from native types
Assert.True(_engine.Execute("o.Type == 'Cat'").GetCompletionValue().AsBoolean());
}
}

[Fact]
public void IndexingBsonProperties()
{
const string jsonAnimals = @" { ""Animals"": [ { ""Id"": 1, ""Type"": ""Cat"" } ] }";
var bsonAnimals = BsonDocument.Parse(jsonAnimals);

_engine.SetValue("animals", bsonAnimals["Animals"]);

// weak equality does conversions from native types
Expand Down Expand Up @@ -2415,10 +2415,25 @@ public void CanAccessDynamicObject()
Assert.False(engine.Execute("test.ContainsKey('c')").GetCompletionValue().AsBoolean());
}

[Fact]
public void CanAccessMemberNamedItemThroughExpando()
{
var parent = (IDictionary<string, object>) new ExpandoObject();
var child = (IDictionary<string, object>) new ExpandoObject();
var values = (IDictionary<string, object>) new ExpandoObject();

parent["child"] = child;
child["item"] = values;
values["title"] = "abc";

_engine.SetValue("parent", parent);
Assert.Equal("abc", _engine.Execute("parent.child.item.title").GetCompletionValue());
}

private class DynamicClass : DynamicObject
{
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = binder.Name;
Expand All @@ -2428,7 +2443,7 @@ public override bool TryGetMember(GetMemberBinder binder, out object result)
}
return true;
}

public override bool TrySetMember(SetMemberBinder binder, object value)
{
_properties[binder.Name] = value;
Expand All @@ -2441,7 +2456,7 @@ public bool ContainsKey(string key)
return _properties.ContainsKey(key);
}
}

[Fact]
public void IntegerEnumResolutionShouldWork()
{
Expand Down Expand Up @@ -2534,7 +2549,7 @@ public void ObjectWrapperOverridingEquality()
_engine.SetValue("a", new Person { Name = "Name" });
_engine.SetValue("b", new Person { Name = "Name" });
_engine.Execute("const arr = [ null, a, undefined ];");

Assert.Equal(1, _engine.Execute("arr.filter(x => x == b).length").GetCompletionValue().AsNumber());
Assert.Equal(1, _engine.Execute("arr.filter(x => x === b).length").GetCompletionValue().AsNumber());

Expand Down
20 changes: 13 additions & 7 deletions Jint/Runtime/Interop/ObjectWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ public override bool Set(JsValue property, JsValue value, JsValue receiver)
{
// can try utilize fast path
var accessor = GetAccessor(_engine, Target.GetType(), member);

// CanPut logic
if (!accessor.Writable || !_engine.Options._IsClrWriteAllowed)
{
return false;
}

accessor.SetValue(_engine, Target, value);
return true;
}
Expand Down Expand Up @@ -96,7 +96,7 @@ public override JsValue Get(JsValue property, JsValue receiver)
var index = (int) ((JsNumber) property)._value;
return (uint) index < list.Count ? FromObject(_engine, list[index]) : Undefined;
}

if (property.IsSymbol() && property != GlobalSymbolRegistry.Iterator)
{
// wrapped objects cannot have symbol properties
Expand All @@ -111,7 +111,7 @@ public override JsValue Get(JsValue property, JsValue receiver)
{
return result;
}

if (_properties is null || !_properties.ContainsKey(member))
{
// can try utilize fast path
Expand Down Expand Up @@ -158,7 +158,7 @@ private IEnumerable<JsValue> EnumerateOwnPropertyKeys(Types types)
}
else if (includeStrings && Target is IDictionary dictionary)
{
// we take values exposed as dictionary keys only
// we take values exposed as dictionary keys only
foreach (var key in dictionary.Keys)
{
if (_engine.ClrTypeConverter.TryConvert(key, typeof(string), CultureInfo.InvariantCulture, out var stringKey))
Expand Down Expand Up @@ -262,7 +262,7 @@ private static ReflectionAccessor GetAccessor(Engine engine, Type type, string m
}

accessor = accessorFactory?.Invoke() ?? ResolvePropertyDescriptorFactory(engine, type, member);

// racy, we don't care, worst case we'll catch up later
Interlocked.CompareExchange(ref Engine.ReflectionAccessors,
new Dictionary<ClrPropertyDescriptorFactoriesKey, ReflectionAccessor>(factories)
Expand All @@ -279,7 +279,7 @@ private static ReflectionAccessor ResolvePropertyDescriptorFactory(Engine engine

// we can always check indexer if there's one, and then fall back to properties if indexer returns null
IndexerAccessor.TryFindIndexer(engine, type, memberName, out var indexerAccessor, out var indexer);

// properties and fields cannot be numbers
if (!isNumber && TryFindStringPropertyAccessor(type, memberName, indexer, out var temp))
{
Expand All @@ -303,6 +303,12 @@ private static ReflectionAccessor ResolvePropertyDescriptorFactory(Engine engine
{
foreach (var iprop in iface.GetProperties())
{
if (iprop.Name == "Item" && iprop.GetIndexParameters().Length == 1)
{
// never take indexers, should use the actual indexer
continue;
}

if (EqualsIgnoreCasing(iprop.Name, memberName))
{
list ??= new List<PropertyInfo>();
Expand Down

0 comments on commit a8d89b2

Please sign in to comment.