Skip to content

Commit

Permalink
make TypeReference use TypeResolver resolution logic
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Aug 29, 2021
1 parent 1c9228b commit faa9836
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 50 deletions.
26 changes: 26 additions & 0 deletions Jint.Tests/Runtime/InteropTests.MemberAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,31 @@ public void ShouldBeAbleToHideGetType()
hiddenGetTypeEngine.SetValue("m", new HiddenMembers());
Assert.True(hiddenGetTypeEngine.Evaluate("m.GetType").IsUndefined());
}

[Fact]
public void TypeReferenceShouldUseTypeResolverConfiguration()
{
var engine = new Engine(options =>
{
options.SetTypeResolver(new TypeResolver
{
MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute))
});
});
engine.SetValue("EchoService", TypeReference.CreateTypeReference(engine, typeof(EchoService)));
Assert.Equal("anyone there", engine.Evaluate("EchoService.Echo('anyone there')").AsString());
Assert.Equal("anyone there", engine.Evaluate("EchoService.echo('anyone there')").AsString());
Assert.True(engine.Evaluate("EchoService.ECHO").IsUndefined());

Assert.True(engine.Evaluate("EchoService.Hidden").IsUndefined());
}

private static class EchoService
{
public static string Echo(string message) => message;

[Obsolete]
public static string Hidden(string message) => message;
}
}
}
51 changes: 13 additions & 38 deletions Jint/Runtime/Interop/TypeReference.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using Jint.Collections;
using Jint.Native;
Expand All @@ -24,7 +23,7 @@ public sealed class TypeReference : FunctionInstance, IConstructor, IObjectWrapp
{
}

public Type ReferenceType { get; set; }
public Type ReferenceType { get; private set; }

public static TypeReference CreateTypeReference(Engine engine, Type type)
{
Expand Down Expand Up @@ -137,23 +136,25 @@ public override PropertyDescriptor GetOwnProperty(JsValue property)

private PropertyDescriptor CreatePropertyDescriptor(string name)
{
var accessor = _memberAccessors.GetOrAdd(
new Tuple<Type, string>(ReferenceType, name),
key => ResolveMemberAccessor(key.Item1, key.Item2)
);
var key = new Tuple<Type, string>(ReferenceType, name);
var accessor = _memberAccessors.GetOrAdd(key, x => ResolveMemberAccessor(x.Item1, x.Item2));
return accessor.CreatePropertyDescriptor(_engine, ReferenceType);
}

private static ReflectionAccessor ResolveMemberAccessor(Type type, string name)
private ReflectionAccessor ResolveMemberAccessor(Type type, string name)
{
var typeResolver = _engine.Options._TypeResolver;

if (type.IsEnum)
{
var memberNameComparer = typeResolver.MemberNameComparer;

var enumValues = Enum.GetValues(type);
var enumNames = Enum.GetNames(type);

for (var i = 0; i < enumValues.Length; i++)
{
if (enumNames.GetValue(i) as string == name)
if (memberNameComparer.Equals(enumNames.GetValue(i), name))
{
var value = enumValues.GetValue(i);
return new ConstantValueAccessor(JsNumber.Create(value));
Expand All @@ -163,36 +164,10 @@ private static ReflectionAccessor ResolveMemberAccessor(Type type, string name)
return ConstantValueAccessor.NullAccessor;
}

var propertyInfo = type.GetProperty(name, BindingFlags.Public | BindingFlags.Static);
if (propertyInfo != null)
{
return new PropertyAccessor(name, propertyInfo);
}

var fieldInfo = type.GetField(name, BindingFlags.Public | BindingFlags.Static);
if (fieldInfo != null)
{
return new FieldAccessor(fieldInfo, name);
}

List<MethodInfo> methods = null;
foreach (var mi in type.GetMethods(BindingFlags.Public | BindingFlags.Static))
{
if (mi.Name != name)
{
continue;
}

methods ??= new List<MethodInfo>();
methods.Add(mi);
}

if (methods == null || methods.Count == 0)
{
return ConstantValueAccessor.NullAccessor;
}

return new MethodAccessor(MethodDescriptor.Build(methods));
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Static;
return typeResolver.TryFindMemberAccessor(type, name, bindingFlags, indexerToTry: null, out var accessor)
? accessor
: ConstantValueAccessor.NullAccessor;
}

public object Target => ReferenceType;
Expand Down
25 changes: 13 additions & 12 deletions Jint/Runtime/Interop/TypeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
{
var isNumber = uint.TryParse(memberName, out _);

var typeResolver = engine.Options._TypeResolver;

// 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);

const BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public;

// properties and fields cannot be numbers
if (!isNumber && TryFindStringPropertyAccessor(type, memberName, indexer, out var temp))
if (!isNumber && TryFindMemberAccessor(type, memberName, bindingFlags, indexer, out var temp))
{
return temp;
}
Expand Down Expand Up @@ -167,15 +167,16 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,
return ConstantValueAccessor.NullAccessor;
}

private bool TryFindStringPropertyAccessor(
internal bool TryFindMemberAccessor(
Type type,
string memberName,
BindingFlags bindingFlags,
PropertyInfo indexerToTry,
out ReflectionAccessor wrapper)
out ReflectionAccessor accessor)
{
// look for a property, bit be wary of indexers, we don't want indexers which have name "Item" to take precedence
PropertyInfo property = null;
foreach (var p in type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
foreach (var p in type.GetProperties(bindingFlags))
{
if (!MemberFilter(p))
{
Expand All @@ -193,13 +194,13 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,

if (property != null)
{
wrapper = new PropertyAccessor(memberName, property, indexerToTry);
accessor = new PropertyAccessor(memberName, property, indexerToTry);
return true;
}

// look for a field
FieldInfo field = null;
foreach (var f in type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
foreach (var f in type.GetFields(bindingFlags))
{
if (!MemberFilter(f))
{
Expand All @@ -215,13 +216,13 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,

if (field != null)
{
wrapper = new FieldAccessor(field, memberName, indexerToTry);
accessor = new FieldAccessor(field, memberName, indexerToTry);
return true;
}

// if no properties were found then look for a method
List<MethodInfo> methods = null;
foreach (var m in type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
foreach (var m in type.GetMethods(bindingFlags))
{
if (!MemberFilter(m))
{
Expand All @@ -237,11 +238,11 @@ internal ReflectionAccessor GetAccessor(Engine engine, Type type, string member,

if (methods?.Count > 0)
{
wrapper = new MethodAccessor(MethodDescriptor.Build(methods));
accessor = new MethodAccessor(MethodDescriptor.Build(methods));
return true;
}

wrapper = default;
accessor = default;
return false;
}

Expand Down

0 comments on commit faa9836

Please sign in to comment.