فهرست منبع

Improve AOT usage scenarios with metadata (#1764)

* create AOT example project
* set REPL for AOT publish
* extract reflection related helpers from TypeConverter to InteropHelper
Marko Lahma 1 سال پیش
والد
کامیت
9fbdab389f
34فایلهای تغییر یافته به همراه595 افزوده شده و 406 حذف شده
  1. 17 0
      Jint.AotExample/Jint.AotExample.csproj
  2. 22 0
      Jint.AotExample/Program.cs
  3. 2 0
      Jint.Repl/Jint.Repl.csproj
  4. 5 4
      Jint.Repl/Program.cs
  5. 1 0
      Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj
  6. 6 5
      Jint.Tests/Runtime/EngineTests.cs
  7. 1 1
      Jint.Tests/Runtime/InteropTests.cs
  8. 6 0
      Jint.sln
  9. 26 5
      Jint/Engine.cs
  10. 3 3
      Jint/Extensions/ReflectionExtensions.cs
  11. 5 0
      Jint/Jint.csproj
  12. 5 2
      Jint/Native/ShadowRealm/ShadowRealm.cs
  13. 8 5
      Jint/Options.cs
  14. 37 0
      Jint/Runtime/InternalTypes.cs
  15. 7 7
      Jint/Runtime/Interop/ClrHelper.cs
  16. 26 6
      Jint/Runtime/Interop/DefaultTypeConverter.cs
  17. 4 1
      Jint/Runtime/Interop/DelegateWrapper.cs
  18. 9 2
      Jint/Runtime/Interop/ITypeConverter.cs
  19. 301 0
      Jint/Runtime/Interop/InteropHelper.cs
  20. 2 0
      Jint/Runtime/Interop/MethodDescriptor.cs
  21. 7 3
      Jint/Runtime/Interop/MethodInfoFunction.cs
  22. 15 9
      Jint/Runtime/Interop/NamespaceReference.cs
  23. 10 4
      Jint/Runtime/Interop/ObjectWrapper.cs
  24. 2 0
      Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs
  25. 3 0
      Jint/Runtime/Interop/Reflection/ExtensionMethodCache.cs
  26. 3 1
      Jint/Runtime/Interop/Reflection/IndexerAccessor.cs
  27. 6 1
      Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs
  28. 9 3
      Jint/Runtime/Interop/TypeDescriptor.cs
  29. 17 5
      Jint/Runtime/Interop/TypeReference.cs
  30. 9 4
      Jint/Runtime/Interop/TypeResolver.cs
  31. 1 1
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  32. 5 4
      Jint/Runtime/Modules/ModuleBuilder.cs
  33. 0 330
      Jint/Runtime/TypeConverter.cs
  34. 15 0
      Jint/Runtime/Types.cs

+ 17 - 0
Jint.AotExample/Jint.AotExample.csproj

@@ -0,0 +1,17 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <IsPackable>false</IsPackable>
+    <PublishAot>true</PublishAot>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Jint\Jint.csproj" />
+    <TrimmerRootAssembly Include="Jint" />
+  </ItemGroup>
+
+</Project>

+ 22 - 0
Jint.AotExample/Program.cs

@@ -0,0 +1,22 @@
+using Jint;
+
+var engine = new Engine(cfg => cfg
+    .AllowClr()
+);
+
+engine.SetValue("company", new Company());
+
+Console.WriteLine($"Company's name: {engine.Evaluate("company.name")}");
+Console.WriteLine($"Company's field: {engine.Evaluate("company.field")}");
+Console.WriteLine($"Company's indexer: {engine.Evaluate("company[42]")}");
+Console.WriteLine($"Company's greeting: {engine.Evaluate("company.sayHello('Mary')")}");
+
+
+public class Company
+{
+    public string Field = "public field value";
+    public string Name => "Jint";
+    public string SayHello(string name) => $"Hello {name}!";
+    public int this[int index] => index;
+}
+

+ 2 - 0
Jint.Repl/Jint.Repl.csproj

@@ -4,8 +4,10 @@
     <OutputType>Exe</OutputType>
     <IsPackable>false</IsPackable>
     <ImplicitUsings>enable</ImplicitUsings>
+    <PublishAot>true</PublishAot>
   </PropertyGroup>
   <ItemGroup>
     <ProjectReference Include="..\Jint\Jint.csproj" />
+    <TrimmerRootAssembly Include="Jint" />
   </ItemGroup>
 </Project>

+ 5 - 4
Jint.Repl/Program.cs

@@ -1,11 +1,13 @@
-using System.Diagnostics;
-using System.Reflection;
+using System.Reflection;
 using Esprima;
 using Jint;
 using Jint.Native;
 using Jint.Native.Json;
 using Jint.Runtime;
 
+#pragma warning disable IL2026
+#pragma warning disable IL2111
+
 var engine = new Engine(cfg => cfg
     .AllowClr()
 );
@@ -30,8 +32,7 @@ if (!string.IsNullOrEmpty(filename))
 }
 
 var assembly = Assembly.GetExecutingAssembly();
-var fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
-var version = fvi.FileVersion;
+var version = assembly.GetName().Version?.ToString();
 
 Console.WriteLine("Welcome to Jint ({0})", version);
 Console.WriteLine("Type 'exit' to leave, " +

+ 1 - 0
Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj

@@ -9,6 +9,7 @@
     <LangVersion>latest</LangVersion>
     <NoWarn>612</NoWarn>
     <ImplicitUsings>enable</ImplicitUsings>
+    <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
   </PropertyGroup>
 
 

+ 6 - 5
Jint.Tests/Runtime/EngineTests.cs

@@ -602,14 +602,15 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ForInStatement()
         {
-            RunTest(@"
+            var engine = new Engine();
+            var result = engine.Evaluate("""
                 var x, y, str = '';
                 for(var z in this) {
-                    str += z;
+                 str += z;
                 }
-
-                equal('xystrz', str);
-            ");
+                return str;
+         """);
+            Assert.Equal("xystrz", result);
         }
 
         [Fact]

+ 1 - 1
Jint.Tests/Runtime/InteropTests.cs

@@ -3431,7 +3431,7 @@ try {
             });
 
             var result = new List<string>();
-            void Debug(object? o)
+            void Debug(object o)
             {
                 result.Add($"{o?.GetType().Name ?? "null"}: {o ?? "null"}");
             }

+ 6 - 0
Jint.sln

@@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		Directory.Build.props = Directory.Build.props
 	EndProjectSection
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jint.AotExample", "Jint.AotExample\Jint.AotExample.csproj", "{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -59,6 +61,10 @@ Global
 		{70198CE9-7DFE-40CA-BBAC-1454C92C4109}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{70198CE9-7DFE-40CA-BBAC-1454C92C4109}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{70198CE9-7DFE-40CA-BBAC-1454C92C4109}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E07CC7D2-2ADF-4D71-A5B1-88209FFC29CD}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 26 - 5
Jint/Engine.cs

@@ -1,4 +1,5 @@
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
 using Esprima;
 using Esprima.Ast;
@@ -227,7 +228,7 @@ namespace Jint
         /// </summary>
         public Engine SetValue(string name, Delegate value)
         {
-            Realm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(this, value), true, false, true));
+            Realm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(this, value), PropertyFlag.NonEnumerable));
             return this;
         }
 
