using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using Jint.Extensions; using Jint.Native; using Jint.Runtime.Descriptors; using Jint.Runtime.Descriptors.Specialized; #pragma warning disable IL2098 #pragma warning disable IL2072 #pragma warning disable IL2077 namespace Jint.Runtime.Interop.Reflection; /// /// Strategy to read and write CLR object properties and fields. /// internal abstract class ReflectionAccessor { private readonly Type _memberType; private readonly PropertyInfo? _indexer; public Type MemberType => _memberType; protected ReflectionAccessor( Type memberType, PropertyInfo? indexer = null) { _memberType = memberType; _indexer = indexer; } public virtual bool Readable => true; public abstract bool Writable { get; } protected abstract object? DoGetValue(object target, string memberName); protected abstract void DoSetValue(object target, string memberName, object? value); public object? GetValue(Engine engine, object target, string memberName) { var constantValue = ConstantValue; if (constantValue is not null) { return constantValue; } // first check indexer so we don't confuse inherited properties etc var value = TryReadFromIndexer(target, memberName); if (value is null) { try { value = DoGetValue(target, memberName); } catch (TargetInvocationException tie) { ExceptionHelper.ThrowMeaningfulException(engine, tie); } } return value; } protected virtual JsValue? ConstantValue => null; private object? TryReadFromIndexer(object target, string memberName) { var getter = _indexer?.GetGetMethod(); if (getter is null) { return null; } try { object[] parameters = [memberName]; return getter.Invoke(target, parameters); } catch { return null; } } public void SetValue(Engine engine, object target, string memberName, JsValue value) { object? converted; if (_memberType == typeof(JsValue)) { converted = value; } else if (!ReflectionExtensions.TryConvertViaTypeCoercion(_memberType, engine.Options.Interop.ValueCoercion, value, out converted)) { // attempt to convert the JsValue to the target type converted = value.ToObject(); if (converted != null && converted.GetType() != _memberType) { converted = ConvertValueToSet(engine, converted); } } try { DoSetValue(target, memberName, converted); } catch (TargetInvocationException exception) { ExceptionHelper.ThrowMeaningfulException(engine, exception); } } protected virtual object? ConvertValueToSet(Engine engine, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] object value) { return engine.TypeConverter.Convert(value, _memberType, CultureInfo.InvariantCulture); } public virtual PropertyDescriptor CreatePropertyDescriptor(Engine engine, object target, string memberName, bool enumerable = true) { return new ReflectionDescriptor(engine, this, target, memberName, enumerable); } }