123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- using System.Collections;
- using System.Diagnostics.CodeAnalysis;
- using Jint.Native;
- using Jint.Runtime;
- using Jint.Runtime.Interop;
- using Jint.Tests.Runtime.Domain;
- namespace Jint.Tests.Runtime;
- public partial class InteropTests
- {
- [Fact]
- public void ShouldHideSpecificMembers()
- {
- var engine = new Engine(options => options.SetMemberAccessor((e, target, member) =>
- {
- if (target is HiddenMembers)
- {
- if (member == nameof(HiddenMembers.Member2) || member == nameof(HiddenMembers.Method2))
- {
- return JsValue.Undefined;
- }
- }
- return null;
- }));
- engine.SetValue("m", new HiddenMembers());
- Assert.Equal("Member1", engine.Evaluate("m.Member1").ToString());
- Assert.Equal("undefined", engine.Evaluate("m.Member2").ToString());
- Assert.Equal("Method1", engine.Evaluate("m.Method1()").ToString());
- // check the method itself, not its invokation as it would mean invoking "undefined"
- Assert.Equal("undefined", engine.Evaluate("m.Method2").ToString());
- }
- [Fact]
- public void ShouldOverrideMembers()
- {
- var engine = new Engine(options => options.SetMemberAccessor((e, target, member) =>
- {
- if (target is HiddenMembers && member == nameof(HiddenMembers.Member1))
- {
- return "Orange";
- }
- return null;
- }));
- engine.SetValue("m", new HiddenMembers());
- Assert.Equal("Orange", engine.Evaluate("m.Member1").ToString());
- }
- [Fact]
- public void ShouldBeAbleToFilterMembers()
- {
- var engine = new Engine(options => options
- .SetTypeResolver(new TypeResolver
- {
- MemberFilter = member => !Attribute.IsDefined(member, typeof(ObsoleteAttribute))
- })
- );
- engine.SetValue("m", new HiddenMembers());
- Assert.True(engine.Evaluate("m.Field1").IsUndefined());
- Assert.True(engine.Evaluate("m.Member1").IsUndefined());
- Assert.True(engine.Evaluate("m.Method1").IsUndefined());
- Assert.True(engine.Evaluate("m.Field2").IsString());
- Assert.True(engine.Evaluate("m.Member2").IsString());
- Assert.True(engine.Evaluate("m.Method2()").IsString());
- // we forbid GetType by default
- Assert.True(engine.Evaluate("m.GetType").IsUndefined());
- }
- [Fact]
- public void ShouldBeAbleToExposeGetType()
- {
- var engine = new Engine(options =>
- {
- options.Interop.AllowGetType = true;
- options.Interop.AllowSystemReflection = true;
- });
- engine.SetValue("m", new HiddenMembers());
- Assert.True(engine.Evaluate("m.GetType").IsCallable);
- // reflection could bypass some safeguards
- Assert.Equal("Method1", engine.Evaluate("m.GetType().GetMethod('Method1').Invoke(m, [])").AsString());
- }
- [Fact]
- public void ShouldBeAbleToVaryGetTypeConfigurationBetweenEngines()
- {
- static string TestAllowGetTypeOption(bool allowGetType)
- {
- var uri = new Uri("https://github.com/sebastienros/jint");
- const string Input = nameof(uri) + ".GetType();";
- using var engine = new Engine(options => options.Interop.AllowGetType = allowGetType);
- engine.SetValue(nameof(uri), JsValue.FromObject(engine, uri));
- var result = engine.Evaluate(Input).ToString();
- return result;
- }
- Assert.Equal("System.Uri", TestAllowGetTypeOption(allowGetType: true));
- var ex = Assert.Throws<JavaScriptException>(() => TestAllowGetTypeOption(allowGetType: false));
- Assert.Equal("Property 'GetType' of object is not a function", ex.Message);
- }
- [Fact]
- public void ShouldProtectFromReflectionServiceUsage()
- {
- var engine = new Engine();
- engine.SetValue("m", new HiddenMembers());
- // we can get a type reference if it's exposed via property, bypassing GetType
- var type = engine.Evaluate("m.Type");
- Assert.IsType<ObjectWrapper>(type);
- var ex = Assert.Throws<InvalidOperationException>(() => engine.Evaluate("m.Type.Module.GetType().Module.GetType('System.DateTime')"));
- Assert.Equal("Cannot access System.Reflection namespace, check Engine's interop options", ex.Message);
- }
- [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());
- }
- [Fact]
- public void CustomDictionaryPropertyAccessTests()
- {
- var engine = new Engine(options =>
- {
- options.AllowClr();
- });
- var dc = new CustomDictionary<float>();
- dc["a"] = 1;
- engine.SetValue("dc", dc);
- Assert.Equal(1, engine.Evaluate("dc.a"));
- Assert.Equal(1, engine.Evaluate("dc['a']"));
- Assert.NotEqual(JsValue.Undefined, engine.Evaluate("dc.Add"));
- Assert.NotEqual(0, engine.Evaluate("dc.Add"));
- Assert.Equal(JsValue.Undefined, engine.Evaluate("dc.b"));
- engine.Execute("dc.b = 5");
- engine.Execute("dc.Add('c', 10)");
- Assert.Equal(5, engine.Evaluate("dc.b"));
- Assert.Equal(10, engine.Evaluate("dc.c"));
- }
- [Fact]
- public void CanAccessBaseClassStaticFields()
- {
- var engine = new Engine(options =>
- {
- options.AllowClr();
- });
- engine.SetValue("B", TypeReference.CreateTypeReference(engine, typeof(InheritingFromClassWithStatics)));
- Assert.Equal(42, engine.Evaluate("B.a").AsNumber());
- Assert.Equal(24, engine.Evaluate("B.a = 24; B.a").AsNumber());
- }
- private class BaseClassWithStatics
- {
- #pragma warning disable CS0414 // Field is assigned but its value is never used
- public static int a = 42;
- #pragma warning restore CS0414 // Field is assigned but its value is never used
- }
- private class InheritingFromClassWithStatics : BaseClassWithStatics
- {
- }
- private static class EchoService
- {
- public static string Echo(string message) => message;
- [Obsolete]
- public static string Hidden(string message) => message;
- }
- private class CustomDictionary<TValue> : IDictionary<string, TValue>
- {
- readonly Dictionary<string, TValue> _dictionary = new Dictionary<string, TValue>();
- public TValue this[string key]
- {
- get
- {
- if (TryGetValue(key, out var val)) return val;
- // Normally, dictionary Item accessor throws an error when key does not exist
- // But this is a custom implementation
- return default;
- }
- set
- {
- _dictionary[key] = value;
- }
- }
- public ICollection<string> Keys => _dictionary.Keys;
- public ICollection<TValue> Values => _dictionary.Values;
- public int Count => _dictionary.Count;
- public bool IsReadOnly => false;
- public void Add(string key, TValue value) => _dictionary.Add(key, value);
- public void Add(KeyValuePair<string, TValue> item) => _dictionary.Add(item.Key, item.Value);
- public void Clear() => _dictionary.Clear();
- public bool Contains(KeyValuePair<string, TValue> item) => _dictionary.ContainsKey(item.Key);
- public bool ContainsKey(string key) => _dictionary.ContainsKey(key);
- public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex) => throw new NotImplementedException();
- public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator() => _dictionary.GetEnumerator();
- public bool Remove(string key) => _dictionary.Remove(key);
- public bool Remove(KeyValuePair<string, TValue> item) => _dictionary.Remove(item.Key);
- public bool TryGetValue(string key, [MaybeNullWhen(false)] out TValue value) => _dictionary.TryGetValue(key, out value);
- IEnumerator IEnumerable.GetEnumerator() => _dictionary.GetEnumerator();
- }
- public class ClassWithData
- {
- public int Age => 42;
- public DataType Data { get; set; }
- public class DataType
- {
- public string Value { get; set; }
- }
- }
- [Fact]
- public void NewTypedObjectFromUntypedInitializerShouldBeMapped()
- {
- var engine = new Engine();
- engine.SetValue("obj", new ClassWithData());
- engine.Execute("obj.Data = { Value: '123' };");
- var obj = engine.Evaluate("obj").ToObject() as ClassWithData;
- Assert.Equal("123", obj?.Data.Value);
- }
- [Fact]
- public void CanConfigureStrictAccess()
- {
- var engine = new Engine();
- engine.SetValue("obj", new ClassWithData());
- engine.Evaluate("obj.Age").AsNumber().Should().Be(42);
- engine.Evaluate("obj.AgeMissing").Should().Be(JsValue.Undefined);
- engine = new Engine(options =>
- {
- options.Interop.ThrowOnUnresolvedMember = true;
- });
- engine.SetValue("obj", new ClassWithData());
- engine.Evaluate("obj.Age").AsNumber().Should().Be(42);
- engine.Invoking(e => e.Evaluate("obj.AgeMissing")).Should().Throw<MissingMemberException>();
- }
- }
|