@@ -244,7 +245,7 @@ namespace Jint
         /// </summary>
         public Engine SetValue(string name, double value)
         {
-            return SetValue(name, JsNumber.Create(value));
+            return SetValue(name, (JsValue) JsNumber.Create(value));
         }
 
         /// <summary>
@@ -252,7 +253,7 @@ namespace Jint
         /// </summary>
         public Engine SetValue(string name, int value)
         {
-            return SetValue(name, JsNumber.Create(value));
+            return SetValue(name, (JsValue) JsNumber.Create(value));
         }
 
         /// <summary>
@@ -260,7 +261,7 @@ namespace Jint
         /// </summary>
         public Engine SetValue(string name, bool value)
         {
-            return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
+            return SetValue(name, (JsValue) (value ? JsBoolean.True : JsBoolean.False));
         }
 
         /// <summary>
@@ -273,7 +274,7 @@ namespace Jint
         }
 
         /// <summary>
-        /// Registers an object value as variable, creates an interop wrapper when needed..
+        /// Registers an object value as variable, creates an interop wrapper when needed.
         /// </summary>
         public Engine SetValue(string name, object? obj)
         {
@@ -284,6 +285,26 @@ namespace Jint
             return SetValue(name, value);
         }
 
+        /// <summary>
+        /// Registers an object value as variable, creates an interop wrapper when needed.
+        /// </summary>
+        public Engine SetValue(string name, [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type)
+        {
+#pragma warning disable IL2111
+            return SetValue(name, TypeReference.CreateTypeReference(this, type));
+#pragma warning restore IL2111
+        }
+
+        /// <summary>
+        /// Registers an object value as variable, creates an interop wrapper when needed.
+        /// </summary>
+        public Engine SetValue<[DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] T>(string name, T? obj)
+        {
+            return obj is Type t
+                ? SetValue(name, t)
+                : SetValue(name, JsValue.FromObject(this, obj));
+        }
+
         internal void LeaveExecutionContext()
         {
             _executionContexts.Pop();

+ 3 - 3
Jint/Extensions/ReflectionExtensions.cs

@@ -41,13 +41,13 @@ namespace Jint.Extensions
             };
         }
 
-        internal static IEnumerable<MethodInfo> GetExtensionMethods(this Type type)
+        internal static IEnumerable<MethodInfo> GetExtensionMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type)
         {
             return type.GetMethods(BindingFlags.Public | BindingFlags.Static)
                 .Where(static m => m.IsExtensionMethod());
         }
 
-        internal static IEnumerable<MethodInfo> GetOperatorOverloadMethods(this Type type)
+        internal static IEnumerable<MethodInfo> GetOperatorOverloadMethods([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type)
         {
             return type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
                 .Where(static m => m.IsSpecialName);
@@ -55,7 +55,7 @@ namespace Jint.Extensions
 
         private static bool IsExtensionMethod(this MethodBase methodInfo)
         {
-            return methodInfo.IsDefined(typeof(ExtensionAttribute), true);
+            return methodInfo.IsDefined(typeof(ExtensionAttribute), inherit: true);
         }
 
         public static bool IsNullable(this Type type)

+ 5 - 0
Jint/Jint.csproj

@@ -19,6 +19,11 @@
 
     <NoWarn>$(NoWarn);1591</NoWarn>
 
+    <IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
+
+    <PolySharpExcludeGeneratedTypes>System.Runtime.CompilerServices.IsExternalInit;System.Runtime.CompilerServices.RequiresLocationAttribute</PolySharpExcludeGeneratedTypes>
+    <PolySharpIncludeRuntimeSupportedAttributes>true</PolySharpIncludeRuntimeSupportedAttributes>
+
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(TargetFramework)' != 'net462' and '$(TargetFramework)' != 'netstandard2.0' ">

+ 5 - 2
Jint/Native/ShadowRealm/ShadowRealm.cs

@@ -1,4 +1,5 @@
-using Esprima;
+using System.Diagnostics.CodeAnalysis;
+using Esprima;
 using Esprima.Ast;
 using Esprima.Utils;
 using Jint.Native.Object;
@@ -55,9 +56,11 @@ public sealed class ShadowRealm : ObjectInstance
         _engine.RunAvailableContinuations();
         return value;
     }
+
+    [RequiresUnreferencedCode("User supplied delegate")]
     public ShadowRealm SetValue(string name, Delegate value)
     {
-        _shadowRealm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(_engine, value), true, false, true));
+        _shadowRealm.GlobalObject.FastSetProperty(name, new PropertyDescriptor(new DelegateWrapper(_engine, value), PropertyFlag.NonEnumerable));
         return this;
     }
 

+ 8 - 5
Jint/Options.cs

@@ -114,17 +114,20 @@ namespace Jint
             // add missing bits if needed
             if (Interop.Enabled)
             {
-                engine.Realm.GlobalObject.SetProperty("System",
-                    new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
+#pragma warning disable IL2026
+
+                engine.Realm.GlobalObject.SetProperty("System", new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
+
                 engine.Realm.GlobalObject.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunction(
                         engine,
                         "importNamespace",
                         (thisObj, arguments) =>
                             new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))),
                     PropertyFlag.AllForbidden));
-                engine.Realm.GlobalObject.SetProperty("clrHelper", new PropertyDescriptor(
-                    new ObjectWrapper(engine, new ClrHelper(Interop)),
-                    PropertyFlag.AllForbidden));
+
+                engine.Realm.GlobalObject.SetProperty("clrHelper", new PropertyDescriptor(new ObjectWrapper(engine, new ClrHelper(Interop)), PropertyFlag.AllForbidden));
+
+#pragma warning restore IL2026
             }
 
             if (Interop.ExtensionMethodTypes.Count > 0)

+ 37 - 0
Jint/Runtime/InternalTypes.cs

@@ -0,0 +1,37 @@
+namespace Jint.Runtime;
+
+[Flags]
+internal enum InternalTypes
+{
+    // should not be used, used for empty match
+    Empty = 0,
+
+    Undefined = 1,
+    Null = 2,
+
+    // primitive  types range start
+    Boolean = 4,
+    String = 8,
+    Number = 16,
+    Integer = 32,
+    Symbol = 64,
+    BigInt = 128,
+
+    // primitive  types range end
+    Object = 256,
+
+    PrivateName = 512,
+
+    // internal usage
+    ObjectEnvironmentRecord = 1024,
+    RequiresCloning = 2048,
+    Module = 4096,
+
+    // the object doesn't override important GetOwnProperty etc which change behavior
+    PlainObject = 8192,
+    // our native array
+    Array = 16384,
+
+    Primitive = Boolean | String | Number | Integer | BigInt | Symbol,
+    InternalFlags = ObjectEnvironmentRecord | RequiresCloning | PlainObject | Array | Module
+}

