From f453474349bcb5757601da8a67e48914bd72e34a Mon Sep 17 00:00:00 2001 From: Scarlet Rose <70824102+scarletquasar@users.noreply.github.com> Date: Tue, 8 Aug 2023 00:13:48 -0300 Subject: [PATCH] Work in progress for Interoperability module The Interoperability module is a reworked version of the future deprecated BindingsManager with the objective of offering better, cached and reliable interoperability functions and models to create a better bridge between the JavaScript interface and the .NET CLR. It was made taking all the current features that can be used in the current CLR in consideration. Future support to manipulate and fetch generic types will also be added to Interoperability method. --- .vscode/settings.json | 3 + .../Library/Reflection/Interoperability.cs | 246 ++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 projects/native/MelonRuntime.Core/Library/Reflection/Interoperability.cs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..d8ad731f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "projects/native/MelonRuntime.sln" +} \ No newline at end of file diff --git a/projects/native/MelonRuntime.Core/Library/Reflection/Interoperability.cs b/projects/native/MelonRuntime.Core/Library/Reflection/Interoperability.cs new file mode 100644 index 00000000..b46b897a --- /dev/null +++ b/projects/native/MelonRuntime.Core/Library/Reflection/Interoperability.cs @@ -0,0 +1,246 @@ +using System.Reflection; + +//TODO: Work in progress class; Should not be used directly on the current modules. +namespace MelonRuntime.Core.Library.Reflection { + /// + /// Representation of an Assembly object in the interoperability context, + /// capable of open and use the internal namespaces of it. Can be created + /// from an external file. InteropAssembly + /// may be used only for raw contexts inside JavaScript to CLR interoperability + /// and is not useful as direct provider to get features from after the engine + /// startup operation. + /// + public class InteropAssembly + { + public string? FullName { get; private set; } + public string? FilePath { get; private set; } + + private Assembly? _assembly; + private Type[]? _types; + private bool _open; + + /// + /// Returns the target assembly object relative to the InteropAssembly + /// instance. + /// + public (Assembly?, NullReferenceException?) GetAssemblyObject() + { + NullReferenceException? error = null; + + if (_assembly == null) + { + error = new NullReferenceException("The assembly is not defined"); + } + + return (_assembly, error); + } + + /// + /// Loads an external assembly information plus the assembly itself + /// to an existing InteropAssembly instance. + /// + public async Task<(Assembly?, Exception?)> OpenFile(string path) + { + Assembly? result = null; + Exception? error = null; + + if (_open) + { + error = new InvalidOperationException("InteropAssembly was already opened."); + return (result, error); + } + + try + { + var file = await File.ReadAllBytesAsync(path); + result = Assembly.Load(file); + + _open = true; + _assembly = result; + _types = _assembly.GetTypes(); + FullName = result.FullName; + FilePath = path; + } + catch (Exception e) { + error = e; + } + + return (result, error); + } + + /// + /// Return all namespaces contained in a InteropAssembly as a + /// InteropNamespace[] + /// + public (InteropNamespace[]?, Exception?) GetNamespaces() + { + Exception? error = null; + InteropNamespace[]? result = null; + + if (!_open) + { + error = new InvalidOperationException("The InteropAssembly was not initialized."); + return (result, error); + } + + var namespaces = _types?.Select(type => new InteropNamespace(type?.FullName, this)); + return (result, error); + } + } + + /// + /// Represents an interoperability namespace. It contains interoperability + /// information about classes, enums, the assembly itself and metadata. + /// This class is an abstraction to represent a real namespace, it is important + /// to know that there are no types in the runtime capable of making this kind of + /// representation. The data can be not fully accurante and result in faulting + /// features of the target namespace. + /// + public class InteropNamespace + { + public string? FullName { get; private set; } + private InteropAssembly? _assembly; + private InteropClass[]? _classes; + private InteropEnum[]? _enums; + + public InteropNamespace(string? fullName, InteropAssembly interopAssembly) + { + FullName = fullName; + _assembly = interopAssembly; + + var allTypes = _assembly + .GetAssemblyObject() + .Item1? + .GetTypes() + .Where(type => type.Namespace == FullName); + + _enums = allTypes? + .Where(type => type.IsEnum) + .Select(type => + { + var assembly = GetAssembly(); + var enumValues = new InteropEnumValues( + Enum + .GetValues(type) + .Cast() + .ToArray(), + assembly, + this); + + var @enum = new InteropEnum(fullName, assembly, this, enumValues); + return @enum; + }) + .ToArray(); + + _classes = allTypes? + .Where(type => !type.IsEnum) + .Select(type => + { + var isStatic = type.IsAbstract && type.IsSealed; + var interopClass = new InteropClass( + fullName, + isStatic, + type.IsAbstract, + type.GetConstructors(), + this, + GetAssembly(), + type); + + return interopClass; + }) + .ToArray(); + } + + public InteropAssembly? GetAssembly() + { + return _assembly; + } + } + + public class InteropClass + { + private InteropAssembly? _assembly; + private InteropNamespace? _namespace; + private InteropMethod[]? _methods; + private InteropField[]? _fields; + private Type? _type; + private ConstructorInfo[]? _constructors; + + public string? FullName { get; private set; } + public bool IsStatic { get; private set; } + public bool IsAbstract { get; private set; } + + public InteropClass( + string? fullName, + bool isStatic, + bool isAbstract, + ConstructorInfo[]? constructors, + InteropNamespace? @namespace, + InteropAssembly? assembly, + Type? type) + { + FullName = fullName; + IsStatic = isStatic; + IsAbstract = isAbstract; + + _constructors = constructors; + _namespace = @namespace; + _assembly = assembly; + _type = type; + } + } + + public class InteropEnum + { + public string? FullName { get; private set; } + + private InteropAssembly? _assembly; + private InteropNamespace? _namespace; + private InteropEnumValues? _values; + + public InteropEnum( + string? fullName, + InteropAssembly? assembly, + InteropNamespace? @namespace, + InteropEnumValues? values) + { + FullName = fullName; + _assembly = assembly; + _namespace = @namespace; + _values = values; + } + } + + public class InteropEnumValues + { + public object[] Values { get; private set; } + + private InteropAssembly? _assembly; + private InteropNamespace? _namespace; + + public InteropEnumValues( + object[] values, + InteropAssembly? assembly, + InteropNamespace? @namespace) + { + Values = values; + _assembly = assembly; + _namespace = @namespace; + } + } + + public class InteropField + { + + } + + public class InteropProperty + { + + } + + public class InteropMethod + { + + } +} \ No newline at end of file