+ 7 - 7
Jint/Runtime/Interop/ClrHelper.cs

@@ -1,8 +1,10 @@
-namespace Jint.Runtime.Interop;
+using Jint.Native;
 
-using Jint.Native;
+namespace Jint.Runtime.Interop;
 
-public class ClrHelper
+#pragma warning disable IL2072
+
+internal sealed class ClrHelper
 {
     private readonly Options.InteropOptions _interopOptions;
 
@@ -74,10 +76,8 @@ public class ClrHelper
         {
             return TypeReference.CreateTypeReference(obj.Engine, t);
         }
-        else
-        {
-            ExceptionHelper.ThrowArgumentException("Must be an ObjectWrapper of Type", nameof(obj));
-        }
+
+        ExceptionHelper.ThrowArgumentException("Must be an ObjectWrapper of Type", nameof(obj));
         return JsValue.Undefined;
     }
 

+ 26 - 6
Jint/Runtime/Interop/DefaultTypeConverter.cs

@@ -10,6 +10,12 @@ using Jint.Native.Function;
 using Jint.Native.Object;
 using Jint.Runtime.Descriptors;
 
+#pragma warning disable IL2026
+#pragma warning disable IL2067
+#pragma warning disable IL2070
+#pragma warning disable IL2072
+#pragma warning disable IL3050
+
 namespace Jint.Runtime.Interop
 {
     public class DefaultTypeConverter : ITypeConverter
@@ -37,7 +43,10 @@ namespace Jint.Runtime.Interop
             _engine = engine;
         }
 
-        public virtual object? Convert(object? value, Type type, IFormatProvider formatProvider)
+        public virtual object? Convert(
+            object? value,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] Type type,
+            IFormatProvider formatProvider)
         {
             if (!TryConvert(value, type, formatProvider, propagateException: true, out var converted, out var problemMessage))
             {
@@ -46,19 +55,28 @@ namespace Jint.Runtime.Interop
             return converted;
         }
 
-        public virtual bool TryConvert(object? value, Type type, IFormatProvider formatProvider, [NotNullWhen(true)] out object? converted)
+        public virtual bool TryConvert(object? value,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] Type type,
+            IFormatProvider formatProvider,
+            [NotNullWhen(true)] out object? converted)
         {
             return TryConvert(value, type, formatProvider, propagateException: false, out converted, out _);
         }
 
-        private bool TryConvert(object? value, Type type, IFormatProvider formatProvider, bool propagateException, out object? converted, out string? problemMessage)
+        private bool TryConvert(
+            object? value,
+            [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type,
+            IFormatProvider formatProvider,
+            bool propagateException,
+            out object? converted,
+            out string? problemMessage)
         {
             converted = null;
             problemMessage = null;
 
             if (value is null)
             {
-                if (TypeConverter.TypeIsNullable(type))
+                if (InteropHelper.TypeIsNullable(type))
                 {
                     return true;
                 }
@@ -76,7 +94,7 @@ namespace Jint.Runtime.Interop
 
             if (type.IsGenericType)
             {
-                var result = TypeConverter.IsAssignableToGenericType(value.GetType(), type);
+                var result = InteropHelper.IsAssignableToGenericType(value.GetType(), type);
                 if (result.IsAssignable)
                 {
                     converted = value;
@@ -245,7 +263,9 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        private Delegate BuildDelegate(Type type, Func<JsValue, JsValue[], JsValue> function)
+        private Delegate BuildDelegate(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type,
+            Func<JsValue, JsValue[], JsValue> function)
         {
             var method = type.GetMethod("Invoke");
             var arguments = method!.GetParameters();

+ 4 - 1
Jint/Runtime/Interop/DelegateWrapper.cs

@@ -4,13 +4,16 @@ using Jint.Extensions;
 using Jint.Native;
 using Jint.Native.Function;
 
+#pragma warning disable IL2072
+#pragma warning disable IL3050
+
 namespace Jint.Runtime.Interop
 {
     /// <summary>
     /// Represents a FunctionInstance wrapper around a CLR method. This is used by user to pass
     /// custom methods to the engine.
     /// </summary>
-    public sealed class DelegateWrapper : Function
+    internal sealed class DelegateWrapper : Function
     {
         private static readonly JsString _name = new JsString("delegate");
         private readonly Delegate _d;

+ 9 - 2
Jint/Runtime/Interop/ITypeConverter.cs

@@ -10,10 +10,17 @@ public interface ITypeConverter
     /// <summary>
     /// Converts value to to type. Throws exception if cannot be done.
     /// </summary>
-    object? Convert(object? value, Type type, IFormatProvider formatProvider);
+    object? Convert(
+        object? value,
+        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] Type type,
+        IFormatProvider formatProvider);
 
     /// <summary>
     /// Converts value to to type. Returns false if cannot be done.
     /// </summary>
-    bool TryConvert(object? value, Type type, IFormatProvider formatProvider, [NotNullWhen(true)] out object? converted);
+    bool TryConvert(
+        object? value,
+        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] Type type,
+        IFormatProvider formatProvider,
+        [NotNullWhen(true)] out object? converted);
 }

+ 301 - 0
Jint/Runtime/Interop/InteropHelper.cs

@@ -0,0 +1,301 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Reflection;
+using Jint.Extensions;
+using Jint.Native;
+
+namespace Jint.Runtime.Interop;
+
+#pragma warning disable IL2072
+
+internal sealed class InteropHelper
+{
+    internal const DynamicallyAccessedMemberTypes DefaultDynamicallyAccessedMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors
+                                                                                          | DynamicallyAccessedMemberTypes.PublicProperties
+                                                                                          | DynamicallyAccessedMemberTypes.PublicMethods
+                                                                                          | DynamicallyAccessedMemberTypes.PublicFields
+                                                                                          | DynamicallyAccessedMemberTypes.PublicEvents;
+
+    internal readonly record struct AssignableResult(int Score, Type MatchingGivenType)
+    {
+        public bool IsAssignable => Score >= 0;
+    }
+
+    /// <summary>
+    /// resources:
+    /// https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-examine-and-instantiate-generic-types-with-reflection
+    /// https://stackoverflow.com/questions/74616/how-to-detect-if-type-is-another-generic-type/1075059#1075059
+    /// https://docs.microsoft.com/en-us/dotnet/api/system.type.isconstructedgenerictype?view=net-6.0
+    /// This can be improved upon - specifically as mentioned in the above MS document:
+    /// GetGenericParameterConstraints()
+    /// and array handling - i.e.
+    /// GetElementType()
+    /// </summary>
+    internal static AssignableResult IsAssignableToGenericType(
+        [DynamicallyAccessedMembers(DefaultDynamicallyAccessedMemberTypes | DynamicallyAccessedMemberTypes.Interfaces)]
+        Type givenType,
+        [DynamicallyAccessedMembers(DefaultDynamicallyAccessedMemberTypes | DynamicallyAccessedMemberTypes.Interfaces)]
+        Type genericType)
+    {
+        if (givenType is null)
+        {
+            return new AssignableResult(-1, typeof(void));
+        }
+
+        if (!genericType.IsConstructedGenericType)
+        {
+            // as mentioned here:
+            // https://docs.microsoft.com/en-us/dotnet/api/system.type.isconstructedgenerictype?view=net-6.0
+            // this effectively means this generic type is open (i.e. not closed) - so any type is "possible" - without looking at the code in the method we don't know
+            // whether any operations are being applied that "don't work"
+            return new AssignableResult(2, givenType);
+        }
+
+        var interfaceTypes = givenType.GetInterfaces();
+        foreach (var it in interfaceTypes)
+        {
+            if (it.IsGenericType)
+            {
+                var givenTypeGenericDef = it.GetGenericTypeDefinition();
+                if (givenTypeGenericDef == genericType)
+                {
+                    return new AssignableResult(0, it);
+                }
+                else if (genericType.IsGenericType && (givenTypeGenericDef == genericType.GetGenericTypeDefinition()))
+                {
+                    return new AssignableResult(0, it);
+                }
+                // TPC: we could also add a loop to recurse and iterate thru the iterfaces of generic type - because of covariance/contravariance
+            }
+        }
+
+        if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
+        {
+            return new AssignableResult(0, givenType);
+        }
+
+        var baseType = givenType.BaseType;
+        if (baseType == null)
+        {
+            return new AssignableResult(-1, givenType);
+        }
+
+        return IsAssignableToGenericType(baseType, genericType);
+    }
+
+    /// <summary>
+    /// Determines how well parameter type matches target method's type.
+    /// </summary>
+    private static int CalculateMethodParameterScore(Engine engine, ParameterInfo parameter, JsValue parameterValue)
+    {
+        var paramType = parameter.ParameterType;
+        var objectValue = parameterValue.ToObject();
+        var objectValueType = objectValue?.GetType();
+
+        if (objectValueType == paramType)
+        {
+            return 0;
+        }
+
+        if (objectValue is null)
+        {
+            if (!parameter.IsOptional && !TypeIsNullable(paramType))
+            {
+                // this is bad
+                return -1;
+            }
+
+            return 0;
+        }
+
+        if (paramType == typeof(JsValue))
+        {
+            // JsValue is convertible to. But it is still not a perfect match
+            return 1;
+        }
+
+        if (paramType == typeof(object))
+        {
+            // a catch-all, prefer others over it
+            return 5;
+        }
+
+        if (paramType == typeof(int) && parameterValue.IsInteger())
+        {
+            return 0;
+        }
+
+        if (paramType == typeof(float) && objectValueType == typeof(double))
+        {
+            return parameterValue.IsInteger() ? 1 : 2;
+        }
+
+        if (paramType.IsEnum &&
+            parameterValue is JsNumber jsNumber
+            && jsNumber.IsInteger()
+            && paramType.GetEnumUnderlyingType() == typeof(int)
+            && Enum.IsDefined(paramType, jsNumber.AsInteger()))
+        {
+            // we can do conversion from int value to enum
+            return 0;
+        }
+
+        if (paramType.IsAssignableFrom(objectValueType))
+        {
+            // is-a-relation
+            return 1;
+        }
+
+        if (parameterValue.IsArray() && paramType.IsArray)
+        {
+            // we have potential, TODO if we'd know JS array's internal type we could have exact match
+            return 2;
+        }
+
+        // not sure the best point to start generic type tests
+        if (paramType.IsGenericParameter)
+        {
+            var genericTypeAssignmentScore = IsAssignableToGenericType(objectValueType!, paramType);
+            if (genericTypeAssignmentScore.Score != -1)
+            {
+                return genericTypeAssignmentScore.Score;
+            }
+        }
+
+        if (CanChangeType(objectValue, paramType))
+        {
+            // forcing conversion isn't ideal, but works, especially for int -> double for example
+            return 3;
+        }
+
+        foreach (var m in objectValueType!.GetOperatorOverloadMethods())
+        {
+            if (paramType.IsAssignableFrom(m.ReturnType) && m.Name is "op_Implicit" or "op_Explicit")
+            {
+                // implicit/explicit operator conversion is OK, but not ideal
+                return 3;
+            }
+        }
+
+        if (ReflectionExtensions.TryConvertViaTypeCoercion(paramType, engine.Options.Interop.ValueCoercion, parameterValue, out _))
+        {
+            // gray JS zone where we start to do odd things
+            return 10;
+        }
+
+        // will rarely succeed
+        return 100;
+    }
+
+    /// <summary>
+    /// Method's match score tells how far away it's from ideal candidate. 0 = ideal, bigger the the number,
+    /// the farther away the candidate is from ideal match. Negative signals impossible match.
+    /// </summary>
+    private static int CalculateMethodScore(Engine engine, MethodDescriptor method, JsValue[] arguments)
+    {
+        if (method.Parameters.Length == 0 && arguments.Length == 0)
+        {
+            // perfect
+            return 0;
+        }
+
+        var score = 0;
+        for (var i = 0; i < arguments.Length; i++)
+        {
+            var jsValue = arguments[i];
+
+            var parameterScore = CalculateMethodParameterScore(engine, method.Parameters[i], jsValue);
+            if (parameterScore < 0)
+            {
+                return parameterScore;
+            }
+
+            score += parameterScore;
+        }
+
+        return score;
+    }
+
+    private static bool CanChangeType(object value, Type targetType)
+    {
+        if (value is null && !targetType.IsValueType)
+        {
+            return true;
+        }
+
+        if (value is not IConvertible)
+        {
+            return false;
+        }
+
+        try
+        {
+            Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture);
+            return true;
+        }
+        catch
+        {
+            // nope
+            return false;
+        }
+    }
+
+    internal static bool TypeIsNullable(Type type)
+    {
+        return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
+    }
+
+
+    internal readonly record struct MethodMatch(MethodDescriptor Method, JsValue[] Arguments, int Score = 0) : IComparable<MethodMatch>
+    {
+        public int CompareTo(MethodMatch other) => Score.CompareTo(other.Score);
+    }
+
+    internal static IEnumerable<MethodMatch> FindBestMatch(
+        Engine engine,
+        MethodDescriptor[] methods,
+        Func<MethodDescriptor, JsValue[]> argumentProvider)
+    {
+        List<MethodMatch>? matchingByParameterCount = null;
+        foreach (var method in methods)
+        {
+            var parameterInfos = method.Parameters;
+            var arguments = argumentProvider(method);
+            if (arguments.Length <= parameterInfos.Length
+                && arguments.Length >= parameterInfos.Length - method.ParameterDefaultValuesCount)
+            {
+                var score = CalculateMethodScore(engine, method, arguments);
+                if (score == 0)
+                {
+                    // perfect match
+                    yield return new MethodMatch(method, arguments);
+                    yield break;
+                }
+
+                if (score < 0)
+                {
+                    // discard
+                    continue;
+                }
+
+                matchingByParameterCount ??= new List<MethodMatch>();
+                matchingByParameterCount.Add(new MethodMatch(method, arguments, score));
+            }
+        }
+
+        if (matchingByParameterCount == null)
+        {
+            yield break;
+        }
+
+        if (matchingByParameterCount.Count > 1)
+        {
+            matchingByParameterCount.Sort();
+        }
+
+        foreach (var match in matchingByParameterCount)
+        {
+            yield return match;
+        }
+    }
+}

+ 2 - 0
Jint/Runtime/Interop/MethodDescriptor.cs

@@ -3,6 +3,8 @@ using System.Reflection;
 using System.Runtime.CompilerServices;
 using Jint.Extensions;
 
+#pragma warning disable IL2072
+
 namespace Jint.Runtime.Interop
 {
     internal sealed class MethodDescriptor

+ 7 - 3
Jint/Runtime/Interop/MethodInfoFunction.cs

@@ -5,6 +5,10 @@ using Jint.Extensions;
 using Jint.Native;
 using Jint.Native.Function;
 
+#pragma warning disable IL2067
+#pragma warning disable IL2072
+#pragma warning disable IL3050
+
 namespace Jint.Runtime.Interop
 {
     internal sealed class MethodInfoFunction : Function
@@ -39,7 +43,7 @@ namespace Jint.Runtime.Interop
                 return false;
             }
 
-            var result = TypeConverter.IsAssignableToGenericType(argObj.GetType(), parameterType);
+            var result = InteropHelper.IsAssignableToGenericType(argObj.GetType(), parameterType);
             if (result.Score < 0)
             {
                 return false;
@@ -59,7 +63,7 @@ namespace Jint.Runtime.Interop
                 return;
             }
 
-            var result = TypeConverter.IsAssignableToGenericType(argObj.GetType(), parameterType);
+            var result = InteropHelper.IsAssignableToGenericType(argObj.GetType(), parameterType);
             if (result.Score < 0)
             {
                 return;
@@ -161,7 +165,7 @@ namespace Jint.Runtime.Interop
             var converter = Engine.TypeConverter;
             var thisObj = thisObject.ToObject() ?? _target;
             object?[]? parameters = null;
-            foreach (var (method, arguments, _) in TypeConverter.FindBestMatch(_engine, _methods, ArgumentProvider))
+            foreach (var (method, arguments, _) in InteropHelper.FindBestMatch(_engine, _methods, ArgumentProvider))
             {
                 var methodParameters = method.Parameters;
                 if (parameters == null || parameters.Length != methodParameters.Length)

+ 15 - 9
Jint/Runtime/Interop/NamespaceReference.cs

@@ -1,9 +1,12 @@
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native.Object;
 using Jint.Runtime.Descriptors;
 
+#pragma warning disable IL3050
+
 namespace Jint.Runtime.Interop
 {
     /// <summary>
@@ -12,6 +15,7 @@ namespace Jint.Runtime.Interop
     /// a new <see cref="NamespaceReference"/> as it assumes that the property is a deeper
     /// level of the current namespace
     /// </summary>
+    [RequiresUnreferencedCode("Dynamic loading")]
     public class NamespaceReference : ObjectInstance, ICallable
     {
         private readonly string _path;
@@ -76,6 +80,7 @@ namespace Jint.Runtime.Interop
             return GetPath(newPath);
         }
 
+        [RequiresUnreferencedCode("Dynamic loading")]
         public JsValue GetPath(string path)
         {
             if (_engine.TypeCache.TryGetValue(path, out var type))
@@ -107,7 +112,7 @@ namespace Jint.Runtime.Interop
             }
 
             // search in lookup assemblies
-            var comparedPath = path.Replace("+", ".");
+            var comparedPath = path.Replace('+', '.');
             foreach (var assembly in _engine.Options.Interop.AllowedAssemblies)
             {
                 type = assembly.GetType(path);
@@ -124,7 +129,7 @@ namespace Jint.Runtime.Interop
                 {
                     foreach (Type nType in GetAllNestedTypes(type))
                     {
-                        if (nType.FullName != null && nType.FullName.Replace("+", ".").Equals(comparedPath, StringComparison.Ordinal))
+                        if (nType.FullName != null && nType.FullName.Replace('+', '.').Equals(comparedPath, StringComparison.Ordinal))
                         {
                             _engine.TypeCache.Add(comparedPath, nType);
                             return TypeReference.CreateTypeReference(_engine, nType);
@@ -153,13 +158,13 @@ namespace Jint.Runtime.Interop
         /// <param name="typeName"> Name of the type. </param>
         ///
         /// <returns>   The type. </returns>
+        [RequiresUnreferencedCode("Assembly type loading")]
         private static Type? GetType(Assembly assembly, string typeName)
         {
-            var compared = typeName.Replace("+", ".");
-            Type[] types = assembly.GetTypes();
-            foreach (Type t in types)
+            var compared = typeName.Replace('+', '.');
+            foreach (Type t in assembly.GetTypes())
             {
-                if (string.Equals(t.FullName?.Replace("+", "."), compared, StringComparison.Ordinal))
+                if (string.Equals(t.FullName?.Replace('+', '.'), compared, StringComparison.Ordinal))
                 {
                     return t;
                 }
@@ -175,10 +180,11 @@ namespace Jint.Runtime.Interop
             return types.ToArray();
         }
 
-        private static void AddNestedTypesRecursively(List<Type> types, Type type)
+        private static void AddNestedTypesRecursively(
+            List<Type> types,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes)] Type type)
         {
-            Type[] nestedTypes = type.GetNestedTypes(BindingFlags.Public);
-            foreach (Type nestedType in nestedTypes)
+            foreach (var nestedType in type.GetNestedTypes(BindingFlags.Public))
             {
                 types.Add(nestedType);
                 AddNestedTypesRecursively(types, nestedType);

+ 10 - 4
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -8,6 +8,10 @@ using Jint.Native.Symbol;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop.Reflection;
 
+#pragma warning disable IL2067
+#pragma warning disable IL2072
+#pragma warning disable IL2075
+
 namespace Jint.Runtime.Interop
 {
     /// <summary>
@@ -17,7 +21,10 @@ namespace Jint.Runtime.Interop
     {
         private readonly TypeDescriptor _typeDescriptor;
 
-        public ObjectWrapper(Engine engine, object obj, Type? type = null)
+        public ObjectWrapper(
+            Engine engine,
+            object obj,
+            Type? type = null)
             : base(engine)
         {
             Target = obj;
@@ -168,8 +175,7 @@ namespace Jint.Runtime.Interop
             else if (includeStrings)
             {
                 // we take public properties and fields
-                var type = ClrType;
-                foreach (var p in type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
+                foreach (var p in ClrType.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
                 {
                     var indexParameters = p.GetIndexParameters();
                     if (indexParameters.Length == 0)
@@ -179,7 +185,7 @@ namespace Jint.Runtime.Interop
                     }
                 }
 
-                foreach (var f in type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
+                foreach (var f in ClrType.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
                 {
                     var jsString = JsString.Create(f.Name);
                     yield return jsString;

+ 2 - 0
Jint/Runtime/Interop/Reflection/DynamicObjectAccessor.cs

@@ -2,6 +2,8 @@ using System.Dynamic;
 using System.Reflection;
 using Jint.Native;
 
+#pragma warning disable IL2092
+
 namespace Jint.Runtime.Interop.Reflection
 {
     internal sealed class DynamicObjectAccessor : ReflectionAccessor

+ 3 - 0
Jint/Runtime/Interop/Reflection/ExtensionMethodCache.cs

@@ -4,6 +4,9 @@ using System.Reflection;
 using System.Threading;
 using Jint.Extensions;
 
+#pragma warning disable IL2067
+#pragma warning disable IL2070
+
 namespace Jint.Runtime.Interop.Reflection
 {
     /// <summary>

+ 3 - 1
Jint/Runtime/Interop/Reflection/IndexerAccessor.cs

@@ -6,6 +6,8 @@ using Jint.Native;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 
+#pragma warning disable IL2067
+
 namespace Jint.Runtime.Interop.Reflection
 {
     internal sealed class IndexerAccessor : ReflectionAccessor
@@ -32,7 +34,7 @@ namespace Jint.Runtime.Interop.Reflection
 
         internal static bool TryFindIndexer(
             Engine engine,
-            Type targetType,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties)] Type targetType,
             string propertyName,
             [NotNullWhen(true)] out IndexerAccessor? indexerAccessor,
             [NotNullWhen(true)] out PropertyInfo? indexer)

+ 6 - 1
Jint/Runtime/Interop/Reflection/ReflectionAccessor.cs

@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Reflection;
 using Jint.Extensions;
@@ -5,6 +6,10 @@ 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
 {
     /// <summary>
@@ -110,7 +115,7 @@ namespace Jint.Runtime.Interop.Reflection
             }
         }
 
-        protected virtual object? ConvertValueToSet(Engine engine, object value)
+        protected virtual object? ConvertValueToSet(Engine engine, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] object value)
         {
             return engine.TypeConverter.Convert(value, _memberType, CultureInfo.InvariantCulture);
         }

+ 9 - 3
Jint/Runtime/Interop/TypeDescriptor.cs

@@ -3,6 +3,10 @@ using System.Collections.Concurrent;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 
+#pragma warning disable IL2067
+#pragma warning disable IL2075
+#pragma warning disable IL2077
+
 namespace Jint.Runtime.Interop
 {
     internal sealed class TypeDescriptor
@@ -16,7 +20,8 @@ namespace Jint.Runtime.Interop
         private readonly PropertyInfo? _keysAccessor;
         private readonly Type? _valueType;
 
-        private TypeDescriptor(Type type)
+        private TypeDescriptor(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.Interfaces)] Type type)
         {
             // check if object has any generic dictionary signature that accepts string as key
             foreach (var i in type.GetInterfaces())
@@ -56,12 +61,13 @@ namespace Jint.Runtime.Interop
 
         public bool Iterable => IsArrayLike || IsDictionary || IsEnumerable;
 
-        public static TypeDescriptor Get(Type type)
+        public static TypeDescriptor Get(
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.Interfaces)] Type type)
         {
             return _cache.GetOrAdd(type, t => new TypeDescriptor(t));
         }
 
-        private static bool DetermineIfObjectIsArrayLikeClrCollection(Type type)
+        private static bool DetermineIfObjectIsArrayLikeClrCollection([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type)
         {
             if (typeof(ICollection).IsAssignableFrom(type))
             {

+ 17 - 5
Jint/Runtime/Interop/TypeReference.cs

@@ -1,4 +1,5 @@
 using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using Jint.Collections;
 using Jint.Native;
@@ -7,6 +8,8 @@ using Jint.Native.Symbol;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop.Reflection;
 
+#pragma warning disable IL2072
+
 namespace Jint.Runtime.Interop
 {
     public sealed class TypeReference : Constructor, IObjectWrapper
@@ -17,7 +20,9 @@ namespace Jint.Runtime.Interop
 
         private readonly record struct MemberAccessorKey(Type Type, string PropertyName);
 
-        private TypeReference(Engine engine, Type type)
+        private TypeReference(
+            Engine engine,
+            [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type)
             : base(engine, engine.Realm, _name)
         {
             ReferenceType = type;
@@ -33,12 +38,15 @@ namespace Jint.Runtime.Interop
 
         public Type ReferenceType { get; }
 
-        public static TypeReference CreateTypeReference<T>(Engine engine)
+        public static TypeReference CreateTypeReference<
+            [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] T>(Engine engine)
         {
             return CreateTypeReference(engine, typeof(T));
         }
 
-        public static TypeReference CreateTypeReference(Engine engine, Type type)
+        public static TypeReference CreateTypeReference(
+            Engine engine,
+            [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type)
         {
             var reference = new TypeReference(engine, type);
             engine.RegisterTypeReference(reference);
@@ -172,7 +180,7 @@ namespace Jint.Runtime.Interop
                         return arguments;
                     });
 
-                    foreach (var (method, methodArguments, _) in TypeConverter.FindBestMatch(engine, constructors, argumentProvider))
+                    foreach (var (method, methodArguments, _) in InteropHelper.FindBestMatch(engine, constructors, argumentProvider))
                     {
                         var retVal = method.Call(engine, null, methodArguments);
                         result = TypeConverter.ToObject(realm, retVal);
@@ -290,7 +298,11 @@ namespace Jint.Runtime.Interop
             return accessor.CreatePropertyDescriptor(_engine, ReferenceType, enumerable: true);
         }
 
-        private static ReflectionAccessor ResolveMemberAccessor(Engine engine, Type type, string name)
+        private static ReflectionAccessor ResolveMemberAccessor(
+            Engine engine,
+            [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.Interfaces)]
+            Type type,
+            string name)
         {
             var typeResolver = engine.Options.Interop.TypeResolver;
 

+ 9 - 4
Jint/Runtime/Interop/TypeResolver.cs

@@ -5,6 +5,11 @@ using System.Reflection;
 using System.Threading;
 using Jint.Runtime.Interop.Reflection;
 
+#pragma warning disable IL2067
+#pragma warning disable IL2070
+#pragma warning disable IL2072
+#pragma warning disable IL2075
+
 namespace Jint.Runtime.Interop
 {
     /// <summary>
@@ -45,7 +50,7 @@ namespace Jint.Runtime.Interop
 
         internal ReflectionAccessor GetAccessor(
             Engine engine,
-            Type type,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.Interfaces)] Type type,
             string member,
             bool mustBeReadable,
             bool mustBeWritable,
@@ -73,7 +78,7 @@ namespace Jint.Runtime.Interop
 
         private ReflectionAccessor ResolvePropertyDescriptorFactory(
             Engine engine,
-            Type type,
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.Interfaces)] Type type,
             string memberName,
             bool mustBeReadable,
             bool mustBeWritable)
@@ -245,7 +250,7 @@ namespace Jint.Runtime.Interop
 
         internal bool TryFindMemberAccessor(
             Engine engine,
-            Type type,
+            [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes | DynamicallyAccessedMemberTypes.Interfaces)] Type type,
             string memberName,
             BindingFlags bindingFlags,
             PropertyInfo? indexerToTry,
@@ -256,7 +261,7 @@ namespace Jint.Runtime.Interop
             var memberNameComparer = MemberNameComparer;
             var typeResolverMemberNameCreator = MemberNameCreator;
 
-            PropertyInfo? GetProperty(Type t)
+            PropertyInfo? GetProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type t)
             {
                 foreach (var p in t.GetProperties(bindingFlags))
                 {

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -66,7 +66,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     var methods = leftMethods.Concat(rightMethods).Where(x => string.Equals(x.Name, clrName, StringComparison.Ordinal) && x.GetParameters().Length == 2);
                     var methodDescriptors = MethodDescriptor.Build(methods.ToArray());
 
-                    return TypeConverter.FindBestMatch(context.Engine, methodDescriptors, _ => arguments).FirstOrDefault().Method;
+                    return InteropHelper.FindBestMatch(context.Engine, methodDescriptors, _ => arguments).FirstOrDefault().Method;
                 });
 
                 if (method != null)

+ 5 - 4
Jint/Runtime/Modules/ModuleBuilder.cs

@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
 using Esprima;
 using Esprima.Ast;
 using Jint.Native;
@@ -61,25 +62,25 @@ public sealed class ModuleBuilder
         return this;
     }
 
-    public ModuleBuilder ExportType<T>()
+    public ModuleBuilder ExportType<[DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] T>()
     {
         ExportType<T>(typeof(T).Name);
         return this;
     }
 
-    public ModuleBuilder ExportType<T>(string name)
+    public ModuleBuilder ExportType<[DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] T>(string name)
     {
         _exports.Add(name, TypeReference.CreateTypeReference<T>(_engine));
         return this;
     }
 
-    public ModuleBuilder ExportType(Type type)
+    public ModuleBuilder ExportType([DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type)
     {
         ExportType(type.Name, type);
         return this;
     }
 
-    public ModuleBuilder ExportType(string name, Type type)
+    public ModuleBuilder ExportType(string name, [DynamicallyAccessedMembers(InteropHelper.DefaultDynamicallyAccessedMemberTypes)] Type type)
     {
         _exports.Add(name, TypeReference.CreateTypeReference(_engine, type));
         return this;

+ 0 - 330
Jint/Runtime/TypeConverter.cs

@@ -1,10 +1,8 @@
 using System.Globalization;
 using System.Numerics;
-using System.Reflection;
 using System.Runtime.CompilerServices;
 using Esprima;
 using Esprima.Ast;
-using Jint.Extensions;
 using Jint.Native;
 using Jint.Native.Number;
 using Jint.Native.Object;
@@ -14,56 +12,6 @@ using Jint.Runtime.Interop;
 
 namespace Jint.Runtime
 {
-    [Flags]
-    public enum Types
-    {
-        Empty = 0,
-        Undefined = 1,
-        Null = 2,
-        Boolean = 4,
-        String = 8,
-        Number = 16,
-        Symbol = 64,
-        BigInt = 128,
-        Object = 256
-    }
-
-    [Flags]
-    internal enum InternalTypes
-    {
-        // should not be used, used for empty match
-        Empty = 0,
-
-        Undefined = 1,
-        Null = 2,
-
-        // primitive  types range start
-        Boolean = 4,
-        String = 8,
-        Number = 16,
-        Integer = 32,
-        Symbol = 64,
-        BigInt = 128,
-
-        // primitive  types range end
-        Object = 256,
-
-        PrivateName = 512,
-
-        // internal usage
-        ObjectEnvironmentRecord = 1024,
-        RequiresCloning = 2048,
-        Module = 4096,
-
-        // the object doesn't override important GetOwnProperty etc which change behavior
-        PlainObject = 8192,
-        // our native array
-        Array = 16384,
-
-        Primitive = Boolean | String | Number | Integer | BigInt | Symbol,
-        InternalFlags = ObjectEnvironmentRecord | RequiresCloning | PlainObject | Array | Module
-    }
-
     public static class TypeConverter
     {
         // how many decimals to check when determining if double is actually an int
@@ -1066,283 +1014,5 @@ namespace Jint.Runtime
                 ExceptionHelper.ThrowTypeError(engine.Realm, "Cannot call method on " + o);
             }
         }
-
-        internal readonly record struct MethodMatch(MethodDescriptor Method, JsValue[] Arguments, int Score = 0) : IComparable<MethodMatch>
-        {
-            public int CompareTo(MethodMatch other) => Score.CompareTo(other.Score);
-        }
-
-        internal static IEnumerable<MethodMatch> FindBestMatch(
-            Engine engine,
-            MethodDescriptor[] methods,
-            Func<MethodDescriptor, JsValue[]> argumentProvider)
-        {
-            List<MethodMatch>? matchingByParameterCount = null;
-            foreach (var method in methods)
-            {
-                var parameterInfos = method.Parameters;
-                var arguments = argumentProvider(method);
-                if (arguments.Length <= parameterInfos.Length
-                    && arguments.Length >= parameterInfos.Length - method.ParameterDefaultValuesCount)
-                {
-                    var score = CalculateMethodScore(engine, method, arguments);
-                    if (score == 0)
-                    {
-                        // perfect match
-                        yield return new MethodMatch(method, arguments);
-                        yield break;
-                    }
-
-                    if (score < 0)
-                    {
-                        // discard
-                        continue;
-                    }
-
-                    matchingByParameterCount ??= new List<MethodMatch>();
-                    matchingByParameterCount.Add(new MethodMatch(method, arguments, score));
-                }
-            }
-
-            if (matchingByParameterCount == null)
-            {
-                yield break;
-            }
-
-            if (matchingByParameterCount.Count > 1)
-            {
-                matchingByParameterCount.Sort();
-            }
-
-            foreach (var match in matchingByParameterCount)
-            {
-                yield return match;
-            }
-        }
-
-        /// <summary>
-        /// Method's match score tells how far away it's from ideal candidate. 0 = ideal, bigger the the number,
-        /// the farther away the candidate is from ideal match. Negative signals impossible match.
-        /// </summary>
-        private static int CalculateMethodScore(Engine engine, MethodDescriptor method, JsValue[] arguments)
-        {
-            if (method.Parameters.Length == 0 && arguments.Length == 0)
-            {
-                // perfect
-                return 0;
-            }
-
-            var score = 0;
-            for (var i = 0; i < arguments.Length; i++)
-            {
-                var jsValue = arguments[i];
-
-                var parameterScore = CalculateMethodParameterScore(engine, method.Parameters[i], jsValue);
-                if (parameterScore < 0)
-                {
-                    return parameterScore;
-                }
-
-                score += parameterScore;
-            }
-
-            return score;
-        }
-
-        internal readonly record struct AssignableResult(int Score, Type MatchingGivenType)
-        {
-            public bool IsAssignable => Score >= 0;
-        }
-
-        /// <summary>
-        /// resources:
-        /// https://docs.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/how-to-examine-and-instantiate-generic-types-with-reflection
-        /// https://stackoverflow.com/questions/74616/how-to-detect-if-type-is-another-generic-type/1075059#1075059
-        /// https://docs.microsoft.com/en-us/dotnet/api/system.type.isconstructedgenerictype?view=net-6.0
-        /// This can be improved upon - specifically as mentioned in the above MS document:
-        /// GetGenericParameterConstraints()
-        /// and array handling - i.e.
-        /// GetElementType()
-        /// </summary>
-        internal static AssignableResult IsAssignableToGenericType(Type givenType, Type genericType)
-        {
-            if (givenType is null)
-            {
-                return new AssignableResult(-1, typeof(void));
-            }
-
-            if (!genericType.IsConstructedGenericType)
-            {
-                // as mentioned here:
-                // https://docs.microsoft.com/en-us/dotnet/api/system.type.isconstructedgenerictype?view=net-6.0
-                // this effectively means this generic type is open (i.e. not closed) - so any type is "possible" - without looking at the code in the method we don't know
-                // whether any operations are being applied that "don't work"
-                return new AssignableResult(2, givenType);
-            }
-
-            var interfaceTypes = givenType.GetInterfaces();
-            foreach (var it in interfaceTypes)
-            {
-                if (it.IsGenericType)
-                {
-                    var givenTypeGenericDef = it.GetGenericTypeDefinition();
-                    if (givenTypeGenericDef == genericType)
-                    {
-                        return new AssignableResult(0, it);
-                    }
-                    else if (genericType.IsGenericType && (givenTypeGenericDef == genericType.GetGenericTypeDefinition()))
-                    {
-                        return new AssignableResult(0, it);
-                    }
-                    // TPC: we could also add a loop to recurse and iterate thru the iterfaces of generic type - because of covariance/contravariance
-                }
-            }
-
-            if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
-            {
-                return new AssignableResult(0, givenType);
-            }
-
-            var baseType = givenType.BaseType;
-            if (baseType == null)
-            {
-                return new AssignableResult(-1, givenType);
-            }
-
-            return IsAssignableToGenericType(baseType, genericType);
-        }
-
-        /// <summary>
-        /// Determines how well parameter type matches target method's type.
-        /// </summary>
-        private static int CalculateMethodParameterScore(Engine engine, ParameterInfo parameter, JsValue parameterValue)
-        {
-            var paramType = parameter.ParameterType;
-            var objectValue = parameterValue.ToObject();
-            var objectValueType = objectValue?.GetType();
-
-            if (objectValueType == paramType)
-            {
-                return 0;
-            }
-
-            if (objectValue is null)
-            {
-                if (!parameter.IsOptional && !TypeIsNullable(paramType))
-                {
-                    // this is bad
-                    return -1;
-                }
-
-                return 0;
-            }
-
-            if (paramType == typeof(JsValue))
-            {
-                // JsValue is convertible to. But it is still not a perfect match
-                return 1;
-            }
-
-            if (paramType == typeof(object))
-            {
-                // a catch-all, prefer others over it
-                return 5;
-            }
-
-            if (paramType == typeof(int) && parameterValue.IsInteger())
-            {
-                return 0;
-            }
-
-            if (paramType == typeof(float) && objectValueType == typeof(double))
-            {
-                return parameterValue.IsInteger() ? 1 : 2;
-            }
-
-            if (paramType.IsEnum &&
-                parameterValue is JsNumber jsNumber
-                && jsNumber.IsInteger()
-                && paramType.GetEnumUnderlyingType() == typeof(int)
-                && Enum.IsDefined(paramType, jsNumber.AsInteger()))
-            {
-                // we can do conversion from int value to enum
-                return 0;
-            }
-
-            if (paramType.IsAssignableFrom(objectValueType))
-            {
-                // is-a-relation
-                return 1;
-            }
-
-            if (parameterValue.IsArray() && paramType.IsArray)
-            {
-                // we have potential, TODO if we'd know JS array's internal type we could have exact match
-                return 2;
-            }
-
-            // not sure the best point to start generic type tests
-            if (paramType.IsGenericParameter)
-            {
-                var genericTypeAssignmentScore = IsAssignableToGenericType(objectValueType!, paramType);
-                if (genericTypeAssignmentScore.Score != -1)
-                {
-                    return genericTypeAssignmentScore.Score;
-                }
-            }
-
-            if (CanChangeType(objectValue, paramType))
-            {
-                // forcing conversion isn't ideal, but works, especially for int -> double for example
-                return 3;
-            }
-
-            foreach (var m in objectValueType!.GetOperatorOverloadMethods())
-            {
-                if (paramType.IsAssignableFrom(m.ReturnType) && m.Name is "op_Implicit" or "op_Explicit")
-                {
-                    // implicit/explicit operator conversion is OK, but not ideal
-                    return 3;
-                }
-            }
-
-            if (ReflectionExtensions.TryConvertViaTypeCoercion(paramType, engine.Options.Interop.ValueCoercion, parameterValue, out _))
-            {
-                // gray JS zone where we start to do odd things
-                return 10;
-            }
-
-            // will rarely succeed
-            return 100;
-        }
-
-        private static bool CanChangeType(object value, Type targetType)
-        {
-            if (value is null && !targetType.IsValueType)
-            {
-                return true;
-            }
-
-            if (value is not IConvertible)
-            {
-                return false;
-            }
-
-            try
-            {
-                Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture);
-                return true;
-            }
-            catch
-            {
-                // nope
-                return false;
-            }
-        }
-
-        internal static bool TypeIsNullable(Type type)
-        {
-            return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
-        }
     }
 }

+ 15 - 0
Jint/Runtime/Types.cs

@@ -0,0 +1,15 @@
+namespace Jint.Runtime;
+
+[Flags]
+public enum Types
+{
+    Empty = 0,
+    Undefined = 1,
+    Null = 2,
+    Boolean = 4,
+    String = 8,
+    Number = 16,
+    Symbol = 64,
+    BigInt = 128,
+    Object = 256
+}