diff --git a/Source/Editor/Content/Import/AudioImportSettings.cs b/Source/Editor/Content/Import/AudioImportSettings.cs
index 19ad1065d..40e715c06 100644
--- a/Source/Editor/Content/Import/AudioImportSettings.cs
+++ b/Source/Editor/Content/Import/AudioImportSettings.cs
@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
namespace FlaxEditor.Content.Import
@@ -93,6 +94,7 @@ namespace FlaxEditor.Content.Import
[StructLayout(LayoutKind.Sequential)]
internal struct InternalOptions
{
+ [MarshalAs(UnmanagedType.I1)]
public AudioFormat Format;
public byte DisableStreaming;
public byte Is3D;
@@ -144,7 +146,7 @@ namespace FlaxEditor.Content.Import
/// Audio asset import entry.
///
///
- public class AudioImportEntry : AssetImportEntry
+ public partial class AudioImportEntry : AssetImportEntry
{
private AudioImportSettings _settings = new AudioImportSettings();
@@ -182,8 +184,9 @@ namespace FlaxEditor.Content.Import
#region Internal Calls
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Import.AudioImportEntry::Internal_GetAudioImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result);
#endregion
}
diff --git a/Source/Editor/Content/Import/ModelImportEntry.cs b/Source/Editor/Content/Import/ModelImportEntry.cs
index bded33956..2b959ddb1 100644
--- a/Source/Editor/Content/Import/ModelImportEntry.cs
+++ b/Source/Editor/Content/Import/ModelImportEntry.cs
@@ -1,8 +1,10 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
+using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
namespace FlaxEditor.Content.Import
@@ -355,6 +357,7 @@ namespace FlaxEditor.Content.Import
private bool ShowAnimation => Type == ModelType.Animation;
[StructLayout(LayoutKind.Sequential)]
+ [NativeMarshalling(typeof(InternalOptionsMarshaler))]
internal struct InternalOptions
{
public ModelType Type;
@@ -410,6 +413,162 @@ namespace FlaxEditor.Content.Import
public int ObjectIndex;
}
+ [CustomMarshaller(typeof(InternalOptions), MarshalMode.Default, typeof(InternalOptionsMarshaler))]
+ internal static class InternalOptionsMarshaler
+ {
+ [Unmanaged]
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct InternalOptionsNative
+ {
+ public int Type;
+
+ // Geometry
+ public byte CalculateNormals;
+ public float SmoothingNormalsAngle;
+ public byte FlipNormals;
+ public float SmoothingTangentsAngle;
+ public byte CalculateTangents;
+ public byte OptimizeMeshes;
+ public byte MergeMeshes;
+ public byte ImportLODs;
+ public byte ImportVertexColors;
+ public byte ImportBlendShapes;
+ public int LightmapUVsSource;
+ //[MarshalAs(UnmanagedType.LPWStr)]
+ public IntPtr CollisionMeshesPrefix;
+
+ // Transform
+ public float Scale;
+ public Quaternion Rotation;
+ public Float3 Translation;
+ public byte CenterGeometry;
+
+ // Animation
+ public int Duration;
+ public float FramesRangeStart;
+ public float FramesRangeEnd;
+ public float DefaultFrameRate;
+ public float SamplingRate;
+ public byte SkipEmptyCurves;
+ public byte OptimizeKeyframes;
+ public byte EnableRootMotion;
+ //[MarshalAs(UnmanagedType.LPWStr)]
+ public IntPtr RootNodeName;
+
+ // Level Of Detail
+ public byte GenerateLODs;
+ public int BaseLOD;
+ public int LODCount;
+ public float TriangleReduction;
+
+ // Misc
+ public byte ImportMaterials;
+ public byte ImportTextures;
+ public byte RestoreMaterialsOnReimport;
+
+ // SDF
+ public byte GenerateSDF;
+ public float SDFResolution;
+
+ // Splitting
+ public byte SplitObjects;
+ public int ObjectIndex;
+ }
+
+ internal static InternalOptions ConvertToManaged(InternalOptionsNative unmanaged) => ToManaged(unmanaged);
+ internal static InternalOptionsNative ConvertToUnmanaged(InternalOptions managed) => ToNative(managed);
+
+ internal static InternalOptions ToManaged(InternalOptionsNative managed)
+ {
+ return new InternalOptions()
+ {
+ Type = (ModelType)managed.Type,
+ CalculateNormals = managed.CalculateNormals,
+ SmoothingNormalsAngle = managed.SmoothingNormalsAngle,
+ FlipNormals = managed.FlipNormals,
+ SmoothingTangentsAngle = managed.SmoothingTangentsAngle,
+ CalculateTangents = managed.CalculateTangents,
+ OptimizeMeshes = managed.OptimizeMeshes,
+ MergeMeshes = managed.MergeMeshes,
+ ImportLODs = managed.ImportLODs,
+ ImportVertexColors = managed.ImportVertexColors,
+ ImportBlendShapes = managed.ImportBlendShapes,
+ LightmapUVsSource = (ModelLightmapUVsSource)managed.LightmapUVsSource,
+ CollisionMeshesPrefix = ManagedString.ToManaged(managed.CollisionMeshesPrefix),
+ Scale = managed.Scale,
+ Rotation = managed.Rotation,
+ Translation = managed.Translation,
+ CenterGeometry = managed.CenterGeometry,
+ Duration = (AnimationDuration)managed.Duration,
+ FramesRangeStart = managed.FramesRangeStart,
+ FramesRangeEnd = managed.FramesRangeEnd,
+ DefaultFrameRate = managed.DefaultFrameRate,
+ SamplingRate = managed.SamplingRate,
+ SkipEmptyCurves = managed.SkipEmptyCurves,
+ OptimizeKeyframes = managed.OptimizeKeyframes,
+ EnableRootMotion = managed.EnableRootMotion,
+ RootNodeName = ManagedString.ToManaged(managed.RootNodeName),
+ GenerateLODs = managed.GenerateLODs,
+ BaseLOD = managed.BaseLOD,
+ LODCount = managed.LODCount,
+ TriangleReduction = managed.TriangleReduction,
+ ImportMaterials = managed.ImportMaterials,
+ ImportTextures = managed.ImportTextures,
+ RestoreMaterialsOnReimport = managed.RestoreMaterialsOnReimport,
+ GenerateSDF = managed.GenerateSDF,
+ SDFResolution = managed.SDFResolution,
+ SplitObjects = managed.SplitObjects,
+ ObjectIndex = managed.ObjectIndex,
+ };
+ }
+ internal static InternalOptionsNative ToNative(InternalOptions managed)
+ {
+ return new InternalOptionsNative()
+ {
+ Type = (int)managed.Type,
+ CalculateNormals = managed.CalculateNormals,
+ SmoothingNormalsAngle = managed.SmoothingNormalsAngle,
+ FlipNormals = managed.FlipNormals,
+ SmoothingTangentsAngle = managed.SmoothingTangentsAngle,
+ CalculateTangents = managed.CalculateTangents,
+ OptimizeMeshes = managed.OptimizeMeshes,
+ MergeMeshes = managed.MergeMeshes,
+ ImportLODs = managed.ImportLODs,
+ ImportVertexColors = managed.ImportVertexColors,
+ ImportBlendShapes = managed.ImportBlendShapes,
+ LightmapUVsSource = (int)managed.LightmapUVsSource,
+ CollisionMeshesPrefix = ManagedString.ToNative(managed.CollisionMeshesPrefix),
+ Scale = managed.Scale,
+ Rotation = managed.Rotation,
+ Translation = managed.Translation,
+ CenterGeometry = managed.CenterGeometry,
+ Duration = (int)managed.Duration,
+ FramesRangeStart = managed.FramesRangeStart,
+ FramesRangeEnd = managed.FramesRangeEnd,
+ DefaultFrameRate = managed.DefaultFrameRate,
+ SamplingRate = managed.SamplingRate,
+ SkipEmptyCurves = managed.SkipEmptyCurves,
+ OptimizeKeyframes = managed.OptimizeKeyframes,
+ EnableRootMotion = managed.EnableRootMotion,
+ RootNodeName = ManagedString.ToNative(managed.RootNodeName),
+ GenerateLODs = managed.GenerateLODs,
+ BaseLOD = managed.BaseLOD,
+ LODCount = managed.LODCount,
+ TriangleReduction = managed.TriangleReduction,
+ ImportMaterials = managed.ImportMaterials,
+ ImportTextures = managed.ImportTextures,
+ RestoreMaterialsOnReimport = managed.RestoreMaterialsOnReimport,
+ GenerateSDF = managed.GenerateSDF,
+ SDFResolution = managed.SDFResolution,
+ SplitObjects = managed.SplitObjects,
+ ObjectIndex = managed.ObjectIndex,
+ };
+ }
+ internal static void Free(InternalOptionsNative unmanaged)
+ {
+ }
+ }
+
internal void ToInternal(out InternalOptions options)
{
options = new InternalOptions
@@ -511,7 +670,7 @@ namespace FlaxEditor.Content.Import
/// Model asset import entry.
///
///
- public class ModelImportEntry : AssetImportEntry
+ public partial class ModelImportEntry : AssetImportEntry
{
private ModelImportSettings _settings = new ModelImportSettings();
@@ -548,8 +707,8 @@ namespace FlaxEditor.Content.Import
#region Internal Calls
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_GetModelImportOptions(string path, out ModelImportSettings.InternalOptions result);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Import.ModelImportEntry::Internal_GetModelImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
+ internal static partial void Internal_GetModelImportOptions(string path, out ModelImportSettings.InternalOptions result);
#endregion
}
diff --git a/Source/Editor/Content/Import/TextureImportEntry.cs b/Source/Editor/Content/Import/TextureImportEntry.cs
index 0ac44ec0b..abffe0ce7 100644
--- a/Source/Editor/Content/Import/TextureImportEntry.cs
+++ b/Source/Editor/Content/Import/TextureImportEntry.cs
@@ -1,10 +1,12 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
+using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
// ReSharper disable InconsistentNaming
@@ -297,6 +299,7 @@ namespace FlaxEditor.Content.Import
public List Sprites = new List();
[StructLayout(LayoutKind.Sequential)]
+ [NativeMarshalling(typeof(InternalOptionsMarshaler))]
internal struct InternalOptions
{
public TextureFormatType Type;
@@ -318,6 +321,85 @@ namespace FlaxEditor.Content.Import
public string[] SpriteNames;
}
+ [CustomMarshaller(typeof(InternalOptions), MarshalMode.Default, typeof(InternalOptionsMarshaler))]
+ internal static class InternalOptionsMarshaler
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct InternalOptionsNative
+ {
+ public byte Type;
+ public byte IsAtlas;
+ public byte NeverStream;
+ public byte Compress;
+ public byte IndependentChannels;
+ public byte sRGB;
+ public byte GenerateMipMaps;
+ public byte FlipY;
+ public byte Resize;
+ public byte PreserveAlphaCoverage;
+ public float PreserveAlphaCoverageReference;
+ public float Scale;
+ public int MaxSize;
+ public int TextureGroup;
+ public Int2 Size;
+ public IntPtr SpriteAreas;
+ public IntPtr SpriteNames;
+ }
+
+ internal static InternalOptions ConvertToManaged(InternalOptionsNative unmanaged) => ToManaged(unmanaged);
+ internal static InternalOptionsNative ConvertToUnmanaged(InternalOptions managed) => ToNative(managed);
+
+ internal static InternalOptions ToManaged(InternalOptionsNative managed)
+ {
+ return new InternalOptions()
+ {
+ Type = (TextureFormatType)managed.Type,
+ IsAtlas = managed.IsAtlas,
+ NeverStream = managed.NeverStream,
+ Compress = managed.Compress,
+ IndependentChannels = managed.IndependentChannels,
+ sRGB = managed.sRGB,
+ GenerateMipMaps = managed.GenerateMipMaps,
+ FlipY = managed.FlipY,
+ Resize = managed.Resize,
+ PreserveAlphaCoverage = managed.PreserveAlphaCoverage,
+ PreserveAlphaCoverageReference = managed.PreserveAlphaCoverageReference,
+ Scale = managed.Scale,
+ MaxSize = managed.MaxSize,
+ TextureGroup = managed.TextureGroup,
+ Size = managed.Size,
+ SpriteAreas = managed.SpriteAreas != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray((ManagedArray)GCHandle.FromIntPtr(managed.SpriteAreas).Target) : null,
+ SpriteNames = managed.SpriteNames != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray((ManagedArray)GCHandle.FromIntPtr(managed.SpriteNames).Target) : null,
+ };
+ }
+ internal static InternalOptionsNative ToNative(InternalOptions managed)
+ {
+ return new InternalOptionsNative()
+ {
+ Type = (byte)managed.Type,
+ IsAtlas = managed.IsAtlas,
+ NeverStream = managed.NeverStream,
+ Compress = managed.Compress,
+ IndependentChannels = managed.IndependentChannels,
+ sRGB = managed.sRGB,
+ GenerateMipMaps = managed.GenerateMipMaps,
+ FlipY = managed.FlipY,
+ Resize = managed.Resize,
+ PreserveAlphaCoverage = managed.PreserveAlphaCoverage,
+ PreserveAlphaCoverageReference = managed.PreserveAlphaCoverageReference,
+ Scale = managed.Scale,
+ MaxSize = managed.MaxSize,
+ TextureGroup = managed.TextureGroup,
+ Size = managed.Size,
+ SpriteAreas = managed.SpriteAreas?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteAreas)))) : IntPtr.Zero,
+ SpriteNames = managed.SpriteNames?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteNames)))) : IntPtr.Zero,
+ };
+ }
+ internal static void Free(InternalOptionsNative unmanaged)
+ {
+ }
+ }
+
internal void ToInternal(out InternalOptions options)
{
options = new InternalOptions
@@ -406,7 +488,7 @@ namespace FlaxEditor.Content.Import
/// Texture asset import entry.
///
///
- public class TextureImportEntry : AssetImportEntry
+ public partial class TextureImportEntry : AssetImportEntry
{
private TextureImportSettings _settings = new TextureImportSettings();
@@ -509,8 +591,9 @@ namespace FlaxEditor.Content.Import
#region Internal Calls
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_GetTextureImportOptions(string path, out TextureImportSettings.InternalOptions result);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Import.TextureImportEntry::Internal_GetTextureImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_GetTextureImportOptions(string path, out TextureImportSettings.InternalOptions result);
#endregion
}
diff --git a/Source/Editor/CustomEditors/CustomEditorsUtil.cs b/Source/Editor/CustomEditors/CustomEditorsUtil.cs
index 4b934cbd6..4df247786 100644
--- a/Source/Editor/CustomEditors/CustomEditorsUtil.cs
+++ b/Source/Editor/CustomEditors/CustomEditorsUtil.cs
@@ -4,13 +4,15 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.Scripting;
using FlaxEngine;
namespace FlaxEditor.CustomEditors
{
- internal static class CustomEditorsUtil
+ internal static partial class CustomEditorsUtil
{
internal static readonly Dictionary InBuildTypeNames = new Dictionary()
{
@@ -129,7 +131,8 @@ namespace FlaxEditor.CustomEditors
return new GenericEditor();
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Type Internal_GetCustomEditor(Type targetType);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.CustomEditors.CustomEditorsUtil::Internal_GetCustomEditor", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalUsing(typeof(SystemTypeMarshaller))]
+ internal static partial Type Internal_GetCustomEditor([MarshalUsing(typeof(SystemTypeMarshaller))] Type targetType);
}
}
diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs
index 421512ce0..9eeb4bb35 100644
--- a/Source/Editor/Editor.cs
+++ b/Source/Editor/Editor.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
using FlaxEditor.Content.Settings;
@@ -20,6 +21,8 @@ using FlaxEngine.Assertions;
using FlaxEngine.GUI;
using FlaxEngine.Json;
+#pragma warning disable CS1591
+
namespace FlaxEditor
{
///
@@ -59,17 +62,20 @@ namespace FlaxEditor
///
/// Gets a value indicating whether this Editor is running a dev instance of the engine.
///
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool IsDevInstance();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::IsDevInstance", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool IsDevInstance();
///
/// Gets a value indicating whether this Editor is running as official build (distributed via Flax services).
///
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool IsOfficialBuild();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::IsOfficialBuild", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool IsOfficialBuild();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_IsPlayMode();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_IsPlayMode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_IsPlayMode();
///
/// True if the editor is running now in a play mode. Assigned by the managed editor instance.
@@ -1206,6 +1212,7 @@ namespace FlaxEditor
}
[StructLayout(LayoutKind.Sequential)]
+ [NativeMarshalling(typeof(VisualScriptLocalMarshaller))]
internal struct VisualScriptLocal
{
public string Value;
@@ -1214,7 +1221,48 @@ namespace FlaxEditor
public int BoxId;
}
+ [CustomMarshaller(typeof(VisualScriptLocal), MarshalMode.Default, typeof(VisualScriptLocalMarshaller))]
+ internal static class VisualScriptLocalMarshaller
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct VisualScriptLocalNative
+ {
+ public IntPtr Value;
+ public IntPtr ValueTypeName;
+ public uint NodeId;
+ public int BoxId;
+ }
+
+ internal static VisualScriptLocal ConvertToManaged(VisualScriptLocalNative unmanaged) => ToManaged(unmanaged);
+ internal static VisualScriptLocalNative ConvertToUnmanaged(VisualScriptLocal managed) => ToNative(managed);
+
+ internal static VisualScriptLocal ToManaged(VisualScriptLocalNative managed)
+ {
+ return new VisualScriptLocal()
+ {
+ Value = ManagedString.ToManaged(managed.Value),
+ ValueTypeName = ManagedString.ToManaged(managed.ValueTypeName),
+ NodeId = managed.NodeId,
+ BoxId = managed.BoxId,
+ };
+ }
+ internal static VisualScriptLocalNative ToNative(VisualScriptLocal managed)
+ {
+ return new VisualScriptLocalNative()
+ {
+ Value = ManagedString.ToNative(managed.Value),
+ ValueTypeName = ManagedString.ToNative(managed.ValueTypeName),
+ NodeId = managed.NodeId,
+ BoxId = managed.BoxId,
+ };
+ }
+ internal static void Free(VisualScriptLocalNative unmanaged)
+ {
+ }
+ }
+
[StructLayout(LayoutKind.Sequential)]
+ [NativeMarshalling(typeof(VisualScriptStackFrameMarshaller))]
internal struct VisualScriptStackFrame
{
public VisualScript Script;
@@ -1222,6 +1270,43 @@ namespace FlaxEditor
public int BoxId;
}
+ [CustomMarshaller(typeof(VisualScriptStackFrame), MarshalMode.Default, typeof(VisualScriptStackFrameMarshaller))]
+ internal static class VisualScriptStackFrameMarshaller
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct VisualScriptStackFrameNative
+ {
+ public IntPtr Script;
+ public uint NodeId;
+ public int BoxId;
+ }
+
+ internal static VisualScriptStackFrame ConvertToManaged(VisualScriptStackFrameNative unmanaged) => ToManaged(unmanaged);
+ internal static VisualScriptStackFrameNative ConvertToUnmanaged(VisualScriptStackFrame managed) => ToNative(managed);
+
+ internal static VisualScriptStackFrame ToManaged(VisualScriptStackFrameNative managed)
+ {
+ return new VisualScriptStackFrame()
+ {
+ Script = VisualScriptMarshaller.ConvertToManaged(managed.Script),
+ NodeId = managed.NodeId,
+ BoxId = managed.BoxId,
+ };
+ }
+ internal static VisualScriptStackFrameNative ToNative(VisualScriptStackFrame managed)
+ {
+ return new VisualScriptStackFrameNative()
+ {
+ Script = VisualScriptMarshaller.ConvertToUnmanaged(managed.Script),
+ NodeId = managed.NodeId,
+ BoxId = managed.BoxId,
+ };
+ }
+ internal static void Free(VisualScriptStackFrameNative unmanaged)
+ {
+ }
+ }
+
internal void BuildCommand(string arg)
{
if (TryBuildCommand(arg))
@@ -1419,116 +1504,133 @@ namespace FlaxEditor
Instance.StateMachine.StateChanged += RequestStartPlayOnEditMode;
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int Internal_ReadOutputLogs(string[] outMessages, byte[] outLogTypes, long[] outLogTimes);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ReadOutputLogs", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial int Internal_ReadOutputLogs([MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref string[] outMessages, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref byte[] outLogTypes, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref long[] outLogTimes, int outCapacity);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_SetPlayMode(bool value);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SetPlayMode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_SetPlayMode([MarshalAs(UnmanagedType.U1)] bool value);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string Internal_GetProjectPath();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetProjectPath", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial string Internal_GetProjectPath();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CloneAssetFile", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_Import(string inputPath, string outputPath, IntPtr arg);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_Import", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_Import(string inputPath, string outputPath, IntPtr arg);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_ImportTexture(string inputPath, string outputPath, ref TextureImportSettings.InternalOptions options);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ImportTexture", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_ImportTexture(string inputPath, string outputPath, ref TextureImportSettings.InternalOptions options);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_ImportModel(string inputPath, string outputPath, ref ModelImportSettings.InternalOptions options);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ImportModel", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_ImportModel(string inputPath, string outputPath, ref ModelImportSettings.InternalOptions options);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ImportAudio", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_SaveJsonAsset(string outputPath, string data, string typename);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SaveJsonAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_SaveJsonAsset(string outputPath, string data, string typename);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_CopyCache(ref Guid dstId, ref Guid srcId);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CopyCache", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_CopyCache(ref Guid dstId, ref Guid srcId);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_BakeLightmaps(bool cancel);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_BakeLightmaps", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_BakeLightmaps([MarshalAs(UnmanagedType.U1)] bool cancel);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string Internal_GetShaderAssetSourceCode(IntPtr obj);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetShaderAssetSourceCode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial string Internal_GetShaderAssetSourceCode(IntPtr obj);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, uint materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CookMeshCollision", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, uint materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_GetCollisionWires(IntPtr collisionData, out Float3[] triangles, out int[] indices);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetCollisionWires", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_GetCollisionWires(IntPtr collisionData, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)] out Float3[] triangles, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)] out int[] indices);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_GetEditorBoxWithChildren(IntPtr obj, out BoundingBox resultAsRef);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetEditorBoxWithChildren", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_GetEditorBoxWithChildren(IntPtr obj, out BoundingBox resultAsRef);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_SetOptions(ref InternalOptions options);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SetOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_SetOptions(ref InternalOptions options);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_DrawNavMesh();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_DrawNavMesh", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_DrawNavMesh();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_CloseSplashScreen();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CloseSplashScreen", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_CloseSplashScreen();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CreateAsset(NewAssetType type, string outputPath);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CreateAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_CreateAsset(NewAssetType type, string outputPath);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CreateVisualScript(string outputPath, string baseTypename);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CreateVisualScript", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_CreateVisualScript(string outputPath, string baseTypename);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string Internal_CanImport(string extension);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CanImport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial string Internal_CanImport(string extension);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CanExport(string path);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CanExport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_CanExport(string path);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_Export(string inputPath, string outputFolder);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_Export", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_Export(string inputPath, string outputFolder);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_GetIsEveryAssemblyLoaded();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetIsEveryAssemblyLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_GetIsEveryAssemblyLoaded();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int Internal_GetLastProjectOpenedEngineBuild();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetLastProjectOpenedEngineBuild", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial int Internal_GetLastProjectOpenedEngineBuild();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_GetIsCSGActive();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetIsCSGActive", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_GetIsCSGActive();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_RunVisualScriptBreakpointLoopTick(float deltaTime);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_RunVisualScriptBreakpointLoopTick", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_RunVisualScriptBreakpointLoopTick(float deltaTime);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern VisualScriptLocal[] Internal_GetVisualScriptLocals();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetVisualScriptLocals", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)]
+ internal static partial VisualScriptLocal[] Internal_GetVisualScriptLocals();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern VisualScriptStackFrame[] Internal_GetVisualScriptStackFrames();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetVisualScriptStackFrames", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)]
+ internal static partial VisualScriptStackFrame[] Internal_GetVisualScriptStackFrames();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern VisualScriptStackFrame Internal_GetVisualScriptPreviousScopeFrame();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetVisualScriptPreviousScopeFrame", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial VisualScriptStackFrame Internal_GetVisualScriptPreviousScopeFrame();
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_EvaluateVisualScriptLocal(IntPtr script, ref VisualScriptLocal local);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_EvaluateVisualScriptLocal", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_EvaluateVisualScriptLocal(IntPtr script, ref VisualScriptLocal local);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_DeserializeSceneObject(IntPtr sceneObject, string json);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_DeserializeSceneObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_DeserializeSceneObject(IntPtr sceneObject, string json);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_LoadAsset(ref Guid id);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_LoadAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_LoadAsset(ref Guid id);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CanSetToRoot", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern float Internal_GetAnimationTime(IntPtr animatedModel);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial float Internal_GetAnimationTime(IntPtr animatedModel);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_SetAnimationTime(IntPtr animatedModel, float time);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_SetAnimationTime(IntPtr animatedModel, float time);
#endregion
}
diff --git a/Source/Editor/GUI/Timeline/ParticleSystemTimeline.cs b/Source/Editor/GUI/Timeline/ParticleSystemTimeline.cs
index 246c33b4d..cd660daab 100644
--- a/Source/Editor/GUI/Timeline/ParticleSystemTimeline.cs
+++ b/Source/Editor/GUI/Timeline/ParticleSystemTimeline.cs
@@ -10,6 +10,7 @@ using FlaxEditor.GUI.Timeline.Tracks;
using FlaxEditor.Utilities;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
+using FlaxEngine.Utilities;
namespace FlaxEditor.GUI.Timeline
{
diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp
index 3d236cd49..11844b23c 100644
--- a/Source/Editor/Managed/ManagedEditor.Internal.cpp
+++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp
@@ -323,20 +323,25 @@ namespace CustomEditorsUtilInternal
{
MonoReflectionType* GetCustomEditor(MonoReflectionType* targetType)
{
+ SCRIPTING_EXPORT("FlaxEditor.CustomEditors.CustomEditorsUtil::Internal_GetCustomEditor")
return CustomEditorsUtil::GetCustomEditor(targetType);
}
}
namespace LayersAndTagsSettingsInternal1
{
- MonoArray* GetCurrentTags()
+ MonoArray* GetCurrentTags(int* tagsCount)
{
+ SCRIPTING_EXPORT("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags")
+ *tagsCount = Level::Tags.Count();
return MUtils::ToArray(Level::Tags);
}
- MonoArray* GetCurrentLayers()
+ MonoArray* GetCurrentLayers(int* layersCount)
{
- return MUtils::ToArray(Span(Level::Layers, Math::Max(1, Level::GetNonEmptyLayerNamesCount())));
+ SCRIPTING_EXPORT("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers")
+ *layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount());
+ return MUtils::ToArray(Span(Level::Layers, *layersCount));
}
}
@@ -344,6 +349,7 @@ namespace GameSettingsInternal1
{
void Apply()
{
+ SCRIPTING_EXPORT("FlaxEditor.Content.Settings.GameSettings::Apply")
LOG(Info, "Apply game settings");
GameSettings::Load();
}
@@ -380,6 +386,7 @@ class ManagedEditorInternal
public:
static bool IsDevInstance()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::IsDevInstance")
#if COMPILE_WITH_DEV_ENV
return true;
#else
@@ -389,6 +396,7 @@ public:
static bool IsOfficialBuild()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::IsOfficialBuild")
#if OFFICIAL_BUILD
return true;
#else
@@ -398,18 +406,20 @@ public:
static bool IsPlayMode()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_IsPlayMode")
return Editor::IsPlayMode;
}
- static int32 ReadOutputLogs(MonoArray* outMessages, MonoArray* outLogTypes, MonoArray* outLogTimes)
+ static int32 ReadOutputLogs(MonoArray** outMessages, MonoArray** outLogTypes, MonoArray** outLogTimes, int outArraySize)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ReadOutputLogs")
ScopeLock lock(CachedLogDataLocker);
if (CachedLogData.IsEmpty() || CachedLogData.Get() == nullptr)
return 0;
int32 count = 0;
- const int32 maxCount = (int32)mono_array_length(outMessages);
+ const int32 maxCount = outArraySize;//(int32)mono_array_length(*outMessages);
byte* ptr = CachedLogData.Get();
byte* end = ptr + CachedLogData.Count();
@@ -429,9 +439,9 @@ public:
auto msgObj = MUtils::ToString(StringView(msg, length));
- mono_array_setref(outMessages, count, msgObj);
- mono_array_set(outLogTypes, byte, count, type);
- mono_array_set(outLogTimes, int64, count, time);
+ mono_array_setref(*outMessages, count, msgObj);
+ mono_array_set(*outLogTypes, byte, count, type);
+ mono_array_set(*outLogTimes, int64, count, time);
count++;
}
@@ -445,21 +455,25 @@ public:
static void SetPlayMode(bool value)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetPlayMode")
Editor::IsPlayMode = value;
}
static MonoString* GetProjectPath()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetProjectPath")
return MUtils::ToString(Editor::Project->ProjectPath);
}
static void CloseSplashScreen()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CloseSplashScreen")
Editor::CloseSplashScreen();
}
static bool CloneAssetFile(MonoString* dstPathObj, MonoString* srcPathObj, Guid* dstId)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CloneAssetFile")
// Get normalized paths
String dstPath, srcPath;
MUtils::ToString(dstPathObj, dstPath);
@@ -489,6 +503,7 @@ public:
static bool CreateAsset(NewAssetType type, MonoString* outputPathObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CreateAsset")
String tag;
switch (type)
{
@@ -541,6 +556,7 @@ public:
static bool CreateVisualScript(MonoString* outputPathObj, MonoString* baseTypenameObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CreateVisualScript")
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
@@ -551,6 +567,7 @@ public:
static MonoString* CanImport(MonoString* extensionObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CanImport")
String extension;
MUtils::ToString(extensionObj, extension);
if (extension.Length() > 0 && extension[0] == '.')
@@ -561,6 +578,7 @@ public:
static bool Import(MonoString* inputPathObj, MonoString* outputPathObj, void* arg)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_Import")
String inputPath, outputPath;
MUtils::ToString(inputPathObj, inputPath);
MUtils::ToString(outputPathObj, outputPath);
@@ -572,6 +590,7 @@ public:
static bool ImportTexture(MonoString* inputPathObj, MonoString* outputPathObj, InternalTextureOptions* optionsObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ImportTexture")
ImportTexture::Options options;
InternalTextureOptions::Convert(optionsObj, &options);
@@ -580,6 +599,7 @@ public:
static bool ImportModel(MonoString* inputPathObj, MonoString* outputPathObj, InternalModelOptions* optionsObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ImportModel")
ImportModelFile::Options options;
InternalModelOptions::Convert(optionsObj, &options);
@@ -588,6 +608,7 @@ public:
static bool ImportAudio(MonoString* inputPathObj, MonoString* outputPathObj, InternalAudioOptions* optionsObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ImportAudio")
ImportAudio::Options options;
InternalAudioOptions::Convert(optionsObj, &options);
@@ -596,6 +617,7 @@ public:
static void GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetAudioClipMetadata")
INTERNAL_CALL_CHECK(clip);
*originalSize = clip->AudioHeader.OriginalSize;
*importedSize = clip->AudioHeader.ImportedSize;
@@ -603,6 +625,7 @@ public:
static bool SaveJsonAsset(MonoString* outputPathObj, MonoString* dataObj, MonoString* dataTypeNameObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SaveJsonAsset")
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
@@ -623,6 +646,8 @@ public:
static bool GetTextureImportOptions(MonoString* pathObj, InternalTextureOptions* result)
{
+ SCRIPTING_EXPORT("FlaxEditor.Content.Import.TextureImportEntry::Internal_GetTextureImportOptions")
+
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
@@ -641,6 +666,7 @@ public:
static void GetModelImportOptions(MonoString* pathObj, InternalModelOptions* result)
{
+ SCRIPTING_EXPORT("FlaxEditor.Content.Import.ModelImportEntry::Internal_GetModelImportOptions")
// Initialize defaults
ImportModelFile::Options options;
if (const auto* graphicsSettings = GraphicsSettings::Get())
@@ -660,6 +686,7 @@ public:
static bool GetAudioImportOptions(MonoString* pathObj, InternalAudioOptions* result)
{
+ SCRIPTING_EXPORT("FlaxEditor.Content.Import.AudioImportEntry::Internal_GetAudioImportOptions")
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
@@ -678,6 +705,7 @@ public:
static bool CanExport(MonoString* pathObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CanExport")
#if COMPILE_WITH_ASSETS_EXPORTER
String path;
MUtils::ToString(pathObj, path);
@@ -691,6 +719,7 @@ public:
static bool Export(MonoString* inputPathObj, MonoString* outputFolderObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_Export")
#if COMPILE_WITH_ASSETS_EXPORTER
String inputPath;
MUtils::ToString(inputPathObj, inputPath);
@@ -708,11 +737,13 @@ public:
static void CopyCache(Guid* dstId, Guid* srcId)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CopyCache")
ShaderCacheManager::CopyCache(*dstId, *srcId);
}
static void BakeLightmaps(bool cancel)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_BakeLightmaps")
auto builder = ShadowsOfMordor::Builder::Instance();
if (cancel)
builder->CancelBuild();
@@ -722,6 +753,7 @@ public:
static MonoString* GetShaderAssetSourceCode(BinaryAsset* obj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetShaderAssetSourceCode")
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
if (obj->WaitForLoaded())
DebugLog::ThrowNullReference();
@@ -747,6 +779,7 @@ public:
static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, uint32 materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CookMeshCollision")
#if COMPILE_WITH_PHYSICS_COOKING
CollisionCooking::Argument arg;
String path;
@@ -767,6 +800,7 @@ public:
static void GetCollisionWires(CollisionData* collisionData, MonoArray** triangles, MonoArray** indices)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetCollisionWires")
if (!collisionData || collisionData->WaitForLoaded() || collisionData->GetOptions().Type == CollisionDataType::None)
return;
@@ -792,32 +826,38 @@ public:
static void GetEditorBoxWithChildren(Actor* obj, BoundingBox* result)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetEditorBoxWithChildren")
INTERNAL_CALL_CHECK(obj);
*result = obj->GetEditorBoxChildren();
}
static void SetOptions(ManagedEditor::InternalOptions* options)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetOptions")
ManagedEditor::ManagedEditorOptions = *options;
}
static void DrawNavMesh()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_DrawNavMesh")
Navigation::DrawNavMesh();
}
static bool GetIsEveryAssemblyLoaded()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetIsEveryAssemblyLoaded")
return Scripting::IsEveryAssemblyLoaded();
}
static int32 GetLastProjectOpenedEngineBuild()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetLastProjectOpenedEngineBuild")
return Editor::LastProjectOpenedEngineBuild;
}
static bool GetIsCSGActive()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetIsCSGActive")
#if COMPILE_WITH_CSG_BUILDER
return CSG::Builder::IsActive();
#else
@@ -827,6 +867,7 @@ public:
static void RunVisualScriptBreakpointLoopTick(float deltaTime)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_RunVisualScriptBreakpointLoopTick")
// Update
Platform::Tick();
Engine::HasFocus = (Engine::MainWindow && Engine::MainWindow->IsFocused()) || Platform::GetHasFocus();
@@ -921,6 +962,7 @@ public:
static MonoArray* GetVisualScriptLocals()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetVisualScriptLocals")
MonoArray* result = nullptr;
const auto stack = VisualScripting::GetThreadStackTop();
if (stack && stack->Scope)
@@ -969,6 +1011,7 @@ public:
static MonoArray* GetVisualScriptStackFrames()
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetVisualScriptStackFrames")
MonoArray* result = nullptr;
const auto stack = VisualScripting::GetThreadStackTop();
if (stack)
@@ -1022,6 +1065,7 @@ public:
static bool EvaluateVisualScriptLocal(VisualScript* script, VisualScriptLocalManaged* local)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_EvaluateVisualScriptLocal")
Variant v;
if (VisualScripting::Evaluate(script, VisualScripting::GetThreadStackTop()->Instance, local->NodeId, local->BoxId, v))
{
@@ -1034,6 +1078,7 @@ public:
static void DeserializeSceneObject(SceneObject* sceneObject, MonoString* jsonObj)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_DeserializeSceneObject")
PROFILE_CPU_NAMED("DeserializeSceneObject");
StringAnsi json;
@@ -1062,11 +1107,13 @@ public:
static void LoadAsset(Guid* id)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_LoadAsset")
Content::LoadAsync(*id);
}
static bool CanSetToRoot(Prefab* prefab, Actor* targetActor)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CanSetToRoot")
// Reference: Prefab::ApplyAll(Actor* targetActor)
if (targetActor->GetPrefabID() != prefab->GetID())
return false;
@@ -1089,11 +1136,13 @@ public:
static float GetAnimationTime(AnimatedModel* animatedModel)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetAnimationTime")
return animatedModel && animatedModel->GraphInstance.State.Count() == 1 ? animatedModel->GraphInstance.State[0].Animation.TimePosition : 0.0f;
}
static void SetAnimationTime(AnimatedModel* animatedModel, float time)
{
+ SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetAnimationTime")
if (animatedModel && animatedModel->GraphInstance.State.Count() == 1)
animatedModel->GraphInstance.State[0].Animation.TimePosition = time;
}
diff --git a/Source/Editor/Modules/ContentImportingModule.cs b/Source/Editor/Modules/ContentImportingModule.cs
index 4597f8d74..fa990dd7a 100644
--- a/Source/Editor/Modules/ContentImportingModule.cs
+++ b/Source/Editor/Modules/ContentImportingModule.cs
@@ -375,7 +375,9 @@ namespace FlaxEditor.Modules
Thread.Sleep(0);
_workerThread.Join(1000);
- _workerThread.Abort();
+#if !USE_NETCORE
+ _workerThread.Abort(); // Deprecated in .NET 7
+#endif
_workerThread = null;
}
diff --git a/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs b/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs
index 7042dfb92..07e29f506 100644
--- a/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs
+++ b/Source/Editor/Modules/SourceCodeEditing/CodeDocsModule.cs
@@ -257,7 +257,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
{
Profiler.BeginEvent("GetXmlDocs");
- var uri = new UriBuilder(assembly.CodeBase);
+ var uri = new UriBuilder(Utils.GetAssemblyLocation(assembly));
var path = Uri.UnescapeDataString(uri.Path);
var name = assembly.GetName().Name;
var xmlFilePath = Path.Combine(Path.GetDirectoryName(path), name + ".xml");
diff --git a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs
index 010cd4942..1e7583dbe 100644
--- a/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs
+++ b/Source/Editor/Modules/SourceCodeEditing/CodeEditingModule.cs
@@ -391,9 +391,27 @@ namespace FlaxEditor.Modules.SourceCodeEditing
private static bool HasAssemblyValidAnyTypes(Assembly assembly)
{
+ var codeBase = Utils.GetAssemblyLocation(assembly);
+#if USE_NETCORE
+ if (assembly.ManifestModule.FullyQualifiedName == "")
+ return false;
+
+ if (string.IsNullOrEmpty(codeBase))
+ return true;
+
+ // Skip runtime related assemblies
+ string repositoryUrl = assembly.GetCustomAttributes().FirstOrDefault(x => x.Key == "RepositoryUrl")?.Value ?? "";
+ if (repositoryUrl != "https://github.com/dotnet/runtime")
+ return true;
+#else
+ if (string.IsNullOrEmpty(codeBase))
+ return true;
+
// Skip assemblies from in-build Mono directory
- var codeBase = assembly.CodeBase;
- return string.IsNullOrEmpty(codeBase) || !codeBase.Contains("/Mono/lib/mono/");
+ if (!codeBase.Contains("/Mono/lib/mono/"))
+ return true;
+#endif
+ return false;
}
private static bool HasAssemblyValidScriptingTypes(Assembly a)
diff --git a/Source/Editor/Plugins/PluginUtils.cs b/Source/Editor/Plugins/PluginUtils.cs
index 3c9765922..659e66db5 100644
--- a/Source/Editor/Plugins/PluginUtils.cs
+++ b/Source/Editor/Plugins/PluginUtils.cs
@@ -24,7 +24,7 @@ namespace FlaxEditor
var type = plugin.GetType();
var assembly = type.Assembly;
- var assemblyPath = assembly.Location;
+ var assemblyPath = Utils.GetAssemblyLocation(assembly);
var assemblyName = assembly.GetName().Name;
var dotEditorPos = assemblyName.LastIndexOf(".Editor", StringComparison.OrdinalIgnoreCase);
if (dotEditorPos != -1)
diff --git a/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs b/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs
index 339ea60f2..9a4bd608f 100644
--- a/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs
+++ b/Source/Editor/Progress/Handlers/CompileScriptsProgress.cs
@@ -41,8 +41,10 @@ namespace FlaxEditor.Progress.Handlers
private void OnScriptsReload()
{
+#if !USE_NETCORE
// Clear types cache
Newtonsoft.Json.JsonSerializer.ClearCache();
+#endif
}
private void OnScriptsReloadEnd()
diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp
index 25e22eaca..1f119f9af 100644
--- a/Source/Editor/Scripting/ScriptsBuilder.cpp
+++ b/Source/Editor/Scripting/ScriptsBuilder.cpp
@@ -176,6 +176,7 @@ bool ScriptsBuilder::IsReady()
void ScriptsBuilder::MarkWorkspaceDirty()
{
+ SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_MarkWorkspaceDirty")
ScopeLock scopeLock(_locker);
_lastSourceCodeEdited = DateTime::Now();
_wasProjectStructureChanged = true;
@@ -183,6 +184,7 @@ void ScriptsBuilder::MarkWorkspaceDirty()
void ScriptsBuilder::CheckForCompile()
{
+ SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_CheckForCompile")
ScopeLock scopeLock(_locker);
if (IsSourceDirty())
Compile();
@@ -205,6 +207,7 @@ void ScriptsBuilderImpl::onScriptsReloadEnd()
void ScriptsBuilder::Compile()
{
+ SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_Compile")
ScopeLock scopeLock(_locker);
// Request compile job
diff --git a/Source/Editor/States/BuildingScenesState.cs b/Source/Editor/States/BuildingScenesState.cs
index c6d749503..4261cc6d6 100644
--- a/Source/Editor/States/BuildingScenesState.cs
+++ b/Source/Editor/States/BuildingScenesState.cs
@@ -6,7 +6,9 @@ using System.Linq;
using FlaxEditor.Options;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
+using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
+using Utils = FlaxEngine.Utils;
namespace FlaxEditor.States
{
diff --git a/Source/Editor/States/EditingSceneState.cs b/Source/Editor/States/EditingSceneState.cs
index 497b7679c..00bca5a21 100644
--- a/Source/Editor/States/EditingSceneState.cs
+++ b/Source/Editor/States/EditingSceneState.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using FlaxEngine;
+using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
namespace FlaxEditor.States
diff --git a/Source/Editor/States/ReloadingScriptsState.cs b/Source/Editor/States/ReloadingScriptsState.cs
index 8676a2af4..d76963734 100644
--- a/Source/Editor/States/ReloadingScriptsState.cs
+++ b/Source/Editor/States/ReloadingScriptsState.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using FlaxEngine;
+using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
namespace FlaxEditor.States
diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs
index 5ff5d35d7..d96155584 100644
--- a/Source/Editor/Surface/Archetypes/Function.cs
+++ b/Source/Editor/Surface/Archetypes/Function.cs
@@ -12,6 +12,7 @@ using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Windows.Assets;
+using FlaxEngine.Utilities;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -587,7 +588,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < _parameters.Length; i++)
{
writer.Write(_parameters[i].Name); // Parameter name
- Utilities.VariantUtils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
+ VariantUtils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
}
SetValue(2, stream.ToArray());
}
@@ -605,7 +606,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < parametersCount; i++)
{
var parameterName = reader.ReadString(); // Parameter name
- var boxType = Utilities.VariantUtils.ReadVariantType(reader); // Box type
+ var boxType = VariantUtils.ReadVariantType(reader); // Box type
MakeBox(i + 1, parameterName, boxType);
}
}
@@ -788,14 +789,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
- signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
+ signature.ReturnType = VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = Utilities.Utils.ReadStr(reader, 11); // Parameter name
- param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
+ param.Type = VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -809,14 +810,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
- signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
+ signature.ReturnType = VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = reader.ReadString(); // Parameter name
- param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
+ param.Type = VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -833,13 +834,13 @@ namespace FlaxEditor.Surface.Archetypes
{
writer.Write((byte)4); // Version
writer.Write(methodInfo.IsStatic); // Is Static
- Utilities.VariantUtils.WriteVariantType(writer, methodInfo.ValueType); // Return type
+ VariantUtils.WriteVariantType(writer, methodInfo.ValueType); // Return type
writer.Write(parameters.Length); // Parameters count
for (int i = 0; i < parameters.Length; i++)
{
ref var param = ref parameters[i];
Utilities.Utils.WriteStr(writer, param.Name, 11); // Parameter name
- Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
+ VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)(param.IsOut ? 1 : 0)); // Is parameter out
}
return stream.ToArray();
@@ -1461,14 +1462,14 @@ namespace FlaxEditor.Surface.Archetypes
if (_signature.IsVirtual)
flags |= Flags.Virtual;
writer.Write((byte)flags); // Flags
- Utilities.VariantUtils.WriteVariantType(writer, _signature.ReturnType); // Return Type
+ VariantUtils.WriteVariantType(writer, _signature.ReturnType); // Return Type
var parametersCount = _signature.Parameters?.Length ?? 0;
writer.Write(parametersCount); // Parameters count
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref _signature.Parameters[i];
Utilities.Utils.WriteStrAnsi(writer, param.Name, 13); // Parameter name
- Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
+ VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)0); // Is parameter out
writer.Write((byte)0); // Has default value
}
@@ -1497,13 +1498,13 @@ namespace FlaxEditor.Surface.Archetypes
var flags = (Flags)reader.ReadByte(); // Flags
_signature.IsStatic = (flags & Flags.Static) == Flags.Static;
_signature.IsVirtual = (flags & Flags.Virtual) == Flags.Virtual;
- _signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return Type
+ _signature.ReturnType = VariantUtils.ReadVariantScriptType(reader); // Return Type
var parametersCount = reader.ReadInt32(); // Parameters count
_signature.Parameters = new Parameter[parametersCount];
for (int i = 0; i < parametersCount; i++)
{
var paramName = Utilities.Utils.ReadStrAnsi(reader, 13); // Parameter name
- var paramType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
+ var paramType = VariantUtils.ReadVariantScriptType(reader); // Parameter type
var isOut = reader.ReadByte() != 0; // Is parameter out
var hasDefaultValue = reader.ReadByte() != 0; // Has default value
_signature.Parameters[i] = new Parameter
diff --git a/Source/Editor/Surface/Archetypes/Packing.cs b/Source/Editor/Surface/Archetypes/Packing.cs
index cc9d77fa5..e3f79b148 100644
--- a/Source/Editor/Surface/Archetypes/Packing.cs
+++ b/Source/Editor/Surface/Archetypes/Packing.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection;
using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
+using FlaxEngine.Utilities;
using FlaxEngine;
namespace FlaxEditor.Surface.Archetypes
@@ -167,7 +168,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
Utilities.Utils.WriteStr(writer, fields[i].Name, 11); // Field type
- Utilities.VariantUtils.WriteVariantType(writer, fields[i].ValueType); // Field type
+ VariantUtils.WriteVariantType(writer, fields[i].ValueType); // Field type
}
Values[1] = stream.ToArray();
}
@@ -184,7 +185,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
var fieldName = Utilities.Utils.ReadStr(reader, 11); // Field name
- var fieldType = Utilities.VariantUtils.ReadVariantType(reader); // Field type
+ var fieldType = VariantUtils.ReadVariantType(reader); // Field type
MakeBox(i + 1, fieldName, new ScriptType(fieldType));
}
}
diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs
index 29f540e62..d7d98b2e1 100644
--- a/Source/Editor/Surface/AttributesEditor.cs
+++ b/Source/Editor/Surface/AttributesEditor.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Runtime.Loader;
using System.Runtime.Serialization.Formatters.Binary;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
@@ -118,8 +119,13 @@ namespace FlaxEditor.Surface
using (var stream = new MemoryStream())
{
+ // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
+ using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
+
var formatter = new BinaryFormatter();
+#pragma warning disable SYSLIB0011
formatter.Serialize(stream, attributes);
+#pragma warning restore SYSLIB0011
_oldData = stream.ToArray();
}
editor.Select(new Proxy
@@ -141,8 +147,13 @@ namespace FlaxEditor.Surface
}
using (var stream = new MemoryStream())
{
+ // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
+ using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
+
var formatter = new BinaryFormatter();
+#pragma warning disable SYSLIB0011
formatter.Serialize(stream, newValue);
+#pragma warning restore SYSLIB0011
var newData = stream.ToArray();
if (!_oldData.SequenceEqual(newData))
{
diff --git a/Source/Editor/Surface/SurfaceMeta.cs b/Source/Editor/Surface/SurfaceMeta.cs
index 05003223b..1b07256b1 100644
--- a/Source/Editor/Surface/SurfaceMeta.cs
+++ b/Source/Editor/Surface/SurfaceMeta.cs
@@ -4,6 +4,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection;
+using System.Runtime.Loader;
using System.Runtime.Serialization.Formatters.Binary;
using FlaxEngine;
@@ -54,8 +56,13 @@ namespace FlaxEditor.Surface
{
try
{
+ // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
+ using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
+
var formatter = new BinaryFormatter();
+#pragma warning disable SYSLIB0011
return (Attribute[])formatter.Deserialize(stream);
+#pragma warning restore SYSLIB0011
}
catch (Exception ex)
{
@@ -122,8 +129,13 @@ namespace FlaxEditor.Surface
}
using (var stream = new MemoryStream())
{
+ // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
+ using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
+
var formatter = new BinaryFormatter();
+#pragma warning disable SYSLIB0011
formatter.Serialize(stream, attributes);
+#pragma warning restore SYSLIB0011
AddEntry(AttributeMetaTypeID, stream.ToArray());
}
}
diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs
index 59d90c916..20d80db7a 100644
--- a/Source/Editor/Surface/SurfaceUtils.cs
+++ b/Source/Editor/Surface/SurfaceUtils.cs
@@ -10,6 +10,7 @@ using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.Scripting;
using FlaxEditor.Utilities;
+using FlaxEngine.Utilities;
using FlaxEngine;
namespace FlaxEditor.Surface
diff --git a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs
index 05c77ab3a..1d5977f3c 100644
--- a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs
+++ b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs
@@ -8,6 +8,7 @@ using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Utilities;
using FlaxEngine;
+using FlaxEngine.Utilities;
using Utils = FlaxEditor.Utilities.Utils;
namespace FlaxEditor.Surface
diff --git a/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs b/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs
index b6c7c160d..7b644623a 100644
--- a/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs
+++ b/Source/Editor/Tools/Terrain/Sculpt/NoiseMode.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using FlaxEngine;
+using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
namespace FlaxEditor.Tools.Terrain.Sculpt
diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs
index c9f3ac6e2..9c8a97f95 100644
--- a/Source/Editor/Windows/OutputLogWindow.cs
+++ b/Source/Editor/Windows/OutputLogWindow.cs
@@ -445,7 +445,7 @@ namespace FlaxEditor.Windows
int logCount;
do
{
- logCount = Editor.Internal_ReadOutputLogs(_outMessages, _outLogTypes, _outLogTimes);
+ logCount = Editor.Internal_ReadOutputLogs(ref _outMessages, ref _outLogTypes, ref _outLogTimes, OutCapacity);
for (int i = 0; i < logCount; i++)
{
diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs
index 23f74aa94..070b3b9cb 100644
--- a/Source/Editor/Windows/Profiler/CPU.cs
+++ b/Source/Editor/Windows/Profiler/CPU.cs
@@ -19,16 +19,16 @@ namespace FlaxEngine
{
get
{
- fixed (char* name = &Name0)
+ fixed (short* name = Name0)
{
- return new string(name);
+ return new string((char*)name);
}
}
}
internal unsafe bool NameStartsWith(string prefix)
{
- fixed (char* name = &Name0)
+ fixed (short* name = Name0)
{
fixed (char* p = prefix)
{
diff --git a/Source/Engine/Animations/AnimationGraph.cs b/Source/Engine/Animations/AnimationGraph.cs
index c337bcb6d..4bebecab0 100644
--- a/Source/Engine/Animations/AnimationGraph.cs
+++ b/Source/Engine/Animations/AnimationGraph.cs
@@ -3,6 +3,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
namespace FlaxEngine
{
@@ -44,6 +45,7 @@ namespace FlaxEngine
/// The node evaluation context structure.
///
[StructLayout(LayoutKind.Sequential)]
+ [NativeMarshalling(typeof(ContextMarshaler))]
public struct Context
{
///
@@ -92,6 +94,61 @@ namespace FlaxEngine
public AnimatedModel Instance;
}
+ [CustomMarshaller(typeof(Context), MarshalMode.Default, typeof(ContextMarshaler))]
+ internal static class ContextMarshaler
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ public struct ContextNative
+ {
+ public IntPtr Graph;
+ public IntPtr GraphExecutor;
+ public IntPtr Node;
+ public uint NodeId;
+ public int BoxId;
+ public float DeltaTime;
+ public ulong CurrentFrameIndex;
+ public IntPtr BaseModel;
+ public IntPtr Instance;
+ }
+
+ internal static Context ConvertToManaged(ContextNative unmanaged) => ToManaged(unmanaged);
+ internal static ContextNative ConvertToUnmanaged(Context managed) => ToNative(managed);
+
+ internal static Context ToManaged(ContextNative managed)
+ {
+ return new Context()
+ {
+ Graph = managed.Graph,
+ GraphExecutor = managed.GraphExecutor,
+ Node = managed.Node,
+ NodeId = managed.NodeId,
+ BoxId = managed.BoxId,
+ DeltaTime = managed.DeltaTime,
+ CurrentFrameIndex = managed.CurrentFrameIndex,
+ BaseModel = SkinnedModelMarshaller.ConvertToManaged(managed.BaseModel),
+ Instance = AnimatedModelMarshaller.ConvertToManaged(managed.Instance),
+ };
+ }
+ internal static ContextNative ToNative(Context managed)
+ {
+ return new ContextNative()
+ {
+ Graph = managed.Graph,
+ GraphExecutor = managed.GraphExecutor,
+ Node = managed.Node,
+ NodeId = managed.NodeId,
+ BoxId = managed.BoxId,
+ DeltaTime = managed.DeltaTime,
+ CurrentFrameIndex = managed.CurrentFrameIndex,
+ BaseModel = SkinnedModelMarshaller.ConvertToUnmanaged(managed.BaseModel),
+ Instance = AnimatedModelMarshaller.ConvertToUnmanaged(managed.Instance),
+ };
+ }
+ internal static void Free(ContextNative unmanaged)
+ {
+ }
+ }
+
///
/// The animation graph 'impulse' connections data container (the actual transfer is done via pointer as it gives better performance).
/// Container for skeleton nodes transformation hierarchy and any other required data.
@@ -203,14 +260,16 @@ namespace FlaxEngine
#region Internal Calls
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern bool Internal_HasConnection(ref CustomNode.Context context, int boxId);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_HasConnection")]
+ [return: MarshalAs(UnmanagedType.U1)]
+ internal static partial bool Internal_HasConnection(ref AnimationGraph.CustomNode.Context context, int boxId);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object Internal_GetInputValue(ref CustomNode.Context context, int boxId);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_GetInputValue")]
+ [return: MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))]
+ internal static partial object Internal_GetInputValue(ref AnimationGraph.CustomNode.Context context, int boxId);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr Internal_GetOutputImpulseData(ref CustomNode.Context context);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_GetOutputImpulseData")]
+ internal static partial IntPtr Internal_GetOutputImpulseData(ref AnimationGraph.CustomNode.Context context);
#endregion
}
diff --git a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp
index 45141bd94..a975736a6 100644
--- a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp
+++ b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp
@@ -13,8 +13,8 @@
#include "Engine/Content/Assets/SkinnedModel.h"
#if USE_MONO
-
#include
+#endif
struct InternalInitData
{
@@ -52,6 +52,7 @@ namespace AnimGraphInternal
{
bool HasConnection(InternalContext* context, int32 boxId)
{
+ SCRIPTING_EXPORT("FlaxEngine.AnimationGraph::Internal_HasConnection")
const auto box = context->Node->TryGetBox(boxId);
if (box == nullptr)
DebugLog::ThrowArgumentOutOfRange("boxId");
@@ -60,6 +61,7 @@ namespace AnimGraphInternal
MonoObject* GetInputValue(InternalContext* context, int32 boxId)
{
+ SCRIPTING_EXPORT("FlaxEngine.AnimationGraph::Internal_GetInputValue")
const auto box = context->Node->TryGetBox(boxId);
if (box == nullptr)
DebugLog::ThrowArgumentOutOfRange("boxId");
@@ -77,14 +79,13 @@ namespace AnimGraphInternal
AnimGraphImpulse* GetOutputImpulseData(InternalContext* context)
{
+ SCRIPTING_EXPORT("FlaxEngine.AnimationGraph::Internal_GetOutputImpulseData")
const auto nodes = context->Node->GetNodes(context->GraphExecutor);
context->GraphExecutor->InitNodes(nodes);
return nodes;
}
}
-#endif
-
void AnimGraphExecutor::initRuntime()
{
#if USE_MONO
@@ -122,7 +123,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
internalContext.Instance = context.Data->Object ? context.Data->Object->GetOrCreateManagedInstance() : nullptr;
// Peek managed object
- const auto obj = mono_gchandle_get_target(data.Handle);
+ const auto obj = MUtils::GetGCHandleTarget(data.Handle);
if (obj == nullptr)
{
LOG(Warning, "Custom node instance is null.");
@@ -166,7 +167,7 @@ void AnimGraph::ClearCustomNode(Node* node)
if (data.Handle)
{
#if USE_MONO
- mono_gchandle_free(data.Handle);
+ MUtils::FreeGCHandle(data.Handle);
#endif
data.Handle = 0;
}
@@ -216,7 +217,7 @@ bool AnimGraph::InitCustomNode(Node* node)
// Allocate managed node object (create GC handle to prevent destruction)
const auto obj = type->CreateInstance();
- const auto handleGC = mono_gchandle_new(obj, false);
+ const auto handleGC = MUtils::NewGCHandle(obj, false);
// Initialize node
InternalInitData initData;
@@ -228,7 +229,7 @@ bool AnimGraph::InitCustomNode(Node* node)
load->Invoke(obj, params, &exception);
if (exception)
{
- mono_gchandle_free(handleGC);
+ MUtils::FreeGCHandle(handleGC);
MException ex(exception);
ex.Log(LogType::Warning, TEXT("AnimGraph"));
diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h
index 74b512303..69855a2ba 100644
--- a/Source/Engine/Animations/Graph/AnimGraph.h
+++ b/Source/Engine/Animations/Graph/AnimGraph.h
@@ -516,7 +516,7 @@ public:
///
/// The GC handle to the managed instance of the node object.
///
- uint32 Handle;
+ gchandle Handle;
};
struct CurveData
diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp
index a7aeeab3d..502228824 100644
--- a/Source/Engine/Content/Asset.cpp
+++ b/Source/Engine/Content/Asset.cpp
@@ -13,6 +13,7 @@
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ConcurrentTaskQueue.h"
#if USE_MONO
+#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include
#endif
@@ -270,7 +271,7 @@ void Asset::OnManagedInstanceDeleted()
if (_gcHandle)
{
#if USE_MONO
- mono_gchandle_free(_gcHandle);
+ MUtils::FreeGCHandle(_gcHandle);
#endif
_gcHandle = 0;
}
diff --git a/Source/Engine/Core/Compiler.h b/Source/Engine/Core/Compiler.h
index fa6b77d05..c8ac8c1c9 100644
--- a/Source/Engine/Core/Compiler.h
+++ b/Source/Engine/Core/Compiler.h
@@ -93,3 +93,14 @@
#endif
#define PACK_STRUCT(__Declaration__) PACK_BEGIN() __Declaration__ PACK_END()
+#define SCRIPTING_EXPORT(name) __pragma(comment(linker, "/EXPORT:" #name "=" __FUNCDNAME__))
+#define SCRIPTING_EXPORT_DEBUG(name) __pragma(message("/EXPORT:" #name "=" __FUNCDNAME__)) \
+ __pragma(comment(linker, "/EXPORT:" #name "=" __FUNCDNAME__))
+
+// TODO: try this one with clang:
+//#ifdef _MSC_VER
+//#define SCRIPTING_EXPORT(name) __pragma(comment(linker, "/EXPORT:" #name "=" __FUNCDNAME__))
+//#endif
+//#ifdef __GNUC__
+//#define SCRIPTING_EXPORT(name) asm(".section .drectve\n\t.ascii \" -export:" #name "=" __FUNCDNAME__ "\"");
+//#endif
diff --git a/Source/Engine/Core/Config/GameSettings.cs b/Source/Engine/Core/Config/GameSettings.cs
index eeb1d76b6..1145db264 100644
--- a/Source/Engine/Core/Config/GameSettings.cs
+++ b/Source/Engine/Core/Config/GameSettings.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
@@ -562,8 +563,8 @@ namespace FlaxEditor.Content.Settings
///
/// Loads the current game settings asset and applies it to the engine runtime configuration.
///
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void Apply();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Settings.GameSettings::Apply")]
+ public static partial void Apply();
#endif
}
}
diff --git a/Source/Engine/Core/Config/LayersAndTagsSettings.cs b/Source/Engine/Core/Config/LayersAndTagsSettings.cs
index 0396b7588..a6003595c 100644
--- a/Source/Engine/Core/Config/LayersAndTagsSettings.cs
+++ b/Source/Engine/Core/Config/LayersAndTagsSettings.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
@@ -24,14 +26,26 @@ namespace FlaxEditor.Content.Settings
/// Gets the current tags collection.
///
/// The tags collection.
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string[] GetCurrentTags();
+ internal static string[] GetCurrentTags()
+ {
+ return GetCurrentTags(out int _);
+ }
///
/// Gets the current layer names (max 32 items but trims last empty items).
///
/// The layers.
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern string[] GetCurrentLayers();
+ public static string[] GetCurrentLayers()
+ {
+ return GetCurrentLayers(out int _);
+ }
+
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "tagCount")]
+ internal static partial string[] GetCurrentTags(out int tagCount);
+
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "layerCount")]
+ internal static partial string[] GetCurrentLayers(out int layerCount);
}
}
diff --git a/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs b/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs
index 2cf55e03e..4ad7ebfc0 100644
--- a/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/ColorConverter.cs
@@ -18,6 +18,16 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
@@ -25,9 +35,9 @@ namespace FlaxEngine.TypeConverters
{
string[] v = str.Split(',');
if (v.Length == 4)
- return new Color(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
+ return new Color(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
if (v.Length == 3)
- return new Color(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), 1.0f);
+ return new Color(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), 1.0f);
throw new FormatException("Invalid Color value format.");
}
return base.ConvertFrom(context, culture, value);
@@ -39,7 +49,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Color)value;
- return v.R + "," + v.G + "," + v.B + "," + v.A;
+ return v.R.ToString(culture) + "," + v.G.ToString(culture) + "," + v.B.ToString(culture) + "," + v.A.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs
index bde9d5bd5..3576862c0 100644
--- a/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Double2Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Double2(double.Parse(v[0]), double.Parse(v[1]));
+ return new Double2(double.Parse(v[0], culture), double.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Double2)value;
- return v.X + "," + v.Y;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs
index ccd27261f..33c4748ae 100644
--- a/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Double3Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Double3(double.Parse(v[0]), double.Parse(v[1]), double.Parse(v[2]));
+ return new Double3(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Double3)value;
- return v.X + "," + v.Y + "," + v.Z;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs
index ade244eb4..3123ffa42 100644
--- a/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Double4Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Double4(double.Parse(v[0]), double.Parse(v[1]), double.Parse(v[2]), double.Parse(v[3]));
+ return new Double4(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture), double.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Double4)value;
- return v.X + "," + v.Y + "," + v.Z + "," + v.W;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs
index eb7041058..41256852e 100644
--- a/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Float2Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Float2(float.Parse(v[0]), float.Parse(v[1]));
+ return new Float2(float.Parse(v[0], culture), float.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Float2)value;
- return v.X + "," + v.Y;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs
index f6a7a5f73..a6de0b63c 100644
--- a/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Float3Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Float3(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]));
+ return new Float3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Float3)value;
- return v.X + "," + v.Y + "," + v.Z;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs
index dd69a5bea..611e0b499 100644
--- a/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Float4Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Float4(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
+ return new Float4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Float4)value;
- return v.X + "," + v.Y + "," + v.Z + "," + v.W;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs
index 02b2b9a0c..30d6eac29 100644
--- a/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Int2Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Int2(int.Parse(v[0]), int.Parse(v[1]));
+ return new Int2(int.Parse(v[0], culture), int.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Int2)value;
- return v.X + "," + v.Y;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs
index 78f3fe2e7..2e029adcf 100644
--- a/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Int3Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Int3(int.Parse(v[0]), int.Parse(v[1]), int.Parse(v[2]));
+ return new Int3(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Int3)value;
- return v.X + "," + v.Y + "," + v.Z;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs
index 5f837e5ea..a222b6e3f 100644
--- a/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Int4Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Int4(int.Parse(v[0]), int.Parse(v[1]), int.Parse(v[2]), int.Parse(v[3]));
+ return new Int4(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture), int.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Int4)value;
- return v.X + "," + v.Y + "," + v.Z + "," + v.W;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs b/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs
index 8cc95fe20..afdfb83f0 100644
--- a/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/QuaternionConverter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Quaternion(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
+ return new Quaternion(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Quaternion)value;
- return v.X + "," + v.Y + "," + v.Z + "," + v.W;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs b/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs
index 483e7a8ed..84bb24773 100644
--- a/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Vector2Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Vector2(float.Parse(v[0]), float.Parse(v[1]));
+ return new Vector2(float.Parse(v[0], culture), float.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Vector2)value;
- return v.X + "," + v.Y;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs b/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs
index 70336deb9..e02f620b6 100644
--- a/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Vector3Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Vector3(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]));
+ return new Vector3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Vector3)value;
- return v.X + "," + v.Y + "," + v.Z;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs b/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs
index 8fcd5ba34..b19b2eaad 100644
--- a/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs
+++ b/Source/Engine/Core/Math/TypeConverters/Vector4Converter.cs
@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
+ ///
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return false;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
///
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
- return new Vector4(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
+ return new Vector4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Vector4)value;
- return v.X + "," + v.Y + "," + v.Z + "," + v.W;
+ return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}
diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp
index 5bf8dbf88..3f47dd7a5 100644
--- a/Source/Engine/Core/Types/Variant.cpp
+++ b/Source/Engine/Core/Types/Variant.cpp
@@ -619,7 +619,11 @@ Variant::Variant(Asset* v)
Variant::Variant(_MonoObject* v)
: Type(VariantType::ManagedObject, v ? mono_object_get_class(v) : nullptr)
{
- AsUint = v ? mono_gchandle_new(v, true) : 0;
+#if USE_NETCORE
+ AsUint64 = v ? MUtils::NewGCHandle(v, true) : 0;
+#else
+ AsUint = v ? MUtils::NewGCHandle(v, true) : 0;
+#endif
}
#else
@@ -957,9 +961,13 @@ Variant::~Variant()
Delete(AsDictionary);
break;
case VariantType::ManagedObject:
-#if USE_MONO
+#if USE_NETCORE
+ if (AsUint64)
+ MUtils::FreeGCHandle(AsUint64);
+ break;
+#elif USE_MONO
if (AsUint)
- mono_gchandle_free(AsUint);
+ MUtils::FreeGCHandle(AsUint);
break;
#endif
default: ;
@@ -1088,8 +1096,10 @@ Variant& Variant::operator=(const Variant& other)
AsDictionary = New>(*other.AsDictionary);
break;
case VariantType::ManagedObject:
-#if USE_MONO
- AsUint = other.AsUint ? mono_gchandle_new(mono_gchandle_get_target(other.AsUint), true) : 0;
+#if USE_NETCORE
+ AsUint64 = other.AsUint64 ? MUtils::NewGCHandle(MUtils::GetGCHandleTarget(other.AsUint64), true) : 0;
+#elif USE_MONO
+ AsUint = other.AsUint ? MUtils::NewGCHandle(MUtils::GetGCHandleTarget(other.AsUint), true) : 0;
#endif
break;
case VariantType::Null:
@@ -1217,7 +1227,7 @@ bool Variant::operator==(const Variant& other) const
case VariantType::ManagedObject:
#if USE_MONO
// TODO: invoke C# Equality logic?
- return AsUint == other.AsUint || mono_gchandle_get_target(AsUint) == mono_gchandle_get_target(other.AsUint);
+ return AsUint == other.AsUint || MUtils::GetGCHandleTarget(AsUint) == MUtils::GetGCHandleTarget(other.AsUint);
#endif
default:
return false;
@@ -1308,8 +1318,10 @@ Variant::operator bool() const
case VariantType::Asset:
return AsAsset != nullptr;
case VariantType::ManagedObject:
-#if USE_MONO
- return AsUint != 0 && mono_gchandle_get_target(AsUint) != nullptr;
+#if USE_NETCORE
+ return AsUint64 != 0 && MUtils::GetGCHandleTarget(AsUint64) != nullptr;
+#elif USE_MONO
+ return AsUint != 0 && MUtils::GetGCHandleTarget(AsUint) != nullptr;
#endif
default:
return false;
@@ -1578,8 +1590,10 @@ Variant::operator void*() const
case VariantType::Blob:
return AsBlob.Data;
case VariantType::ManagedObject:
-#if USE_MONO
- return AsUint ? mono_gchandle_get_target(AsUint) : nullptr;
+#if USE_NETCORE
+ return AsUint64 ? MUtils::GetGCHandleTarget(AsUint64) : nullptr;
+#elif USE_MONO
+ return AsUint ? MUtils::GetGCHandleTarget(AsUint) : nullptr;
#endif
default:
return nullptr;
@@ -1623,8 +1637,10 @@ Variant::operator ScriptingObject*() const
Variant::operator _MonoObject*() const
{
-#if USE_MONO
- return Type.Type == VariantType::ManagedObject && AsUint ? mono_gchandle_get_target(AsUint) : nullptr;
+#if USE_NETCORE
+ return Type.Type == VariantType::ManagedObject && AsUint64 ? MUtils::GetGCHandleTarget(AsUint64) : nullptr;
+#elif USE_MONO
+ return Type.Type == VariantType::ManagedObject && AsUint ? MUtils::GetGCHandleTarget(AsUint) : nullptr;
#else
return nullptr;
#endif
@@ -2337,9 +2353,14 @@ void Variant::SetType(const VariantType& type)
Delete(AsDictionary);
break;
case VariantType::ManagedObject:
-#if USE_MONO
+#if USE_NETCORE
+ if (AsUint64)
+ MUtils::FreeGCHandle(AsUint64);
+ break;
+#elif USE_MONO
if (AsUint)
- mono_gchandle_free(AsUint);
+ MUtils::FreeGCHandle(AsUint);
+ break;
#endif
break;
default: ;
@@ -2447,9 +2468,14 @@ void Variant::SetType(VariantType&& type)
Delete(AsDictionary);
break;
case VariantType::ManagedObject:
-#if USE_MONO
+#if USE_NETCORE
+ if (AsUint64)
+ MUtils::FreeGCHandle(AsUint64);
+ break;
+#elif USE_MONO
if (AsUint)
- mono_gchandle_free(AsUint);
+ MUtils::FreeGCHandle(AsUint);
+ break;
#endif
break;
default: ;
@@ -2632,7 +2658,11 @@ void Variant::SetManagedObject(_MonoObject* object)
{
if (Type.Type != VariantType::ManagedObject)
SetType(VariantType(VariantType::ManagedObject, mono_object_get_class(object)));
- AsUint = mono_gchandle_new(object, true);
+#if USE_NETCORE
+ AsUint64 = MUtils::NewGCHandle(object, true);
+#else
+ AsUint = MUtils::NewGCHandle(object, true);
+#endif
}
else
{
@@ -2751,8 +2781,10 @@ String Variant::ToString() const
case VariantType::Typename:
return String((const char*)AsBlob.Data, AsBlob.Length ? AsBlob.Length - 1 : 0);
case VariantType::ManagedObject:
-#if USE_MONO
- return AsUint ? String(MUtils::ToString(mono_object_to_string(mono_gchandle_get_target(AsUint), nullptr))) : TEXT("null");
+#if USE_NETCORE
+ return AsUint64 ? String(MUtils::ToString(mono_object_to_string(MUtils::GetGCHandleTarget(AsUint64), nullptr))) : TEXT("null");
+#elif USE_MONO
+ return AsUint ? String(MUtils::ToString(mono_object_to_string(MUtils::GetGCHandleTarget(AsUint), nullptr))) : TEXT("null");
#endif
default:
return String::Empty;
@@ -3671,7 +3703,12 @@ void Variant::AllocStructure()
Platform::MemoryCopy(AsBlob.Data, data, AsBlob.Length);
#else
Type.Type = VariantType::ManagedObject;
- AsUint = mono_gchandle_new(instance, true);
+
+#if USE_NETCORE
+ AsUint64 = MUtils::NewGCHandle(instance, true);
+#else
+ AsUint = MUtils::NewGCHandle(instance, true);
+#endif
#endif
}
else
@@ -3763,8 +3800,10 @@ uint32 GetHash(const Variant& key)
case VariantType::Typename:
return GetHash((const char*)key.AsBlob.Data);
case VariantType::ManagedObject:
-#if USE_MONO
- return key.AsUint ? (uint32)mono_object_hash(mono_gchandle_get_target(key.AsUint)) : 0;
+#if USE_NETCORE
+ return key.AsUint64 ? (uint32)mono_object_hash(MUtils::GetGCHandleTarget(key.AsUint64)) : 0;
+#elif USE_MONO
+ return key.AsUint ? (uint32)mono_object_hash(MUtils::GetGCHandleTarget(key.AsUint)) : 0;
#endif
default:
return 0;
diff --git a/Source/Engine/Engine/DebugLogHandler.cs b/Source/Engine/Engine/DebugLogHandler.cs
index b7bc0d52e..288c5e0d0 100644
--- a/Source/Engine/Engine/DebugLogHandler.cs
+++ b/Source/Engine/Engine/DebugLogHandler.cs
@@ -4,10 +4,12 @@ using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Security;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
namespace FlaxEngine
{
- internal sealed class DebugLogHandler : ILogHandler
+ internal partial class DebugLogHandler : ILogHandler
{
///
/// Occurs on sending a log message.
@@ -64,14 +66,14 @@ namespace FlaxEngine
Debug.Logger.LogException(exception);
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_LogWrite(LogType level, string msg);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.DebugLogHandler::Internal_LogWrite", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_LogWrite(LogType level, string msg);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_Log(LogType level, string msg, IntPtr obj, string stackTrace);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.DebugLogHandler::Internal_Log", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_Log(LogType level, string msg, IntPtr obj, string stackTrace);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_LogException(Exception exception, IntPtr obj);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.DebugLogHandler::Internal_LogException", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_LogException([MarshalUsing(typeof(FlaxEngine.ExceptionMarshaller))] Exception exception, IntPtr obj);
[SecuritySafeCritical]
public static string Internal_GetStackTrace()
diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs
new file mode 100644
index 000000000..fa6fdaa81
--- /dev/null
+++ b/Source/Engine/Engine/NativeInterop.cs
@@ -0,0 +1,2103 @@
+#if USE_NETCORE
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+using System.IO;
+using System.Runtime.CompilerServices;
+using FlaxEngine.Assertions;
+using FlaxEngine.Utilities;
+using System.Runtime.InteropServices.Marshalling;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using FlaxEngine.Visject;
+using System.Diagnostics;
+using System.Collections;
+using FlaxEditor.Content;
+
+#pragma warning disable CS1591
+#pragma warning disable CS8632
+
+[assembly: DisableRuntimeMarshalling]
+
+namespace FlaxEngine
+{
+ #region Native structures
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ManagedClass
+ {
+ internal IntPtr typeHandle;
+ internal IntPtr name;
+ internal IntPtr fullname;
+ internal IntPtr @namespace;
+ internal uint typeAttributes;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ClassMethod
+ {
+ internal IntPtr name;
+ internal int numParameters;
+ internal IntPtr typeHandle;
+ internal uint methodAttributes;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ClassField
+ {
+ internal IntPtr name;
+ internal IntPtr fieldHandle;
+ internal IntPtr fieldTypeHandle;
+ internal uint fieldAttributes;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ClassProperty
+ {
+ internal IntPtr name;
+ internal IntPtr getterHandle;
+ internal IntPtr setterHandle;
+ internal uint getterFlags;
+ internal uint setterFlags;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ClassAttribute
+ {
+ internal IntPtr name;
+ internal IntPtr attributeHandle;
+ internal IntPtr attributeTypeHandle;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ internal struct VariantNative
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct VariantNativeType
+ {
+ internal VariantUtils.VariantType types;
+ internal IntPtr TypeName; // char*
+ }
+
+ [FieldOffset(0)]
+ VariantNativeType Type;
+
+ [FieldOffset(8)]
+ byte AsBool;
+
+ [FieldOffset(8)]
+ short AsInt16;
+
+ [FieldOffset(8)]
+ ushort AsUint16;
+
+ [FieldOffset(8)]
+ int AsInt;
+
+ [FieldOffset(8)]
+ uint AsUint;
+
+ [FieldOffset(8)]
+ long AsInt64;
+
+ [FieldOffset(8)]
+ ulong AsUint64;
+
+ [FieldOffset(8)]
+ float AsFloat;
+
+ [FieldOffset(8)]
+ double AsDouble;
+
+ [FieldOffset(8)]
+ IntPtr AsPointer;
+
+ [FieldOffset(8)]
+ int AsData0;
+
+ [FieldOffset(12)]
+ int AsData1;
+
+ [FieldOffset(16)]
+ int AsData2;
+
+ [FieldOffset(20)]
+ int AsData3;
+
+ [FieldOffset(24)]
+ int AsData4;
+
+ [FieldOffset(28)]
+ int AsData5;
+ }
+
+#if false
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct GuidNative
+ {
+ internal int A;
+ internal short B;
+ internal short C;
+ internal byte D;
+ internal byte E;
+ internal byte F;
+ internal byte G;
+ internal byte H;
+ internal byte I;
+ internal byte J;
+ internal byte K;
+
+ internal GuidNative(Guid guid)
+ {
+ byte[] bytes = guid.ToByteArray();
+ A = MemoryMarshal.Cast(bytes.AsSpan())[0];
+ B = MemoryMarshal.Cast(bytes.AsSpan())[4];
+ C = MemoryMarshal.Cast(bytes.AsSpan())[6];
+ D = bytes[8];
+ E = bytes[9];
+ F = bytes[10];
+ G = bytes[11];
+ H = bytes[12];
+ I = bytes[13];
+ J = bytes[14];
+ K = bytes[15];
+ }
+
+ internal static implicit operator Guid(GuidNative guid) => new Guid(guid.A, guid.B, guid.C, guid.D, guid.E, guid.F, guid.G, guid.H, guid.I, guid.J, guid.K);
+ internal static explicit operator GuidNative(Guid guid) => new GuidNative(guid);
+ }
+#endif
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct VersionNative
+ {
+ internal int _Major;
+ internal int _Minor;
+ internal int _Build;
+ internal int _Revision;
+
+ internal VersionNative(Version ver)
+ {
+ _Major = ver.Major;
+ _Minor = ver.Minor;
+ _Build = ver.Build;
+ _Revision = ver.Revision;
+ }
+
+ internal Version GetVersion()
+ {
+ return new Version(_Major, _Minor, _Build, _Revision);
+ }
+ }
+
+ #endregion
+
+ #region Wrappers
+ ///
+ /// Wrapper for managed arrays that needs to be pinned.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ManagedArray
+ {
+ internal Array array = null;
+ internal GCHandle handle;
+ internal int elementSize;
+
+ internal ManagedArray(Array arr)
+ {
+ handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
+ elementSize = Marshal.SizeOf(arr.GetType().GetElementType());
+ array = arr;
+ }
+
+ internal void Release()
+ {
+ handle.Free();
+ array = null;
+ }
+
+ internal IntPtr PointerToPinnedArray => Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
+
+ internal int Length => array.Length;
+
+ internal static ManagedArray Get(ref T[] arr)
+ {
+ return new ManagedArray(arr);
+ }
+
+ internal static ManagedArray Get(Array arr)
+ {
+ ManagedArray managedArray = new ManagedArray(arr);
+ return managedArray;
+ }
+ }
+
+ internal static class ManagedString
+ {
+ [System.Diagnostics.DebuggerStepThrough]
+ internal static unsafe IntPtr ToNative(string str)
+ {
+ if (str == null)
+ return IntPtr.Zero;
+#if false
+ // HACK: Pin the string and pass the address to it including the length, no marshalling required
+ GCHandle handle = GCHandle.Alloc(str, GCHandleType.Pinned);
+ IntPtr ptr = handle.AddrOfPinnedObject() - sizeof(int);
+
+ pinnedStrings.TryAdd(ptr, handle);
+ return ptr;
+#endif
+
+#if false
+ // HACK: Return address to the allocated string structure which includes the string length.
+ // We assume the string content is copied in native side before GC frees it.
+ IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2);
+#endif
+ IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(str));
+ return ptr;
+ }
+
+ [System.Diagnostics.DebuggerStepThrough]
+ internal static string ToManaged(IntPtr ptr)
+ {
+ if (ptr == IntPtr.Zero)
+ return null;
+
+ return (string)GCHandle.FromIntPtr(ptr).Target;
+ }
+
+ [System.Diagnostics.DebuggerStepThrough]
+ internal static void Free(IntPtr ptr)
+ {
+ if (ptr == IntPtr.Zero)
+ return;
+
+ GCHandle.FromIntPtr(ptr).Free();
+ }
+ }
+
+ #endregion
+
+ #region Marshallers
+
+ [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedIn, typeof(GCHandleMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedOut, typeof(GCHandleMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(object), MarshalMode.ElementIn, typeof(GCHandleMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedOut, typeof(GCHandleMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedIn, typeof(GCHandleMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(object), MarshalMode.ElementOut, typeof(GCHandleMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(object), MarshalMode.ManagedToUnmanagedRef, typeof(GCHandleMarshaller.Bidirectional))]
+ [CustomMarshaller(typeof(object), MarshalMode.UnmanagedToManagedRef, typeof(GCHandleMarshaller.Bidirectional))]
+ [CustomMarshaller(typeof(object), MarshalMode.ElementRef, typeof(GCHandleMarshaller))]
+ internal static class GCHandleMarshaller
+ {
+ public static class NativeToManaged
+ {
+ public static object ConvertToManaged(IntPtr unmanaged) => unmanaged == IntPtr.Zero ? null : GCHandle.FromIntPtr(unmanaged).Target;
+ public static void Free(IntPtr unmanaged)
+ {
+ // This is a permanent handle, do not release it
+ }
+ }
+ public static class ManagedToNative
+ {
+ public static IntPtr ConvertToUnmanaged(object managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero;
+ public static void Free(IntPtr unmanaged)
+ {
+ if (unmanaged == IntPtr.Zero)
+ return;
+ GCHandle.FromIntPtr(unmanaged).Free();
+ }
+ }
+ public struct Bidirectional
+ {
+ object managed;
+ IntPtr unmanaged;
+ public void FromManaged(object managed) { this.managed = managed; }
+ public IntPtr ToUnmanaged() { unmanaged = GCHandleMarshaller.ToNative(managed); return unmanaged; }
+ public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; }
+ public object ToManaged() { managed = GCHandleMarshaller.ToManaged(unmanaged); unmanaged = IntPtr.Zero; return managed; }
+ public void Free()
+ {
+ // FIXME, might be a permanent handle or a temporary one
+ throw new NotImplementedException();
+ /*if (unmanaged == IntPtr.Zero)
+ return;
+ GCHandle.FromIntPtr(unmanaged).Free();*/
+ }
+ }
+ internal static object ConvertToManaged(IntPtr unmanaged) => ToManaged(unmanaged);
+ internal static IntPtr ConvertToUnmanaged(object managed) => ToNative(managed);
+ internal static object ToManaged(IntPtr managed) => managed != IntPtr.Zero ? GCHandle.FromIntPtr(managed).Target : null;
+ internal static IntPtr ToNative(object managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero;
+ internal static void Free(IntPtr unmanaged)
+ {
+ if (unmanaged == IntPtr.Zero)
+ return;
+ GCHandle.FromIntPtr(unmanaged).Free();
+ }
+ }
+
+ [CustomMarshaller(typeof(System.Type), MarshalMode.Default, typeof(SystemTypeMarshaller))]
+ internal static class SystemTypeMarshaller
+ {
+ internal static System.Type ConvertToManaged(IntPtr unmanaged) => (System.Type)GCHandleMarshaller.ConvertToManaged(unmanaged);
+
+ internal static IntPtr ConvertToUnmanaged(System.Type managed)
+ {
+ if (managed == null)
+ return IntPtr.Zero;
+
+ GCHandle handle = NativeInterop.GetOrAddTypeGCHandle(managed);
+ return GCHandle.ToIntPtr(handle);
+ }
+
+ internal static void Free(IntPtr unmanaged)
+ {
+ // Cached handle, do not release
+ }
+ }
+
+ [CustomMarshaller(typeof(Exception), MarshalMode.Default, typeof(ExceptionMarshaller))]
+ internal static class ExceptionMarshaller
+ {
+ internal static Exception ConvertToManaged(IntPtr unmanaged) => (Exception)GCHandleMarshaller.ConvertToManaged(unmanaged);
+ internal static IntPtr ConvertToUnmanaged(Exception managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
+ internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
+ }
+
+ [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedIn, typeof(ObjectMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedOut, typeof(ObjectMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementIn, typeof(ObjectMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ManagedToUnmanagedOut, typeof(ObjectMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.UnmanagedToManagedIn, typeof(ObjectMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(FlaxEngine.Object), MarshalMode.ElementOut, typeof(ObjectMarshaller.NativeToManaged))]
+ internal static class ObjectMarshaller
+ {
+ public static class NativeToManaged
+ {
+ public static FlaxEngine.Object ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (FlaxEngine.Object)GCHandle.FromIntPtr(unmanaged).Target : null;
+ }
+ public static class ManagedToNative
+ {
+ public static IntPtr ConvertToUnmanaged(FlaxEngine.Object managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero;
+ }
+ }
+
+ [CustomMarshaller(typeof(CultureInfo), MarshalMode.Default, typeof(CultureInfoMarshaller))]
+ internal static class CultureInfoMarshaller
+ {
+ internal static CultureInfo ConvertToManaged(IntPtr unmanaged) => (CultureInfo)GCHandleMarshaller.ConvertToManaged(unmanaged);
+ internal static IntPtr ConvertToUnmanaged(CultureInfo managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
+ internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
+ }
+
+ [CustomMarshaller(typeof(Array), MarshalMode.Default, typeof(SystemArrayMarshaller))]
+ internal static class SystemArrayMarshaller
+ {
+ internal static Array ConvertToManaged(IntPtr unmanaged) => (Array)GCHandleMarshaller.ConvertToManaged(unmanaged);
+ internal static IntPtr ConvertToUnmanaged(Array managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
+ internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
+ }
+
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedIn, typeof(DictionaryMarshaller<,>.ManagedToNative))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedOut, typeof(DictionaryMarshaller<,>.ManagedToNative))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementIn, typeof(DictionaryMarshaller<,>.ManagedToNative))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedOut, typeof(DictionaryMarshaller<,>.NativeToManaged))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedIn, typeof(DictionaryMarshaller<,>.NativeToManaged))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementOut, typeof(DictionaryMarshaller<,>.NativeToManaged))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ManagedToUnmanagedRef, typeof(DictionaryMarshaller<,>.Bidirectional))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.UnmanagedToManagedRef, typeof(DictionaryMarshaller<,>.Bidirectional))]
+ [CustomMarshaller(typeof(Dictionary<,>), MarshalMode.ElementRef, typeof(DictionaryMarshaller<,>))]
+ internal static unsafe class DictionaryMarshaller
+ {
+ public static class NativeToManaged
+ {
+ public static Dictionary ConvertToManaged(IntPtr unmanaged) => DictionaryMarshaller.ToManaged(unmanaged);
+ public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged);
+ }
+ public static class ManagedToNative
+ {
+ public static IntPtr ConvertToUnmanaged(Dictionary managed) => DictionaryMarshaller.ToNative(managed);
+ public static void Free(IntPtr unmanaged) => DictionaryMarshaller.Free(unmanaged);
+ }
+
+ public struct Bidirectional
+ {
+ Dictionary managed;
+ IntPtr unmanaged;
+ public void FromManaged(Dictionary managed) { this.managed = managed; }
+ public IntPtr ToUnmanaged() { unmanaged = DictionaryMarshaller.ToNative(managed); return unmanaged; }
+ public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; }
+ public Dictionary ToManaged() { managed = DictionaryMarshaller.ToManaged(unmanaged); unmanaged = IntPtr.Zero; return managed; }
+ public void Free() => DictionaryMarshaller.Free(unmanaged);
+ }
+
+ public static Dictionary ConvertToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (Dictionary)GCHandle.FromIntPtr(unmanaged).Target : null;
+ public static IntPtr ConvertToUnmanaged(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero;
+
+ public static Dictionary ToManaged(IntPtr unmanaged) => unmanaged != IntPtr.Zero ? (Dictionary)GCHandle.FromIntPtr(unmanaged).Target : null;
+ public static IntPtr ToNative(Dictionary managed) => managed != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed)) : IntPtr.Zero;
+ public static void Free(IntPtr unmanaged)
+ {
+ if (unmanaged != IntPtr.Zero)
+ GCHandle.FromIntPtr(unmanaged).Free();
+ }
+ }
+
+ [CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.Default, typeof(ArrayMarshaller<,>))]
+ [ContiguousCollectionMarshaller]
+ internal static unsafe class ArrayMarshaller
+ where TUnmanagedElement : unmanaged
+ {
+ internal static TUnmanagedElement* AllocateContainerForUnmanagedElements(T[]? managed, out int numElements)
+ {
+ if (managed is null)
+ {
+ numElements = 0;
+ return null;
+ }
+
+ numElements = managed.Length;
+
+ ManagedArray array = ManagedArray.Get(new TUnmanagedElement[numElements]);
+ var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(array));
+
+ return (TUnmanagedElement*)ptr;
+ }
+
+ internal static ReadOnlySpan GetManagedValuesSource(T[]? managed)
+ => managed;
+
+ internal static Span GetUnmanagedValuesDestination(TUnmanagedElement* unmanaged, int numElements)
+ {
+ if (unmanaged == null)
+ return Span.Empty;
+
+ ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
+ return new Span(array.array as TUnmanagedElement[]);
+ }
+
+ internal static T[]? AllocateContainerForManagedElements(TUnmanagedElement* unmanaged, int numElements)
+ {
+ if (unmanaged is null)
+ return null;
+
+ ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanaged)).Target;
+ return new T[numElements];
+ }
+
+ internal static Span GetManagedValuesDestination(T[]? managed)
+ => managed;
+
+ internal static ReadOnlySpan GetUnmanagedValuesSource(TUnmanagedElement* unmanagedValue, int numElements)
+ {
+ if (unmanagedValue == null)
+ return ReadOnlySpan.Empty;
+
+ ManagedArray array = (ManagedArray)GCHandle.FromIntPtr(new IntPtr(unmanagedValue)).Target;
+ return new ReadOnlySpan(array.array as TUnmanagedElement[]);
+ }
+
+ internal static void Free(TUnmanagedElement* unmanaged)
+ {
+ if (unmanaged == null)
+ return;
+
+ GCHandle handle = GCHandle.FromIntPtr(new IntPtr(unmanaged));
+ Unsafe.Unbox(handle.Target).Release();
+ handle.Free();
+ }
+
+ internal static ref T GetPinnableReference(T[]? array)
+ {
+ if (array is null)
+ return ref Unsafe.NullRef();
+
+ ManagedArray managedArray = ManagedArray.Get(array);
+ var ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managedArray));
+ return ref Unsafe.AsRef(ptr.ToPointer());
+ }
+ }
+
+ [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(StringMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(StringMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(string), MarshalMode.ElementIn, typeof(StringMarshaller.ManagedToNative))]
+ [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(StringMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(StringMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(string), MarshalMode.ElementOut, typeof(StringMarshaller.NativeToManaged))]
+ [CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedRef, typeof(StringMarshaller.Bidirectional))]
+ [CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedRef, typeof(StringMarshaller.Bidirectional))]
+ [CustomMarshaller(typeof(string), MarshalMode.ElementRef, typeof(StringMarshaller))]
+ internal static class StringMarshaller
+ {
+ public static class NativeToManaged
+ {
+ public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
+ public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
+ }
+ public static class ManagedToNative
+ {
+ public static unsafe IntPtr ConvertToUnmanaged(string managed)
+ {
+ if (managed == null)
+ return IntPtr.Zero;
+#if false
+ // HACK: Pin the string and pass the address to it including the length, no marshalling required
+ GCHandle handle = GCHandle.Alloc(managed, GCHandleType.Pinned);
+ IntPtr ptr = handle.AddrOfPinnedObject() - sizeof(int);
+
+ pinnedStrings.TryAdd(ptr, handle);
+ return ptr;
+#endif
+
+#if false
+ // HACK: Return address to the allocated string structure which includes the string length.
+ // We assume the string content is copied in native side before GC frees it.
+ IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref managed)) + sizeof(int) * 2);
+#endif
+ IntPtr ptr = GCHandle.ToIntPtr(GCHandle.Alloc(managed, GCHandleType.Pinned));
+ return ptr;
+ }
+ public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
+ }
+ public struct Bidirectional
+ {
+ string managed;
+ IntPtr unmanaged;
+ public void FromManaged(string managed) { this.managed = managed; }
+ public IntPtr ToUnmanaged() { unmanaged = ManagedString.ToNative(managed); return unmanaged; }
+ public void FromUnmanaged(IntPtr unmanaged) { this.unmanaged = unmanaged; }
+ public string ToManaged() { managed = ManagedString.ToManaged(unmanaged); unmanaged = IntPtr.Zero; return managed; }
+ public void Free() => ManagedString.Free(unmanaged);
+ }
+ internal static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
+ internal static IntPtr ConvertToUnmanaged(string managed) => ManagedString.ToNative(managed);
+ internal static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
+
+ internal static string ToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
+ internal static IntPtr ToNative(string managed) => ManagedString.ToNative(managed);
+ }
+
+#endregion
+
+ ///
+ /// Provides a Mono-like API for native code to access managed runtime.
+ ///
+ internal unsafe static partial class NativeInterop
+ {
+ internal static Dictionary AssemblyLocations = new Dictionary();
+
+ private static bool firstAssemblyLoaded = false;
+
+ private static Dictionary typeCache = new Dictionary();
+
+ private static Dictionary marshallableStructCache = new Dictionary();
+
+ private static IntPtr boolTruePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)1, GCHandleType.Pinned));
+ private static IntPtr boolFalsePtr = GCHandle.ToIntPtr(GCHandle.Alloc((int)0, GCHandleType.Pinned));
+
+ private static List methodHandles = new();
+ private static List methodHandlesCollectible = new();
+ private static Dictionary cachedDelegates = new Dictionary();
+ private static Dictionary cachedDelegatesCollectible = new Dictionary();
+ private static Dictionary typeHandleCache = new Dictionary();
+ private static Dictionary typeHandleCacheCollectible = new Dictionary();
+ private static List fieldHandleCache = new();
+ private static List fieldHandleCacheCollectible = new();
+ private static Dictionary assemblyHandles = new Dictionary();
+
+ private static string hostExecutable;
+ private static IntPtr hostExecutableHandle = IntPtr.Zero;
+ private static AssemblyLoadContext scriptingAssemblyLoadContext;
+
+ [System.Diagnostics.DebuggerStepThrough]
+ private static IntPtr InternalDllResolver(string libraryName, Assembly assembly, DllImportSearchPath? dllImportSearchPath)
+ {
+ if (libraryName == "FlaxEngine")
+ {
+ if (hostExecutableHandle == IntPtr.Zero)
+ hostExecutableHandle = NativeLibrary.Load(hostExecutable, assembly, dllImportSearchPath);
+ return hostExecutableHandle;
+ }
+ return IntPtr.Zero;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void Init(IntPtr hostExecutableName)
+ {
+ hostExecutable = Marshal.PtrToStringUni(hostExecutableName);
+ NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), InternalDllResolver);
+
+ // TODO: benchmark collectible setting performance, maybe enable it only in editor builds?
+ scriptingAssemblyLoadContext = new AssemblyLoadContext(null, true);
+
+ DelegateHelpers.Init();
+ }
+
+ internal static T[] GCHandleArrayToManagedArray(ManagedArray array)
+ {
+ IntPtr[] ptrArray = (IntPtr[])array.array;
+ T[] managedArray = new T[array.Length];
+ for (int i = 0; i < managedArray.Length; i++)
+ {
+ IntPtr ptr = ptrArray[i];
+ managedArray[i] = ptr != IntPtr.Zero ? (T)GCHandle.FromIntPtr(ptr).Target : default(T);
+ }
+ return managedArray;
+ }
+
+ internal static IntPtr[] ManagedArrayToGCHandleArray(Array array)
+ {
+ IntPtr[] pointerArray = new IntPtr[array.Length];
+ for (int i = 0; i < pointerArray.Length; i++)
+ {
+ var obj = array.GetValue(i);
+ pointerArray.SetValue(obj != null ? GCHandle.ToIntPtr(GCHandle.Alloc(obj)) : IntPtr.Zero, i);
+ }
+ return pointerArray;
+ }
+
+ internal static T[] NativeArrayToManagedArray(U[] nativeArray, Func toManagedFunc)
+ {
+ T[] managedArray = new T[nativeArray.Length];
+ if (nativeArray.Length > 0)
+ {
+ Assert.IsTrue(toManagedFunc != null);
+ for (int i = 0; i < nativeArray.Length; i++)
+ managedArray[i] = toManagedFunc(nativeArray[i]);
+ }
+
+ return managedArray;
+ }
+
+ private static Type FindType(string typeName)
+ {
+ if (typeCache.TryGetValue(typeName, out Type type))
+ return type;
+
+ type = Type.GetType(typeName, ResolveAssemblyByName, null);
+ if (type == null)
+ {
+ foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
+ {
+ type = assembly.GetType(typeName);
+ if (type != null)
+ break;
+ }
+ }
+
+ if (type == null)
+ {
+ string oldTypeName = typeName;
+ typeName = typeName.Substring(0, typeName.IndexOf(','));
+ type = Type.GetType(typeName, ResolveAssemblyByName, null);
+ if (type == null)
+ {
+ foreach (var assembly in scriptingAssemblyLoadContext.Assemblies)
+ {
+ type = assembly.GetType(typeName);
+ if (type != null)
+ break;
+ }
+ }
+ typeName = oldTypeName;
+ }
+
+ typeCache.Add(typeName, type);
+
+ return type;
+ }
+
+ private static Assembly ResolveAssemblyByName(AssemblyName assemblyName)
+ {
+ foreach (Assembly assembly in scriptingAssemblyLoadContext.Assemblies)
+ if (assembly.GetName() == assemblyName)
+ return assembly;
+ return null;
+ }
+
+ internal static bool IsBlittable(Type type)
+ {
+ if (type.IsPrimitive || type.IsEnum)
+ return true;
+ if (type.IsArray && IsBlittable(type.GetElementType()))
+ return true;
+ if (type.IsPointer && type.HasElementType && type.GetElementType().IsPrimitive)
+ return true;
+
+ if (type.IsClass)
+ return false;
+
+ if (type.IsValueType)
+ {
+ var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+ foreach (FieldInfo field in fields)
+ {
+ if (!IsBlittable(field.FieldType))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Returns blittable internal type for given type.
+ ///
+ internal static Type GetInternalType(Type type)
+ {
+ string[] splits = type.AssemblyQualifiedName.Split(',');
+ string @namespace = string.Join('.', splits[0].Split('.').SkipLast(1));
+ string className = @namespace.Length > 0 ? splits[0].Substring(@namespace.Length + 1) : splits[0];
+ string parentClassName = "";
+ if (className.Contains("+"))
+ {
+ parentClassName = className.Substring(0, className.LastIndexOf('+') + 1);
+ className = className.Substring(parentClassName.Length);
+ }
+ string marshallerName = className + "Marshaller";
+ string internalAssemblyQualifiedName = $"{@namespace}.{parentClassName}{marshallerName}+{className}Internal,{String.Join(',', splits.Skip(1))}";
+ return FindType(internalAssemblyQualifiedName);
+ }
+
+ internal static void MarshalFromObject(object obj, Type objectType, ref IntPtr targetPtr)
+ {
+ void EnsureAlignment(ref IntPtr ptr, int alignment)
+ {
+ int paddingSize = (int)(ptr.ToInt64() % alignment);
+ if (paddingSize != 0)
+ ptr = IntPtr.Add(ptr, alignment - paddingSize);
+ }
+
+ int readOffset = 0;
+ if (objectType == typeof(IntPtr))
+ {
+ EnsureAlignment(ref targetPtr, IntPtr.Size);
+ Marshal.WriteIntPtr(targetPtr, (IntPtr)obj);
+ readOffset = IntPtr.Size;
+ }
+ else if (objectType == typeof(byte))
+ {
+ EnsureAlignment(ref targetPtr, sizeof(byte));
+ Marshal.WriteByte(targetPtr, (byte)obj);
+ readOffset = sizeof(byte);
+ }
+ else if (objectType == typeof(bool))
+ {
+ EnsureAlignment(ref targetPtr, sizeof(byte));
+ Marshal.WriteByte(targetPtr, ((bool)obj) ? (byte)1 : (byte)0);
+ readOffset = sizeof(byte);
+ }
+ else if (objectType.IsEnum)
+ {
+ MarshalFromObject(obj, objectType.GetEnumUnderlyingType(), ref targetPtr);
+ }
+ else if (objectType == typeof(int) || objectType.Name == "Int32&")
+ {
+ EnsureAlignment(ref targetPtr, sizeof(int));
+ Marshal.WriteInt32(targetPtr, (int)obj);
+ readOffset = sizeof(int);
+ }
+ else if (objectType == typeof(long))
+ {
+ EnsureAlignment(ref targetPtr, sizeof(long));
+ Marshal.WriteInt64(targetPtr, (long)obj);
+ readOffset = sizeof(long);
+ }
+ else if (objectType == typeof(short))
+ {
+ EnsureAlignment(ref targetPtr, sizeof(short));
+ Marshal.WriteInt16(targetPtr, (short)obj);
+ readOffset = sizeof(short);
+ }
+ else if (objectType == typeof(string))
+ {
+ EnsureAlignment(ref targetPtr, IntPtr.Size);
+ IntPtr strPtr = ManagedString.ToNative(obj as string);
+ Marshal.WriteIntPtr(targetPtr, strPtr);
+ readOffset = IntPtr.Size;
+ }
+ else if (objectType.IsByRef)
+ throw new NotImplementedException();
+ else if (objectType.IsClass)
+ {
+ if (objectType.IsPointer)
+ {
+ EnsureAlignment(ref targetPtr, IntPtr.Size);
+ var unboxed = Pointer.Unbox(obj);
+ if (unboxed == null)
+ Marshal.WriteIntPtr(targetPtr, IntPtr.Zero);
+ else if (obj is FlaxEngine.Object fobj)
+ Marshal.WriteIntPtr(targetPtr, fobj.__unmanagedPtr);
+ else
+ Marshal.WriteIntPtr(targetPtr, GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Weak)));
+ readOffset = IntPtr.Size;
+ }
+ else
+ {
+ EnsureAlignment(ref targetPtr, IntPtr.Size);
+ Marshal.WriteIntPtr(targetPtr, obj != null ? GCHandle.ToIntPtr(GCHandle.Alloc(obj, GCHandleType.Weak)) : IntPtr.Zero);
+ readOffset = IntPtr.Size;
+ }
+ }
+ else if (objectType.IsValueType)
+ {
+ if (GetMarshallableStructFields(objectType, out FieldInfo[] fields))
+ {
+ IntPtr origPtr = targetPtr;
+
+ foreach (var field in fields)
+ MarshalFromObject(field.GetValue(obj), field.FieldType, ref targetPtr);
+
+ Assert.IsTrue((targetPtr-origPtr) == Marshal.SizeOf(objectType));
+ }
+ else
+ {
+ Marshal.StructureToPtr(obj, targetPtr, false);
+ readOffset = Marshal.SizeOf(objectType);
+ }
+ }
+ else
+ throw new NotImplementedException(objectType.FullName);
+
+ targetPtr = IntPtr.Add(targetPtr, readOffset);
+ }
+
+ private static bool GetMarshallableStructFields(Type type, out FieldInfo[] fields)
+ {
+ if (marshallableStructCache.TryGetValue(type, out fields))
+ return fields != null;
+
+ fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+ if (!fields.Any(x => x.FieldType.IsClass || x.FieldType.Name == "Boolean"))
+ fields = null;
+
+ marshallableStructCache.Add(type, fields);
+ return fields != null;
+ }
+
+ internal static object MarshalToObject(Type type, IntPtr ptr, out int readSize)
+ {
+ readSize = 0;
+ if (type.Name == "IntPtr&" || type.Name == "Byte*" || type.Name == "IntPtr")
+ {
+ readSize += IntPtr.Size;
+ return ptr;
+ }
+ else if (type.IsByRef)
+ {
+ return MarshalToObject(type.GetElementType(), ptr, out readSize);
+ }
+ else if (type == typeof(string))
+ {
+ readSize += IntPtr.Size;
+ return ManagedString.ToManaged(ptr);
+ }
+ else if (type == typeof(bool))
+ {
+ readSize += sizeof(byte);
+ return Marshal.ReadByte(ptr) != 0;
+ }
+ else if (type.IsEnum)
+ {
+ return Enum.ToObject(type, MarshalToObject(type.GetEnumUnderlyingType(), ptr, out readSize));
+ }
+ else if (type.IsArray)
+ {
+ readSize += IntPtr.Size;
+ if (ptr == IntPtr.Zero)
+ return null;
+
+ ManagedArray arr = (ManagedArray)GCHandle.FromIntPtr(ptr).Target;
+ if (type.Name == "Type[]")
+ {
+ var intPtrArr = arr.array as IntPtr[];
+ Type[] typeArr = intPtrArr.Select(x => GCHandle.FromIntPtr(x).Target).OfType().ToArray();
+ return typeArr;
+ }
+ if (arr.array.GetType().GetElementType() == typeof(string))
+ {
+ string[] strs = new string[arr.Length];
+ IntPtr[] ptrs = ((IntPtr[])arr.array);
+ for (int i = 0; i < strs.Length; i++)
+ strs[i] = ManagedString.ToManaged(ptrs[i]);
+ return strs;
+ }
+ return arr.array;
+ }
+ else if (type.IsClass)
+ {
+ readSize += IntPtr.Size;
+ if (ptr == IntPtr.Zero)
+ return null;
+
+ var obj = GCHandle.FromIntPtr(ptr).Target;
+ Assert.IsTrue(obj == null || obj.GetType() == type || obj.GetType().IsSubclassOf(type) || (obj.GetType().FullName == "System.RuntimeType" && type == typeof(Type)));
+ return obj;
+ }
+ else if (type.IsValueType)
+ {
+ if (GetMarshallableStructFields(type, out FieldInfo[] fields))
+ {
+ object target = RuntimeHelpers.GetUninitializedObject(type);
+ IntPtr fieldPtr = ptr;
+
+ foreach (var field in fields)
+ {
+ object fieldValue;
+ int size;
+
+ if (field.FieldType.IsClass || field.FieldType == typeof(IntPtr))
+ fieldValue = MarshalToObject(field.FieldType, Marshal.ReadIntPtr(fieldPtr), out size);
+ else
+ {
+ void EnsureAlignment(ref IntPtr ptr2, ref int readSize, int alignment)
+ {
+ int paddingSize = (int)(ptr2.ToInt64() % alignment);
+ if (paddingSize != 0)
+ {
+ int offset = alignment - paddingSize;
+ ptr2 = IntPtr.Add(ptr2, offset);
+ readSize += offset;
+ }
+ }
+
+ int fieldReadSize = 0;
+ int fieldSize;
+ Type fieldType = field.FieldType;
+ if (field.FieldType.IsEnum)
+ fieldType = field.FieldType.GetEnumUnderlyingType();
+ else if (field.FieldType == typeof(bool))
+ fieldType = typeof(byte);
+
+ if (fieldType.IsValueType && !fieldType.IsEnum && !fieldType.IsPrimitive) // Is it a structure?
+ fieldSize = 1;
+ else if (fieldType.IsClass || fieldType.IsPointer)
+ fieldSize = IntPtr.Size;
+ else
+ fieldSize = Marshal.SizeOf(fieldType);
+ EnsureAlignment(ref fieldPtr, ref fieldReadSize, fieldSize);
+
+ fieldValue = MarshalToObject(field.FieldType, fieldPtr, out size);
+ }
+
+ field.SetValue(target, fieldValue);
+
+ fieldPtr = IntPtr.Add(fieldPtr, size);
+ }
+
+ var totalReadSize = fieldPtr.ToInt64() - ptr.ToInt64();
+ Assert.IsTrue(totalReadSize == Marshal.SizeOf(type));
+ readSize += (int)totalReadSize;
+ return target;
+ }
+ }
+ else if (type.Name == "IDictionary")
+ {
+ readSize += IntPtr.Size;
+ if (ptr == IntPtr.Zero)
+ return null;
+
+ return GCHandle.FromIntPtr(ptr).Target;
+ }
+
+ readSize += Marshal.SizeOf(type);
+ return Marshal.PtrToStructure(ptr, type);
+ }
+
+ internal static GCHandle GetMethodGCHandle(MethodInfo method)
+ {
+ Type[] parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray();
+
+ GCHandle delegTupleHandle = GCHandle.Alloc(new Tuple(parameterTypes, method));
+
+ if (parameterTypes.Any(x => x.IsCollectible) || method.IsCollectible)
+ methodHandlesCollectible.Add(delegTupleHandle);
+ else
+ methodHandles.Add(delegTupleHandle);
+
+ return delegTupleHandle;
+ }
+
+ internal static GCHandle GetAssemblyHandle(Assembly assembly)
+ {
+ if (!assemblyHandles.TryGetValue(assembly, out GCHandle handle))
+ {
+ handle = GCHandle.Alloc(assembly);
+ assemblyHandles.Add(assembly, handle);
+ }
+ return handle;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetManagedClasses(IntPtr assemblyHandle, ManagedClass** managedClasses, int* managedClassCount)
+ {
+ Assembly assembly = (Assembly)GCHandle.FromIntPtr(assemblyHandle).Target;
+ var assemblyTypes = GetAssemblyTypes(assembly);
+
+ ManagedClass* arr = (ManagedClass*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * assemblyTypes.Length).ToPointer();
+
+ for (int i = 0; i < assemblyTypes.Length; i++)
+ {
+ var type = assemblyTypes[i];
+ IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i);
+ bool isStatic = type.IsAbstract && type.IsSealed;
+ bool isInterface = type.IsInterface;
+ bool isAbstract = type.IsAbstract;
+
+ GCHandle typeHandle = GetOrAddTypeGCHandle(type);
+
+ ManagedClass managedClass = new ManagedClass()
+ {
+ typeHandle = GCHandle.ToIntPtr(typeHandle),
+ name = Marshal.StringToCoTaskMemAnsi(type.Name),
+ fullname = Marshal.StringToCoTaskMemAnsi(type.FullName),
+ @namespace = Marshal.StringToCoTaskMemAnsi(type.Namespace ?? ""),
+ typeAttributes = (uint)type.Attributes,
+ };
+ Marshal.StructureToPtr(managedClass, ptr, false);
+ }
+
+ *managedClasses = arr;
+ *managedClassCount = assemblyTypes.Length;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe void GetManagedClassFromType(IntPtr typeHandle, ManagedClass* managedClass, IntPtr* assemblyHandle)
+ {
+ var type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+
+ GCHandle classTypeHandle = GetOrAddTypeGCHandle(type);
+
+ *managedClass = new ManagedClass()
+ {
+ typeHandle = GCHandle.ToIntPtr(classTypeHandle),
+ name = Marshal.StringToCoTaskMemAnsi(type.Name),
+ fullname = Marshal.StringToCoTaskMemAnsi(type.FullName),
+ @namespace = Marshal.StringToCoTaskMemAnsi(type.Namespace ?? ""),
+ typeAttributes = (uint)type.Attributes,
+ };
+ *assemblyHandle = GCHandle.ToIntPtr(GetAssemblyHandle(type.Assembly));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void GetClassMethods(IntPtr typeHandle, ClassMethod** classMethods, int* classMethodsCount)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+
+ List methods = new List();
+ var staticMethods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
+ var instanceMethods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
+ foreach (MethodInfo methodInfo in staticMethods)
+ methods.Add(methodInfo);
+ foreach (MethodInfo methodInfo in instanceMethods)
+ methods.Add(methodInfo);
+
+ ClassMethod* arr = (ClassMethod*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * methods.Count).ToPointer();
+ for (int i = 0; i < methods.Count; i++)
+ {
+ IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i);
+ ClassMethod classMethod = new ClassMethod()
+ {
+ name = Marshal.StringToCoTaskMemAnsi(methods[i].Name),
+ numParameters = methods[i].GetParameters().Length,
+ methodAttributes = (uint)methods[i].Attributes,
+ };
+ classMethod.typeHandle = GCHandle.ToIntPtr(GetMethodGCHandle(methods[i]));
+ Marshal.StructureToPtr(classMethod, ptr, false);
+ }
+ *classMethods = arr;
+ *classMethodsCount = methods.Count;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void GetClassFields(IntPtr typeHandle, ClassField** classFields, int* classFieldsCount)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+
+ ClassField* arr = (ClassField*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * fields.Length).ToPointer();
+ for (int i = 0; i < fields.Length; i++)
+ {
+ IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i);
+
+ GCHandle fieldHandle = GCHandle.Alloc(fields[i]);
+ if (type.IsCollectible)
+ fieldHandleCacheCollectible.Add(fieldHandle);
+ else
+ fieldHandleCache.Add(fieldHandle);
+
+ ClassField classField = new ClassField()
+ {
+ name = Marshal.StringToCoTaskMemAnsi(fields[i].Name),
+ fieldHandle = GCHandle.ToIntPtr(fieldHandle),
+ fieldTypeHandle = GCHandle.ToIntPtr(GetOrAddTypeGCHandle(fields[i].FieldType)),
+ fieldAttributes = (uint)fields[i].Attributes,
+ };
+ Marshal.StructureToPtr(classField, ptr, false);
+ }
+ *classFields = arr;
+ *classFieldsCount = fields.Length;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void GetClassProperties(IntPtr typeHandle, ClassProperty** classProperties, int* classPropertiesCount)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+
+ ClassProperty* arr = (ClassProperty*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * properties.Length).ToPointer();
+ for (int i = 0; i < properties.Length; i++)
+ {
+ IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i);
+
+ var getterMethod = properties[i].GetGetMethod(true);
+ var setterMethod = properties[i].GetSetMethod(true);
+
+ ClassProperty classProperty = new ClassProperty()
+ {
+ name = Marshal.StringToCoTaskMemAnsi(properties[i].Name),
+ };
+ if (getterMethod != null)
+ {
+ var getterHandle = GetMethodGCHandle(getterMethod);
+ classProperty.getterHandle = GCHandle.ToIntPtr(getterHandle);
+ }
+ if (setterMethod != null)
+ {
+ var setterHandle = GetMethodGCHandle(setterMethod);
+ classProperty.setterHandle = GCHandle.ToIntPtr(setterHandle);
+ }
+ Marshal.StructureToPtr(classProperty, ptr, false);
+ }
+ *classProperties = arr;
+ *classPropertiesCount = properties.Length;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void GetClassAttributes(IntPtr typeHandle, ClassAttribute** classAttributes, int* classAttributesCount)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ object[] attributeValues = type.GetCustomAttributes(false);
+ Type[] attributeTypes = type.GetCustomAttributes(false).Select(x => x.GetType()).ToArray();
+
+ ClassAttribute* arr = (ClassAttribute*)Marshal.AllocCoTaskMem(Unsafe.SizeOf() * attributeTypes.Length).ToPointer();
+ for (int i = 0; i < attributeTypes.Length; i++)
+ {
+ IntPtr ptr = IntPtr.Add(new IntPtr(arr), Unsafe.SizeOf() * i);
+
+ GCHandle attributeHandle = GCHandle.Alloc(attributeValues[i]);
+ GCHandle attributeTypeHandle = GetOrAddTypeGCHandle(attributeTypes[i]);
+
+ ClassAttribute classAttribute = new ClassAttribute()
+ {
+ name = Marshal.StringToCoTaskMemAnsi(attributeTypes[i].Name),
+ attributeTypeHandle = GCHandle.ToIntPtr(attributeTypeHandle),
+ attributeHandle = GCHandle.ToIntPtr(attributeHandle),
+ };
+ Marshal.StructureToPtr(classAttribute, ptr, false);
+ }
+ *classAttributes = arr;
+ *classAttributesCount = attributeTypes.Length;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void GetClassInterfaces(IntPtr typeHandle, IntPtr* classInterfaces, int* classInterfacesCount)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+
+ var interfaces = type.GetInterfaces();
+
+ // mono doesn't seem to return any interfaces, or returns only "immediate" interfaces? // FIXME?
+ //foreach (Type interfaceType in type.BaseType.GetInterfaces())
+ // interfaces.Remove(interfaceType.FullName);
+
+ IntPtr arr = Marshal.AllocCoTaskMem(IntPtr.Size * interfaces.Length);
+ for (int i = 0; i < interfaces.Length; i++)
+ {
+ IntPtr ptr = IntPtr.Add(arr, IntPtr.Size * i);
+
+ GCHandle handle = GetTypeGCHandle(interfaces[i]);
+ Marshal.WriteIntPtr(ptr, GCHandle.ToIntPtr(handle));
+ }
+ *classInterfaces = arr;
+ *classInterfacesCount = interfaces.Length;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetMethodReturnType(IntPtr methodHandle)
+ {
+ (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target;
+ Type returnType = methodInfo.ReturnType;
+
+ return GCHandle.ToIntPtr(GetTypeGCHandle(returnType));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void GetMethodParameterTypes(IntPtr methodHandle, IntPtr* typeHandles)
+ {
+ (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target;
+ Type returnType = methodInfo.ReturnType;
+
+ IntPtr arr = Marshal.AllocCoTaskMem(IntPtr.Size * parameterTypes.Length);
+ for (int i = 0; i < parameterTypes.Length; i++)
+ {
+ IntPtr ptr = IntPtr.Add(new IntPtr(arr), IntPtr.Size * i);
+
+ GCHandle typeHandle = GetOrAddTypeGCHandle(parameterTypes[i]);
+
+ Marshal.WriteIntPtr(ptr, GCHandle.ToIntPtr(typeHandle));
+ }
+ *typeHandles = arr;
+ }
+
+ ///
+ /// Returns pointer to the string's internal structure, containing the buffer and length of the string.
+ ///
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetStringPointer(IntPtr stringHandle)
+ {
+ string str = (string)GCHandle.FromIntPtr(stringHandle).Target;
+ IntPtr ptr = (Unsafe.Read(Unsafe.AsPointer(ref str)) + sizeof(int) * 2);
+ return ptr;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr NewObject(IntPtr typeHandle)
+ {
+ Type classType = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+
+ if (classType == typeof(Script))
+ {
+ // FIXME: Script is an abstract type which can not be instantiated
+ classType = typeof(VisjectScript);
+ }
+
+ object obj = RuntimeHelpers.GetUninitializedObject(classType);
+
+ IntPtr handle = GCHandle.ToIntPtr(GCHandle.Alloc(obj));
+ return handle;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr NewArray(IntPtr typeHandle, long size)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ Type elementType;
+ if (IsBlittable(type))
+ elementType = type;
+ else
+ elementType = GetInternalType(type) ?? typeof(IntPtr);
+ Array arr = Array.CreateInstance(elementType, size);
+ ManagedArray managedArray = new ManagedArray(arr);
+ return GCHandle.ToIntPtr(GCHandle.Alloc(managedArray/*, GCHandleType.Weak*/));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe IntPtr GetArrayPointerToElement(IntPtr arrayHandle, int size, int index)
+ {
+ object obj = GCHandle.FromIntPtr(arrayHandle).Target;
+ if (obj is ManagedArray managedArray)
+ {
+ if (managedArray.Length == 0)
+ return IntPtr.Zero;
+ Assert.IsTrue(index >= 0 && index < managedArray.Length);
+ IntPtr ptr = IntPtr.Add(managedArray.PointerToPinnedArray, size * index);
+ return ptr;
+ }
+ else
+ {
+ Array array = (Array)obj;
+ if (array.Length == 0)
+ return IntPtr.Zero;
+ Assert.IsTrue(index >= 0 && index < array.Length);
+ IntPtr ptr = Marshal.UnsafeAddrOfPinnedArrayElement(array, index);
+ return ptr;
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal static int GetArrayLength(IntPtr handlePtr)
+ {
+ object obj = GCHandle.FromIntPtr(handlePtr).Target;
+ if (obj is ManagedArray managedArray)
+ return managedArray.Length;
+ else
+ return ((Array)obj).Length;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetStringEmpty()
+ {
+ string str = "";
+ return GCHandle.ToIntPtr(GCHandle.Alloc(str));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr NewStringUTF16(char* text, int length)
+ {
+ string str = new string(new ReadOnlySpan(text, length));
+ return GCHandle.ToIntPtr(GCHandle.Alloc(str));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr NewString(sbyte* text)
+ {
+ string str = new string(text);
+ return GCHandle.ToIntPtr(GCHandle.Alloc(str));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr NewStringLength(sbyte* text, int length)
+ {
+ string str = new string(text, 0, length);
+ return GCHandle.ToIntPtr(GCHandle.Alloc(str));
+ }
+
+ ///
+ /// Creates a managed copy of the value, and stores it in a boxed reference.
+ ///
+ [UnmanagedCallersOnly]
+ internal static IntPtr BoxValue(IntPtr typeHandle, IntPtr valuePtr)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+
+ object value = MarshalToObject(type, valuePtr, out _);
+ return GCHandle.ToIntPtr(GCHandle.Alloc(value, GCHandleType.Weak));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetObjectType(IntPtr handle)
+ {
+ var obj = GCHandle.FromIntPtr(handle).Target;
+ Type classType = obj.GetType();
+ return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(classType));
+ }
+
+ internal unsafe class StructUnboxer where T : struct
+ {
+ public StructUnboxer(object obj, IntPtr ptr)
+ {
+ IntPtr* pin = (IntPtr*)ptr;
+ *pin = new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox(obj)));
+ }
+ }
+
+ [UnmanagedCallersOnly]
+ internal unsafe static IntPtr UnboxValue(IntPtr handlePtr)
+ {
+ GCHandle handle = GCHandle.FromIntPtr(handlePtr);
+ object obj = handle.Target;
+ Type type = obj.GetType();
+
+ if (!type.IsValueType)
+ return handlePtr;
+
+ // HACK: Get the address of a non-pinned value
+ IntPtr ptr = IntPtr.Zero;
+ IntPtr* addr = &ptr;
+ Activator.CreateInstance(typeof(StructUnboxer<>).MakeGenericType(type), obj, new IntPtr(addr));
+
+ return ptr;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void RaiseException(IntPtr exceptionHandle)
+ {
+ var exception = (Exception)GCHandle.FromIntPtr(exceptionHandle).Target;
+ throw exception;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void ObjectInit(IntPtr handle)
+ {
+ object obj = GCHandle.FromIntPtr(handle).Target;
+ Type classType = obj.GetType();
+
+ var ctors = classType.GetMember(".ctor", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ var ctor = ctors[0] as ConstructorInfo;
+ ctor.Invoke(obj, null);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr InvokeMethod(IntPtr instance, IntPtr methodHandle, IntPtr paramData, IntPtr exceptionPtr)
+ {
+ (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target;
+ int numParams = parameterTypes.Length;
+
+ var intPtrParams = stackalloc IntPtr[numParams];
+ var objParams = new object[numParams];
+
+ IntPtr curptr = paramData;
+ for (int i = 0; i < numParams; i++)
+ {
+ intPtrParams[i] = Marshal.ReadIntPtr(curptr);
+ curptr = IntPtr.Add(curptr, sizeof(IntPtr));
+ objParams[i] = MarshalToObject(parameterTypes[i], intPtrParams[i], out _);
+ }
+
+ object returnObject;
+ try
+ {
+ object inst = instance != IntPtr.Zero ? GCHandle.FromIntPtr(instance).Target : null;
+ returnObject = methodInfo.Invoke(inst, objParams);
+ }
+ catch (Exception exception)
+ {
+ // The internal exception thrown in MethodInfo.Invoke is caught here
+ Exception realException = exception;
+ if (exception.InnerException != null && exception.TargetSite.ReflectedType.Name == "MethodInvoker")
+ realException = exception.InnerException;
+
+ if (exceptionPtr != IntPtr.Zero)
+ Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(realException, GCHandleType.Weak)));
+ else
+ throw realException;
+ return IntPtr.Zero;
+ }
+
+ // Marshal reference parameters back to original unmanaged references
+ for (int i = 0; i < numParams; i++)
+ {
+ Type parameterType = parameterTypes[i];
+ if (parameterType.IsByRef)
+ {
+ IntPtr paramPtr = intPtrParams[i];
+ MarshalFromObject(objParams[i], parameterType.GetElementType(), ref paramPtr);
+ }
+ }
+
+ if (returnObject is not null)
+ {
+ if (returnObject is string returnStr)
+ return ManagedString.ToNative(returnStr);
+ else if (returnObject is IntPtr returnPtr)
+ return returnPtr;
+ else if (returnObject is bool boolValue)
+ return boolValue ? boolTruePtr : boolFalsePtr;
+ else if (returnObject is Type typeValue)
+ return GCHandle.ToIntPtr(GetOrAddTypeGCHandle(typeValue));
+ else if (returnObject is object[] objectArray)
+ return GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(ManagedArrayToGCHandleArray(objectArray)), GCHandleType.Weak));
+ else
+ return GCHandle.ToIntPtr(GCHandle.Alloc(returnObject, GCHandleType.Weak));
+ }
+ return IntPtr.Zero;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetMethodUnmanagedFunctionPointer(IntPtr methodHandle)
+ {
+ (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target;
+ int numParams = parameterTypes.Length;
+ parameterTypes = parameterTypes.Append(methodInfo.ReturnType).ToArray();
+
+ // Wrap the method call, this is needed to get the object instance from GCHandle and to pass the exception back to native side
+ var invokeThunk = typeof(ThunkContext).GetMethod("InvokeThunk");
+ parameterTypes = invokeThunk.GetParameters().Select(x => x.ParameterType).Append(invokeThunk.ReturnType).ToArray();
+ Type delegateType = DelegateHelpers.NewDelegateType(parameterTypes);
+
+ var context = new ThunkContext();
+ context.methodInfo = methodInfo;
+ context.numParams = numParams;
+ context.objParams = new object[numParams];
+ context.parameterTypes = methodInfo.GetParameters().Select(x => x.ParameterType).ToArray();
+
+ Delegate methodDelegate = Delegate.CreateDelegate(delegateType, context, invokeThunk);
+
+ IntPtr functionPtr = Marshal.GetFunctionPointerForDelegate(methodDelegate);
+
+ // Keep a reference to the delegate to prevent it from being garbage collected
+ var delegates = cachedDelegates;
+ if (parameterTypes.Any(x => scriptingAssemblyLoadContext.Assemblies.Contains(x.Module.Assembly)))
+ delegates = cachedDelegatesCollectible;
+ lock (delegates)
+ {
+ delegates[functionPtr] = methodDelegate;
+ }
+
+ return functionPtr;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void FieldSetValue(IntPtr handle, IntPtr fieldHandle, IntPtr value)
+ {
+ var obj = GCHandle.FromIntPtr(handle).Target;
+ FieldInfo fieldInfo = (FieldInfo)GCHandle.FromIntPtr(fieldHandle).Target;
+
+ var val = Marshal.PtrToStructure(value, fieldInfo.FieldType);
+ fieldInfo.SetValue(obj, val);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void FieldGetValue(IntPtr handle, IntPtr fieldHandle, IntPtr value_)
+ {
+ var obj = GCHandle.FromIntPtr(handle).Target;
+ FieldInfo fieldInfo = (FieldInfo)GCHandle.FromIntPtr(fieldHandle).Target;
+ object fieldValue = fieldInfo.GetValue(obj);
+
+ IntPtr value;
+ if (fieldValue is Type type)
+ value = GCHandle.ToIntPtr(GetOrAddTypeGCHandle(type));
+ else if (fieldInfo.FieldType == typeof(IntPtr))
+ value = (IntPtr)fieldValue;
+ else
+ value = GCHandle.ToIntPtr(GCHandle.Alloc(fieldValue, GCHandleType.Weak));
+ Marshal.WriteIntPtr(value_, value);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void SetArrayValueReference(IntPtr arrayHandle, IntPtr elementPtr, IntPtr valueHandle)
+ {
+ ManagedArray arr = (ManagedArray)GCHandle.FromIntPtr(arrayHandle).Target;
+ int index = (int)(elementPtr.ToInt64() - arr.PointerToPinnedArray.ToInt64()) / arr.elementSize;
+ arr.array.SetValue(valueHandle, index);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr LoadAssemblyFromPath(IntPtr assemblyPath_, IntPtr* assemblyName, IntPtr* assemblyFullName)
+ {
+ if (!firstAssemblyLoaded)
+ {
+ // This assembly was already loaded when initializing the host context
+ firstAssemblyLoaded = true;
+
+ Assembly flaxEngineAssembly = AppDomain.CurrentDomain.GetAssemblies().First(x => x.GetName().Name == "FlaxEngine.CSharp");
+ *assemblyName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.GetName().Name);
+ *assemblyFullName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.FullName);
+ return GCHandle.ToIntPtr(GetAssemblyHandle(flaxEngineAssembly));
+ }
+ string assemblyPath = Marshal.PtrToStringAnsi(assemblyPath_);
+
+ Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
+
+ *assemblyName = Marshal.StringToCoTaskMemAnsi(assembly.GetName().Name);
+ *assemblyFullName = Marshal.StringToCoTaskMemAnsi(assembly.FullName);
+ return GCHandle.ToIntPtr(GetAssemblyHandle(assembly));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr LoadAssemblyImage(IntPtr data, int len, IntPtr assemblyPath_, IntPtr* assemblyName, IntPtr* assemblyFullName)
+ {
+ if (!firstAssemblyLoaded)
+ {
+ // This assembly was already loaded when initializing the host context
+ firstAssemblyLoaded = true;
+
+ Assembly flaxEngineAssembly = AppDomain.CurrentDomain.GetAssemblies().First(x => x.GetName().Name == "FlaxEngine.CSharp");
+ *assemblyName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.GetName().Name);
+ *assemblyFullName = Marshal.StringToCoTaskMemAnsi(flaxEngineAssembly.FullName);
+ return GCHandle.ToIntPtr(GetAssemblyHandle(flaxEngineAssembly));
+ }
+
+ string assemblyPath = Marshal.PtrToStringAnsi(assemblyPath_);
+
+ byte[] raw = new byte[len];
+ Marshal.Copy(data, raw, 0, len);
+
+ using MemoryStream stream = new MemoryStream(raw);
+ Assembly assembly = scriptingAssemblyLoadContext.LoadFromStream(stream);
+
+ // Assemblies loaded via streams have no Location: https://github.com/dotnet/runtime/issues/12822
+ AssemblyLocations.Add(assembly.FullName, assemblyPath);
+
+ *assemblyName = Marshal.StringToCoTaskMemAnsi(assembly.GetName().Name);
+ *assemblyFullName = Marshal.StringToCoTaskMemAnsi(assembly.FullName);
+ return GCHandle.ToIntPtr(GetAssemblyHandle(assembly));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetAssemblyByName(IntPtr name_, IntPtr* assemblyName, IntPtr* assemblyFullName)
+ {
+ string name = Marshal.PtrToStringAnsi(name_);
+
+ Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.GetName().Name == name);
+ if (assembly == null)
+ assembly = scriptingAssemblyLoadContext.Assemblies.FirstOrDefault(x => x.GetName().Name == name);
+
+ *assemblyName = Marshal.StringToCoTaskMemAnsi(assembly.GetName().Name);
+ *assemblyFullName = Marshal.StringToCoTaskMemAnsi(assembly.FullName);
+ return GCHandle.ToIntPtr(GetAssemblyHandle(assembly));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetRuntimeInformation()
+ {
+ return Marshal.StringToCoTaskMemAnsi(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void CloseAssembly(IntPtr assemblyHandle)
+ {
+ Assembly assembly = (Assembly)GCHandle.FromIntPtr(assemblyHandle).Target;
+ GCHandle.FromIntPtr(assemblyHandle).Free();
+ assemblyHandles.Remove(assembly);
+
+ AssemblyLocations.Remove(assembly.FullName);
+
+ // Clear all caches which might hold references to closing assembly
+ cachedDelegatesCollectible.Clear();
+ typeCache.Clear();
+ marshallableStructCache.Clear();
+
+ // Release all GCHandles in collectible ALC
+ foreach (var pair in typeHandleCacheCollectible)
+ pair.Value.Free();
+ typeHandleCacheCollectible.Clear();
+
+ foreach (var handle in methodHandlesCollectible)
+ handle.Free();
+ methodHandlesCollectible.Clear();
+
+ foreach (var handle in fieldHandleCacheCollectible)
+ handle.Free();
+ fieldHandleCacheCollectible.Clear();
+
+ // Unload the ALC
+ bool unloading = true;
+ scriptingAssemblyLoadContext.Unloading += (alc) => { unloading = false; };
+ scriptingAssemblyLoadContext.Unload();
+
+ while (unloading)
+ System.Threading.Thread.Sleep(1);
+
+ // TODO: benchmark collectible setting performance, maybe enable it only in editor builds?
+ scriptingAssemblyLoadContext = new AssemblyLoadContext(null, true);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetAssemblyObject(IntPtr assemblyName_)
+ {
+ string assemblyName = Marshal.PtrToStringAnsi(assemblyName_);
+ Assembly assembly = null;
+ assembly = scriptingAssemblyLoadContext.Assemblies.FirstOrDefault(x => x.FullName == assemblyName);
+ if (assembly == null)
+ assembly = System.AppDomain.CurrentDomain.GetAssemblies().First(x => x.FullName == assemblyName);
+
+ return GCHandle.ToIntPtr(GCHandle.Alloc(assembly));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static unsafe int NativeSizeOf(IntPtr typeHandle, uint* align)
+ {
+ Type originalType = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ Type type = GetInternalType(originalType) ?? originalType;
+
+ if (type == typeof(Version))
+ type = typeof(VersionNative);
+
+ int size = Marshal.SizeOf(type);
+ *align = (uint)size; // Is this correct?
+ return size;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static byte TypeIsSubclassOf(IntPtr typeHandle, IntPtr othertypeHandle, byte checkInterfaces)
+ {
+ if (typeHandle == othertypeHandle)
+ return 1;
+
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ Type otherType = (Type)GCHandle.FromIntPtr(othertypeHandle).Target;
+
+ if (type == otherType)
+ return 1;
+
+ if (checkInterfaces != 0 && otherType.IsInterface)
+ {
+ if (type.GetInterfaces().Contains(otherType))
+ return 1;
+ }
+
+ return type.IsSubclassOf(otherType) ? (byte)1 : (byte)0;
+ }
+
+ [UnmanagedCallersOnly]
+ internal static byte TypeIsValueType(IntPtr typeHandle)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ return (byte)(type.IsValueType ? 1 : 0);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static byte TypeIsEnum(IntPtr typeHandle)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ return (byte)(type.IsEnum ? 1 : 0);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetClassParent(IntPtr typeHandle)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+ return GCHandle.ToIntPtr(GetTypeGCHandle(type.BaseType));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static byte GetMethodParameterIsOut(IntPtr methodHandle, int parameterNum)
+ {
+ (Type[] parameterTypes, MethodInfo methodInfo) = (Tuple)GCHandle.FromIntPtr(methodHandle).Target;
+ ParameterInfo parameterInfo = methodInfo.GetParameters()[parameterNum];
+ return (byte)(parameterInfo.IsOut ? 1 : 0);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetNullReferenceException()
+ {
+ var exception = new NullReferenceException();
+ return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetNotSupportedException()
+ {
+ var exception = new NotSupportedException();
+ return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetArgumentNullException()
+ {
+ var exception = new ArgumentNullException();
+ return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetArgumentException()
+ {
+ var exception = new ArgumentException();
+ return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr GetArgumentOutOfRangeException()
+ {
+ var exception = new ArgumentOutOfRangeException();
+ return GCHandle.ToIntPtr(GCHandle.Alloc(exception, GCHandleType.Weak));
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr NewGCHandle(IntPtr ptr, byte pinned)
+ {
+ object obj = GCHandle.FromIntPtr(ptr).Target;
+ GCHandle handle = GCHandle.Alloc(obj, pinned != 0 ? GCHandleType.Pinned : GCHandleType.Normal);
+ return GCHandle.ToIntPtr(handle);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static IntPtr NewGCHandleWeakref(IntPtr ptr, byte track_resurrection)
+ {
+ object obj = GCHandle.FromIntPtr(ptr).Target;
+ GCHandle handle = GCHandle.Alloc(obj, track_resurrection != 0 ? GCHandleType.WeakTrackResurrection : GCHandleType.Weak);
+ return GCHandle.ToIntPtr(handle);
+ }
+
+ [UnmanagedCallersOnly]
+ internal static void FreeGCHandle(IntPtr ptr)
+ {
+ GCHandle handle = GCHandle.FromIntPtr(ptr);
+ handle.Free();
+ }
+
+ internal enum MonoType
+ {
+ MONO_TYPE_END = 0x00,
+ MONO_TYPE_VOID = 0x01,
+ MONO_TYPE_BOOLEAN = 0x02,
+ MONO_TYPE_CHAR = 0x03,
+ MONO_TYPE_I1 = 0x04,
+ MONO_TYPE_U1 = 0x05,
+ MONO_TYPE_I2 = 0x06,
+ MONO_TYPE_U2 = 0x07,
+ MONO_TYPE_I4 = 0x08,
+ MONO_TYPE_U4 = 0x09,
+ MONO_TYPE_I8 = 0x0a,
+ MONO_TYPE_U8 = 0x0b,
+ MONO_TYPE_R4 = 0x0c,
+ MONO_TYPE_R8 = 0x0d,
+ MONO_TYPE_STRING = 0x0e,
+ MONO_TYPE_PTR = 0x0f,
+ MONO_TYPE_BYREF = 0x10,
+ MONO_TYPE_VALUETYPE = 0x11,
+ MONO_TYPE_CLASS = 0x12,
+ MONO_TYPE_VAR = 0x13,
+ MONO_TYPE_ARRAY = 0x14,
+ MONO_TYPE_GENERICINST = 0x15,
+ MONO_TYPE_TYPEDBYREF = 0x16,
+ MONO_TYPE_I = 0x18,
+ MONO_TYPE_U = 0x19,
+ MONO_TYPE_FNPTR = 0x1b,
+ MONO_TYPE_OBJECT = 0x1c,
+ MONO_TYPE_SZARRAY = 0x1d,
+ MONO_TYPE_MVAR = 0x1e,
+ MONO_TYPE_CMOD_REQD = 0x1f,
+ MONO_TYPE_CMOD_OPT = 0x20,
+ MONO_TYPE_INTERNAL = 0x21,
+ MONO_TYPE_MODIFIER = 0x40,
+ MONO_TYPE_SENTINEL = 0x41,
+ MONO_TYPE_PINNED = 0x45,
+ MONO_TYPE_ENUM = 0x55,
+ };
+
+ [UnmanagedCallersOnly]
+ internal static int GetTypeMonoTypeEnum(IntPtr typeHandle)
+ {
+ Type type = (Type)GCHandle.FromIntPtr(typeHandle).Target;
+
+ MonoType monoType;
+ switch (type)
+ {
+ case Type _ when type == typeof(bool):
+ monoType = MonoType.MONO_TYPE_BOOLEAN;
+ break;
+ case Type _ when type == typeof(sbyte):
+ case Type _ when type == typeof(short):
+ monoType = MonoType.MONO_TYPE_I2;
+ break;
+ case Type _ when type == typeof(byte):
+ case Type _ when type == typeof(ushort):
+ monoType = MonoType.MONO_TYPE_U2;
+ break;
+ case Type _ when type == typeof(int):
+ monoType = MonoType.MONO_TYPE_I4;
+ break;
+ case Type _ when type == typeof(uint):
+ monoType = MonoType.MONO_TYPE_U4;
+ break;
+ case Type _ when type == typeof(long):
+ monoType = MonoType.MONO_TYPE_I8;
+ break;
+ case Type _ when type == typeof(ulong):
+ monoType = MonoType.MONO_TYPE_U8;
+ break;
+ case Type _ when type == typeof(float):
+ monoType = MonoType.MONO_TYPE_R4;
+ break;
+ case Type _ when type == typeof(double):
+ monoType = MonoType.MONO_TYPE_R8;
+ break;
+ case Type _ when type == typeof(string):
+ monoType = MonoType.MONO_TYPE_STRING;
+ break;
+ case Type _ when type == typeof(IntPtr):
+ monoType = MonoType.MONO_TYPE_PTR;
+ break;
+ case Type _ when type.IsEnum:
+ {
+ var elementType = type.GetEnumUnderlyingType();
+ if (elementType == typeof(sbyte) || elementType == typeof(short))
+ monoType = MonoType.MONO_TYPE_I2;
+ else if (elementType == typeof(byte) || elementType == typeof(ushort))
+ monoType = MonoType.MONO_TYPE_U2;
+ else if (elementType == typeof(int))
+ monoType = MonoType.MONO_TYPE_I4;
+ else if (elementType == typeof(uint))
+ monoType = MonoType.MONO_TYPE_U4;
+ else if (elementType == typeof(long))
+ monoType = MonoType.MONO_TYPE_I8;
+ else if (elementType == typeof(ulong))
+ monoType = MonoType.MONO_TYPE_U8;
+ else
+ throw new Exception($"GetTypeMonoTypeEnum: Unsupported type '{type.FullName}'");
+ break;
+ }
+ case Type _ when type.IsValueType && !type.IsEnum && !type.IsPrimitive:
+ monoType = MonoType.MONO_TYPE_VALUETYPE;
+ break;
+ case Type _ when type.IsClass:
+ monoType = MonoType.MONO_TYPE_OBJECT;
+ break;
+ case Type _ when type.IsGenericType:
+ monoType = MonoType.MONO_TYPE_GENERICINST;
+ break;
+
+ default: throw new Exception($"GetTypeMonoTypeEnum: Unsupported type '{type.FullName}'");
+ }
+
+ return (int)monoType;
+ }
+
+ ///
+ /// Returns all types that that owned by this assembly.
+ ///
+ private static Type[] GetAssemblyTypes(Assembly assembly)
+ {
+ var referencedAssemblies = assembly.GetReferencedAssemblies();
+ var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
+
+ List referencedTypes = new List();
+ foreach (var assemblyName in referencedAssemblies)
+ {
+ var asm = allAssemblies.FirstOrDefault(x => x.GetName().Name == assemblyName.Name);
+ if (asm == null)
+ continue;
+ referencedTypes.AddRange(asm.DefinedTypes.Select(x => x.FullName).ToArray());
+ }
+
+ // TODO: use MetadataReader to read types without loading any of the referenced assemblies?
+ // https://makolyte.com/csharp-get-a-list-of-types-defined-in-an-assembly-without-loading-it/
+
+ /*var assemblyPath = Utils.GetAssemblyLocation(assembly);
+ List types = new List();
+ using (var sr = new StreamReader(assemblyPath))
+ {
+ using (var portableExecutableReader = new PEReader(sr.BaseStream))
+ {
+ var metadataReader = portableExecutableReader.GetMetadataReader();
+
+ foreach (var typeDefHandle in metadataReader.TypeDefinitions)
+ {
+ var typeDef = metadataReader.GetTypeDefinition(typeDefHandle);
+
+ if (string.IsNullOrEmpty(metadataReader.GetString(typeDef.Namespace)))
+ continue; //if it's namespace is blank, it's not a user-defined type
+
+ if (typeDef.Attributes.HasFlag(TypeAttributes.Abstract) || !typeDef.Attributes.HasFlag(TypeAttributes.Public))
+ continue; //Not a internal concrete type
+
+ types.Add(typeDef);
+ }
+ }
+ }*/
+
+ // We need private types of this assembly too, DefinedTypes contains a lot of types from other assemblies...
+ var types = referencedTypes.Any() ? assembly.DefinedTypes.Where(x => !referencedTypes.Contains(x.FullName)).ToArray() : assembly.DefinedTypes.ToArray();
+
+ Assert.IsTrue(AppDomain.CurrentDomain.GetAssemblies().Where(x => x.GetName().Name == "FlaxEngine.CSharp").Count() == 1);
+
+ return types;
+ }
+
+ internal static GCHandle GetOrAddTypeGCHandle(Type type)
+ {
+ GCHandle handle;
+ if (typeHandleCache.TryGetValue(type.AssemblyQualifiedName, out handle))
+ return handle;
+
+ if (typeHandleCacheCollectible.TryGetValue(type.AssemblyQualifiedName, out handle))
+ return handle;
+
+ handle = GCHandle.Alloc(type);
+ if (type.IsCollectible) // check if generic parameters are also collectible?
+ typeHandleCacheCollectible.Add(type.AssemblyQualifiedName, handle);
+ else
+ typeHandleCache.Add(type.AssemblyQualifiedName, handle);
+
+ return handle;
+ }
+
+ internal static GCHandle GetTypeGCHandle(Type type)
+ {
+ GCHandle handle;
+ if (typeHandleCache.TryGetValue(type.AssemblyQualifiedName, out handle))
+ return handle;
+
+ if (typeHandleCacheCollectible.TryGetValue(type.AssemblyQualifiedName, out handle))
+ return handle;
+
+ throw new Exception($"GCHandle not found for type '{type.FullName}'");
+ }
+
+ internal static class DelegateHelpers
+ {
+ private static readonly Func MakeNewCustomDelegate =
+ (Func)Delegate.CreateDelegate(typeof(Func),
+ typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
+ .GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static));
+
+ internal static void Init()
+ {
+ // Ensures the MakeNewCustomDelegate is put in the collectible ALC?
+ using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection();
+
+ MakeNewCustomDelegate(new[] { typeof(void) });
+ }
+
+ internal static Type NewDelegateType(params Type[] parameters)
+ {
+ if (parameters.Any(x => scriptingAssemblyLoadContext.Assemblies.Contains(x.Module.Assembly)))
+ {
+ // Ensure the new delegate is placed in the collectible ALC
+ //using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection();
+ return MakeNewCustomDelegate(parameters);
+ }
+
+ return MakeNewCustomDelegate(parameters);
+ }
+ }
+
+ ///
+ /// Wrapper class for invoking function pointers from unmanaged code.
+ ///
+ internal class ThunkContext
+ {
+ internal MethodInfo methodInfo;
+ internal int numParams;
+ internal Type[] parameterTypes;
+ internal object[] objParams;
+
+ private static MethodInfo castMethod = typeof(ThunkContext).GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic);
+
+ private static object CastParameter(Type type, IntPtr ptr)
+ {
+ if (type.IsValueType)
+ {
+ var closeCast = castMethod.MakeGenericMethod(type);
+ return closeCast.Invoke(ptr, new[] { (object)ptr });
+ }
+ else if (type.IsClass)
+ return ptr == IntPtr.Zero ? null : GCHandle.FromIntPtr(ptr).Target;
+ return null;
+ }
+
+ public IntPtr InvokeThunk(IntPtr instance, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7)
+ {
+ var intPtrParams = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 };
+
+ for (int i = 0; i < numParams; i++)
+ objParams[i] = CastParameter(parameterTypes[i], intPtrParams[i]);
+
+ object returnObject;
+ try
+ {
+ returnObject = methodInfo.Invoke(instance != IntPtr.Zero ? GCHandle.FromIntPtr(instance).Target : null, objParams);
+ }
+ catch (Exception ex)
+ {
+ IntPtr exceptionPtr = intPtrParams[numParams];
+ Marshal.WriteIntPtr(exceptionPtr, GCHandle.ToIntPtr(GCHandle.Alloc(ex, GCHandleType.Weak)));
+ return IntPtr.Zero;
+ }
+
+ // Marshal reference parameters
+ /*for (int i = 0; i < numParams; i++)
+ {
+ Type parameterType = parameterTypes[i];
+ if (parameterType.IsByRef)
+ {
+ MarshalFromObject(parameterType.GetElementType(), objParams[i], intPtrParams[i], out int writeSize);
+ }
+ }*/
+
+ if (returnObject is not null)
+ {
+ if (returnObject is string returnStr)
+ return ManagedString.ToNative(returnStr);
+ else if (returnObject is IntPtr returnPtr)
+ return returnPtr;
+ else if (returnObject is bool boolValue)
+ return boolValue ? boolTruePtr : boolFalsePtr;
+ else
+ return GCHandle.ToIntPtr(GCHandle.Alloc(returnObject, GCHandleType.Weak));
+ }
+ return IntPtr.Zero;
+ }
+ }
+ }
+}
+#endif
diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp
index b5001a730..7f871df9d 100644
--- a/Source/Engine/Level/Level.cpp
+++ b/Source/Engine/Level/Level.cpp
@@ -1277,6 +1277,7 @@ bool Level::SaveAllScenes()
void Level::SaveAllScenesAsync()
{
+ SCRIPTING_EXPORT("FlaxEngine.Level::Internal_SaveAllScenesAsync")
ScopeLock lock(_sceneActionsLocker);
for (int32 i = 0; i < Scenes.Count(); i++)
_sceneActions.Enqueue(New(Scenes[i]));
@@ -1381,6 +1382,7 @@ bool Level::UnloadAllScenes()
void Level::UnloadAllScenesAsync()
{
+ SCRIPTING_EXPORT("FlaxEngine.Level::Internal_UnloadAllScenesAsync")
ScopeLock lock(_sceneActionsLocker);
_sceneActions.Enqueue(New());
}
diff --git a/Source/Engine/Main/Windows/main.cpp b/Source/Engine/Main/Windows/main.cpp
index 1f0e9676e..762f82110 100644
--- a/Source/Engine/Main/Windows/main.cpp
+++ b/Source/Engine/Main/Windows/main.cpp
@@ -50,14 +50,14 @@ int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmd
#endif
Platform::PreInit(hInstance);
- __try
+ //__try
{
return Engine::Main(lpCmdLine);
}
- __except (SehExceptionHandler(GetExceptionInformation()))
+ /*__except (SehExceptionHandler(GetExceptionInformation()))
{
return -1;
- }
+ }*/
}
#endif
diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp
index 72634230a..750f9c2aa 100644
--- a/Source/Engine/Networking/NetworkManager.cpp
+++ b/Source/Engine/Networking/NetworkManager.cpp
@@ -303,6 +303,7 @@ bool NetworkManager::StartHost()
void NetworkManager::Stop()
{
+ SCRIPTING_EXPORT("FlaxEngine.Networking.NetworkManager::Internal_Stop");
if (Mode == NetworkManagerMode::Offline && State == NetworkConnectionState::Offline)
return;
PROFILE_CPU();
diff --git a/Source/Engine/Platform/Base/PlatformBase.h b/Source/Engine/Platform/Base/PlatformBase.h
index 7693a3ea6..14513effe 100644
--- a/Source/Engine/Platform/Base/PlatformBase.h
+++ b/Source/Engine/Platform/Base/PlatformBase.h
@@ -175,6 +175,7 @@ public:
/// Size of the memory to copy in bytes
FORCE_INLINE static void MemoryCopy(void* dst, const void* src, uint64 size)
{
+ SCRIPTING_EXPORT("FlaxEngine.Utils::MemoryCopy")
memcpy(dst, src, static_cast(size));
}
@@ -196,6 +197,7 @@ public:
/// Size of the memory to clear in bytes
FORCE_INLINE static void MemoryClear(void* dst, uint64 size)
{
+ SCRIPTING_EXPORT("FlaxEngine.Utils::MemoryClear")
memset(dst, 0, static_cast(size));
}
@@ -207,6 +209,7 @@ public:
/// Size of the memory to compare in bytes.
FORCE_INLINE static int32 MemoryCompare(const void* buf1, const void* buf2, uint64 size)
{
+ SCRIPTING_EXPORT("FlaxEngine.Utils::MemoryCompare")
return memcmp(buf1, buf2, static_cast(size));
}
diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
index 50ac72381..c0b7a6bf8 100644
--- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp
+++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp
@@ -1658,6 +1658,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
void LinuxClipboard::Clear()
{
+ SCRIPTING_EXPORT("FlaxEngine.Clipboard::Internal_Clear");
SetText(StringView::Empty);
}
diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp
index 19325bbc3..66d90e21a 100644
--- a/Source/Engine/Platform/Mac/MacPlatform.cpp
+++ b/Source/Engine/Platform/Mac/MacPlatform.cpp
@@ -110,6 +110,7 @@ Float2 MacUtils::GetScreensOrigin()
void MacClipboard::Clear()
{
+ SCRIPTING_EXPORT("FlaxEngine.Clipboard::Internal_Clear");
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
}
diff --git a/Source/Engine/Platform/Windows/WindowsClipboard.cpp b/Source/Engine/Platform/Windows/WindowsClipboard.cpp
index 28f4c2188..0c9e46296 100644
--- a/Source/Engine/Platform/Windows/WindowsClipboard.cpp
+++ b/Source/Engine/Platform/Windows/WindowsClipboard.cpp
@@ -16,6 +16,7 @@ typedef struct _DROPFILES
void WindowsClipboard::Clear()
{
+ SCRIPTING_EXPORT("FlaxEngine.Clipboard::Internal_Clear");
OpenClipboard(nullptr);
EmptyClipboard();
CloseClipboard();
diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp
index d929bdb5c..63562be0b 100644
--- a/Source/Engine/Render2D/Render2D.cpp
+++ b/Source/Engine/Render2D/Render2D.cpp
@@ -699,6 +699,7 @@ void Render2D::Begin(GPUContext* context, GPUTextureView* output, GPUTextureView
void Render2D::End()
{
+ SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_End")
RENDER2D_CHECK_RENDERING_STATE;
ASSERT(Context != nullptr && Output != nullptr);
ASSERT(GUIShader != nullptr);
@@ -814,6 +815,7 @@ void Render2D::PeekTransform(Matrix3x3& transform)
void Render2D::PopTransform()
{
+ SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopTransform")
RENDER2D_CHECK_RENDERING_STATE;
ASSERT(TransformLayersStack.HasItems());
@@ -855,6 +857,7 @@ void Render2D::PeekClip(Rectangle& clipRect)
void Render2D::PopClip()
{
+ SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopClip")
RENDER2D_CHECK_RENDERING_STATE;
ClipLayersStack.Pop();
@@ -876,6 +879,7 @@ void Render2D::PeekTint(Color& tint)
void Render2D::PopTint()
{
+ SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopTint")
RENDER2D_CHECK_RENDERING_STATE;
TintLayersStack.Pop();
diff --git a/Source/Engine/Render2D/Render2D.cs b/Source/Engine/Render2D/Render2D.cs
index b642cc5c1..22ce6529b 100644
--- a/Source/Engine/Render2D/Render2D.cs
+++ b/Source/Engine/Render2D/Render2D.cs
@@ -64,8 +64,7 @@ namespace FlaxEngine
/// The rectangle to draw.
public static void DrawSprite(SpriteHandle spriteHandle, Rectangle rect)
{
- var color = Color.White;
- Internal_DrawSprite(ref spriteHandle, ref rect, ref color);
+ DrawSprite(spriteHandle, rect, Color.White);
}
///
@@ -86,8 +85,7 @@ namespace FlaxEngine
/// The rectangle to draw.
public static void DrawSpritePoint(SpriteHandle spriteHandle, Rectangle rect)
{
- var color = Color.White;
- Internal_DrawSpritePoint(ref spriteHandle, ref rect, ref color);
+ DrawSpritePoint(spriteHandle, rect, Color.White);
}
///
diff --git a/Source/Engine/Renderer/Renderer.cs b/Source/Engine/Renderer/Renderer.cs
index 6c9b25dff..9efe13e75 100644
--- a/Source/Engine/Renderer/Renderer.cs
+++ b/Source/Engine/Renderer/Renderer.cs
@@ -15,7 +15,9 @@ namespace FlaxEngine
/// The custom set of actors to render. If empty, the loaded scenes will be rendered.
public static void DrawSceneDepth(GPUContext context, SceneRenderTask task, GPUTexture output, List customActors)
{
- Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), Utils.ExtractArrayFromList(customActors));
+ var temp = Utils.ExtractArrayFromList(customActors);
+ var tempCount = temp.Length;
+ Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), ref temp, ref tempCount);
}
}
}
diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp
index eae964e29..53887c94b 100644
--- a/Source/Engine/Scripting/BinaryModule.cpp
+++ b/Source/Engine/Scripting/BinaryModule.cpp
@@ -820,22 +820,33 @@ MMethod* ManagedBinaryModule::FindMethod(MClass* mclass, const ScriptingTypeMeth
{
#if USE_MONO
MonoMethodSignature* sig = mono_method_signature(method->GetNative());
- if (method->IsStatic() != signature.IsStatic ||
+ /*if (method->IsStatic() != signature.IsStatic ||
method->GetName() != signature.Name ||
(int32)mono_signature_get_param_count(sig) != signature.Params.Count())
+ continue;*/
+ if (method->IsStatic() != signature.IsStatic)
+ continue;
+ if (method->GetName() != signature.Name)
+ continue;
+ if ((int32)mono_signature_get_param_count(sig) != signature.Params.Count())
continue;
void* sigParams = nullptr;
- mono_signature_get_params(sig, &sigParams);
+ MonoType* type = mono_signature_get_params(sig, &sigParams);
bool isValid = true;
- for (int32 paramIdx = 0; paramIdx < signature.Params.Count(); paramIdx++)
+ int paramIdx = 0;
+ while (type != nullptr)
{
auto& param = signature.Params[paramIdx];
if (param.IsOut != (mono_signature_param_is_out(sig, paramIdx) != 0) ||
- !VariantTypeEquals(param.Type, ((MonoType**)sigParams)[paramIdx]))
+ !VariantTypeEquals(param.Type, type))
{
+ auto asdf = VariantTypeEquals(param.Type, type);
isValid = false;
break;
}
+
+ type = mono_signature_get_params(sig, &sigParams);
+ paramIdx++;
}
if (isValid && VariantTypeEquals(signature.ReturnType, mono_signature_get_return_type(sig)))
return method;
@@ -1187,8 +1198,6 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
#if USE_MONO
const auto mMethod = (MMethod*)method;
MonoMethodSignature* signature = mono_method_signature(mMethod->GetNative());
- void* signatureParams = nullptr;
- mono_signature_get_params(signature, &signatureParams);
const int32 parametersCount = mono_signature_get_param_count(signature);
if (paramValues.Length() != parametersCount)
{
@@ -1222,20 +1231,25 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
void** params = (void**)alloca(parametersCount * sizeof(void*));
bool failed = false;
bool hasOutParams = false;
- for (int32 paramIdx = 0; paramIdx < parametersCount; paramIdx++)
+ void* sigParams = nullptr;
+ MonoType* type = mono_signature_get_params(signature, &sigParams);
+ for (int paramIdx = 0; type != nullptr;)
{
auto& paramValue = paramValues[paramIdx];
const bool isOut = mono_signature_param_is_out(signature, paramIdx) != 0;
hasOutParams |= isOut;
// Marshal parameter for managed method
- MType paramType(((MonoType**)signatureParams)[paramIdx]);
+ MType paramType(type);
params[paramIdx] = MUtils::VariantToManagedArgPtr(paramValue, paramType, failed);
if (failed)
{
LOG(Error, "Failed to marshal parameter {5}:{4} of method '{0}.{1}' (args count: {2}), value type: {6}, value: {3}", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount, paramValue, paramType.ToString(), paramIdx, paramValue.Type);
return true;
}
+
+ type = mono_signature_get_params(signature, &sigParams);
+ paramIdx++;
}
// Invoke the method
diff --git a/Source/Engine/Scripting/DotNet/CoreCLR.cpp b/Source/Engine/Scripting/DotNet/CoreCLR.cpp
new file mode 100644
index 000000000..060865a6b
--- /dev/null
+++ b/Source/Engine/Scripting/DotNet/CoreCLR.cpp
@@ -0,0 +1,166 @@
+#include "CoreCLR.h"
+
+#include "Engine/Core/Log.h"
+#include "Engine/Platform/Platform.h"
+#include "Engine/Platform/FileSystem.h"
+#include "Engine/Core/Types/DateTime.h"
+#include "Engine/Debug/DebugLog.h"
+#include "Engine/Core/Collections/Dictionary.h"
+
+#include
+#include
+#include
+
+#if PLATFORM_WINDOWS
+#include // CoTask*
+#undef SetEnvironmentVariable
+#undef LoadLibrary
+#endif
+
+#if COMPILE_WITH_PROFILER
+#endif
+
+namespace CoreCLRPrivate
+{
+}
+
+static Dictionary cachedFunctions;
+static String assemblyName = TEXT("FlaxEngine.CSharp");
+static Char* typeName = TEXT("FlaxEngine.NativeInterop, FlaxEngine.CSharp");
+
+hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config;
+hostfxr_initialize_for_dotnet_command_line_fn hostfxr_initialize_for_dotnet_command_line;
+hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate;
+hostfxr_close_fn hostfxr_close;
+load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer;
+get_function_pointer_fn get_function_pointer;
+hostfxr_set_error_writer_fn hostfxr_set_error_writer;
+hostfxr_get_dotnet_environment_info_result_fn hostfxr_get_dotnet_environment_info_result;
+hostfxr_run_app_fn hostfxr_run_app;
+
+bool CoreCLR::LoadHostfxr(const String& library_path)
+{
+ Platform::SetEnvironmentVariable(TEXT("DOTNET_MULTILEVEL_LOOKUP"), TEXT("0")); // FIXME: not needed with .NET 7
+
+ Platform::SetEnvironmentVariable(TEXT("DOTNET_TieredPGO"), TEXT("1"));
+ Platform::SetEnvironmentVariable(TEXT("DOTNET_TC_QuickJitForLoops"), TEXT("1"));
+ Platform::SetEnvironmentVariable(TEXT("DOTNET_ReadyToRun"), TEXT("0"));
+
+ char_t hostfxrPath[1024];
+ size_t hostfxrPathSize = sizeof(hostfxrPath) / sizeof(char_t);
+
+ get_hostfxr_parameters params;
+ params.size = sizeof(hostfxr_initialize_parameters);
+ params.assembly_path = library_path.Get();
+ params.dotnet_root = nullptr;//dotnetRoot.Get();
+
+ int rc = get_hostfxr_path(hostfxrPath, &hostfxrPathSize, ¶ms);
+ if (rc != 0)
+ {
+ LOG(Error, "Failed to find hostfxr: {0:x}", (unsigned int)rc);
+ return false;
+ }
+ LOG(Info, "Found hostfxr in {0}", hostfxrPath);
+
+ void *hostfxr = Platform::LoadLibrary(hostfxrPath);
+ hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_runtime_config");
+ hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_dotnet_command_line");
+ hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)Platform::GetProcAddress(hostfxr, "hostfxr_get_runtime_delegate");
+ hostfxr_close = (hostfxr_close_fn)Platform::GetProcAddress(hostfxr, "hostfxr_close");
+ hostfxr_set_error_writer = (hostfxr_set_error_writer_fn)Platform::GetProcAddress(hostfxr, "hostfxr_set_error_writer");
+ hostfxr_get_dotnet_environment_info_result = (hostfxr_get_dotnet_environment_info_result_fn)Platform::GetProcAddress(hostfxr, "hostfxr_get_dotnet_environment_info_result");
+ hostfxr_run_app = (hostfxr_run_app_fn)Platform::GetProcAddress(hostfxr, "hostfxr_run_app");
+
+ return true;
+}
+
+bool CoreCLR::InitHostfxr(const String& config_path, const String& library_path)
+{
+ const wchar_t* argv[1] = { library_path.Get() };
+
+ hostfxr_initialize_parameters params;
+ params.size = sizeof(hostfxr_initialize_parameters);
+ params.host_path = library_path.Get();
+ params.dotnet_root = nullptr;//dotnetRoot.Get(); // This probably must be set
+
+ hostfxr_handle handle = nullptr;
+
+ // Initialize hosting component, hostfxr_initialize_for_dotnet_command_line is used here
+ // to allow self-contained engine installation to be used when needed.
+
+ int rc = hostfxr_initialize_for_dotnet_command_line(1, argv, ¶ms, &handle);
+ if (rc != 0 || handle == nullptr)
+ {
+ LOG(Error, "Failed to initialize hostfxr: {0:x}", (unsigned int)rc);
+ hostfxr_close(handle);
+ return false;
+ }
+
+ void* pget_function_pointer = nullptr;
+ rc = hostfxr_get_runtime_delegate(handle, hdt_get_function_pointer, &pget_function_pointer);
+ if (rc != 0 || pget_function_pointer == nullptr)
+ LOG(Error, "Failed to get runtime delegate hdt_get_function_pointer: {0:x}", (unsigned int)rc);
+
+ hostfxr_close(handle);
+ get_function_pointer = (get_function_pointer_fn)pget_function_pointer;
+
+ return true;
+}
+
+void* CoreCLR::GetFunctionPointerFromDelegate(const String& methodName)
+{
+ void* fun;
+ if (cachedFunctions.TryGet(methodName, fun))
+ return fun;
+
+ String delegateTypeName = String::Format(TEXT("{0}+{1}Delegate, {2}"), TEXT("FlaxEngine.NativeInterop"), methodName, assemblyName);
+
+ int rc = get_function_pointer(typeName, methodName.Get(), delegateTypeName.Get(), nullptr, nullptr, &fun);
+ if (rc != 0)
+ LOG(Fatal, "Failed to get unmanaged function pointer for method {0}: 0x{1:x}", methodName.Get(), (unsigned int)rc);
+
+ cachedFunctions.Add(String(methodName), fun);
+
+ return fun;
+}
+
+void* CoreCLR::GetStaticMethodPointer(const String& methodName)
+{
+ void* fun;
+ if (cachedFunctions.TryGet(methodName, fun))
+ return fun;
+
+ int rc = get_function_pointer(typeName, methodName.Get(), UNMANAGEDCALLERSONLY_METHOD, nullptr, nullptr, &fun);
+ if (rc != 0)
+ LOG(Fatal, "Failed to get unmanaged function pointer for method {0}: 0x{1:x}", methodName.Get(), (unsigned int)rc);
+
+ cachedFunctions.Add(String(methodName), fun);
+
+ return fun;
+}
+
+void* CoreCLR::Allocate(int size)
+{
+#if PLATFORM_WINDOWS
+ void* ptr = CoTaskMemAlloc(size);
+#else
+ void* ptr = malloc(size);
+#endif
+
+#if COMPILE_WITH_PROFILER
+ Platform::OnMemoryAlloc(ptr, size);
+#endif
+ return ptr;
+}
+
+void CoreCLR::Free(void* ptr)
+{
+#if COMPILE_WITH_PROFILER
+ Platform::OnMemoryFree(ptr);
+#endif
+#if PLATFORM_WINDOWS
+ CoTaskMemFree(ptr);
+#else
+ free(ptr);
+#endif
+}
diff --git a/Source/Engine/Scripting/DotNet/CoreCLR.h b/Source/Engine/Scripting/DotNet/CoreCLR.h
new file mode 100644
index 000000000..3bfe6c3e7
--- /dev/null
+++ b/Source/Engine/Scripting/DotNet/CoreCLR.h
@@ -0,0 +1,48 @@
+#pragma once
+
+// FIXME
+#include
+
+#include "Engine/Core/Types/String.h"
+#include "Engine/Scripting/Types.h"
+
+#if defined(_WIN32)
+#define CORECLR_DELEGATE_CALLTYPE __stdcall
+#else
+#define CORECLR_DELEGATE_CALLTYPE
+#endif
+
+class CoreCLR
+{
+private:
+public:
+ static bool LoadHostfxr(const String& library_path);
+ static bool InitHostfxr(const String& config_path, const String& library_path);
+
+ static void* GetFunctionPointerFromDelegate(const String& methodName);
+ static void* GetStaticMethodPointer(const String& methodName);
+
+ template
+ static RetType CallStaticMethodInternal(const String& methodName, Args... args)
+ {
+ typedef RetType(CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
+ fun function = (fun)GetStaticMethodPointer(methodName);
+ return function(args...);
+ }
+
+ template
+ static RetType CallStaticMethodInternalPointer(void* funPtr, Args... args)
+ {
+ typedef RetType(CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
+ fun function = (fun)funPtr;
+ return function(args...);
+ }
+
+ static const char* GetClassFullname(void* klass);
+ static void* Allocate(int size);
+ static void Free(void* ptr);
+ static gchandle NewGCHandle(void* obj, bool pinned);
+ static gchandle NewGCHandleWeakref(void* obj, bool track_resurrection);
+ static void* GetGCHandleTarget(const gchandle& gchandle);
+ static void FreeGCHandle(const gchandle& gchandle);
+};
diff --git a/Source/Engine/Scripting/DotNet/MonoApi.cpp b/Source/Engine/Scripting/DotNet/MonoApi.cpp
new file mode 100644
index 000000000..f87347b80
--- /dev/null
+++ b/Source/Engine/Scripting/DotNet/MonoApi.cpp
@@ -0,0 +1,1563 @@
+#include
+#include
+
+#include "CoreCLR.h"
+#include "Engine/Scripting/Types.h"
+#include "Engine/Core/Collections/Dictionary.h"
+#include "Engine/Graphics/RenderView.h"
+#include "Engine/Core/Types/StringBuilder.h"
+
+#pragma warning(disable : 4297)
+
+struct CoreCLRClass* GetClass(void* type);
+struct CoreCLRClass* GetOrCreateClass(void* type);
+
+struct _MonoString
+{
+ int32_t length;
+ mono_unichar2 chars[MONO_ZERO_LEN_ARRAY];
+};
+
+struct CoreCLRAssembly;
+struct CoreCLRMethod;
+struct CoreCLRField;
+struct CoreCLRCustomAttribute;
+struct CoreCLRProperty;
+struct CoreCLRClass;
+
+// Structures used to pass information from runtime, must match with the structures in managed side
+struct ManagedClass
+{
+ void* typeHandle;
+ const char* name;
+ const char* fullname;
+ const char* namespace_;
+ uint32 typeAttributes;
+};
+
+struct ClassMethod
+{
+ const char* name;
+ int numParameters;
+ void* handle;
+ uint32 methodAttributes;
+};
+
+struct ClassField
+{
+ const char* name;
+ void* fieldHandle;
+ void* fieldType;
+ uint32 fieldAttributes;
+};
+
+struct ClassProperty
+{
+ const char* name;
+ void* getterHandle;
+ void* setterHandle;
+ uint32 getterFlags;
+ uint32 setterFlags;
+};
+
+struct ClassAttribute
+{
+ const char* name;
+ void* attributeHandle;
+ void* attributeTypeHandle;
+};
+
+Dictionary classHandles;
+Dictionary assemblyHandles;
+uint32 TypeTokenPool = 0;
+
+struct CoreCLRAssembly
+{
+private:
+ StringAnsi _name;
+ StringAnsi _fullname;
+ Array _classes;
+ void* _assemblyHandle;
+
+public:
+ CoreCLRAssembly(void* assemblyHandle, const char* name, const char* fullname)
+ {
+ _assemblyHandle = assemblyHandle;
+ _name = name;
+ _fullname = fullname;
+
+ ManagedClass* managedClasses;
+ int classCount;
+
+ CoreCLR::CallStaticMethodInternal(TEXT("GetManagedClasses"), _assemblyHandle, &managedClasses, &classCount);
+ for (int i = 0; i < classCount; i++)
+ {
+ CoreCLRClass* mci = New(managedClasses[i].typeHandle, StringAnsi(managedClasses[i].name), StringAnsi(managedClasses[i].fullname), StringAnsi(managedClasses[i].namespace_), managedClasses[i].typeAttributes, this);
+ _classes.Add(mci);
+
+ ASSERT(managedClasses[i].typeHandle != nullptr);
+ classHandles.Add(managedClasses[i].typeHandle, mci);
+
+ CoreCLR::Free((void*)managedClasses[i].name);
+ CoreCLR::Free((void*)managedClasses[i].fullname);
+ CoreCLR::Free((void*)managedClasses[i].namespace_);
+ }
+ CoreCLR::Free(managedClasses);
+
+ assemblyHandles.Add(_assemblyHandle, this);
+ }
+
+ ~CoreCLRAssembly()
+ {
+ _classes.ClearDelete();
+ }
+
+ void* GetHandle()
+ {
+ return _assemblyHandle;
+ }
+
+ const StringAnsi& GetName()
+ {
+ return _name;
+ }
+
+ const StringAnsi& GetFullname()
+ {
+ return _fullname;
+ }
+
+ Array GetClasses()
+ {
+ return _classes;
+ }
+
+ void AddClass(CoreCLRClass* klass)
+ {
+ _classes.Add(klass);
+ }
+};
+
+struct CoreCLRClass
+{
+private:
+ StringAnsi _fullname;
+ StringAnsi _name;
+ StringAnsi _namespace;
+ uint32 _typeAttributes;
+ CoreCLRAssembly* _image;
+ uint32 _typeToken;
+ uint32 _size;
+ void* _typeHandle;
+ bool _cachedMethods = false;
+ Array _methods;
+ bool _cachedFields = false;
+ Array _fields;
+ bool _cachedAttributes = false;
+ Array _attributes;
+ bool _cachedProperties = false;
+ Array _properties;
+ bool _cachedInterfaces = false;
+ Array _interfaces;
+
+public:
+ CoreCLRClass(void* typeHandle, StringAnsi name, StringAnsi fullname, StringAnsi namespace_, uint32 typeAttributes, CoreCLRAssembly* image)
+ : _typeHandle(typeHandle), _name(name), _fullname(fullname), _namespace(namespace_), _typeAttributes(typeAttributes), _image(image)
+ {
+ _typeToken = TypeTokenPool++;
+ }
+
+ ~CoreCLRClass()
+ {
+ for (auto method : _methods)
+ {
+ //int rem = monoMethods.RemoveValue(method);
+ //ASSERT(rem > 0)
+ }
+ _methods.ClearDelete();
+ _fields.ClearDelete();
+ _attributes.ClearDelete();
+ _properties.ClearDelete();
+ _interfaces.Clear();
+
+ classHandles.Remove(_typeHandle);
+ }
+
+ uint32 GetAttributes()
+ {
+ return _typeAttributes;
+ }
+
+ uint32 GetTypeToken()
+ {
+ return _typeToken;
+ }
+
+ int GetSize()
+ {
+ if (_size != 0)
+ return _size;
+
+ uint32 dummy;
+ _size = mono_class_value_size((MonoClass*)this, &dummy);
+
+ return _size;
+ }
+
+ const StringAnsi& GetName()
+ {
+ return _name;
+ }
+
+ const StringAnsi& GetFullname()
+ {
+ return _fullname; // FIXME: this should probably return the decorated C# name for generic types (foo) and not the IL-name (foo`1[[T)
+ }
+
+ const StringAnsi& GetNamespace()
+ {
+ return _namespace;
+ }
+
+ void* GetTypeHandle()
+ {
+ return _typeHandle;
+ }
+
+ CoreCLRAssembly* GetAssembly()
+ {
+ return _image;
+ }
+
+ Array GetMethods()
+ {
+ if (_cachedMethods)
+ return _methods;
+
+ ClassMethod* foundMethods;
+ int numMethods;
+
+ CoreCLR::CallStaticMethodInternal(TEXT("GetClassMethods"), _typeHandle, &foundMethods, &numMethods);
+ for (int i = 0; i < numMethods; i++)
+ {
+ CoreCLRMethod* method = New(StringAnsi(foundMethods[i].name), foundMethods[i].numParameters, foundMethods[i].handle, foundMethods[i].methodAttributes, this);
+ _methods.Add(method);
+
+ CoreCLR::Free((void*)foundMethods[i].name);
+ }
+ CoreCLR::Free(foundMethods);
+
+ _cachedMethods = true;
+ return _methods;
+ }
+
+ Array GetFields()
+ {
+ if (_cachedFields)
+ return _fields;
+
+ ClassField* foundFields;
+ int numFields;
+
+ CoreCLR::CallStaticMethodInternal(TEXT("GetClassFields"), _typeHandle, &foundFields, &numFields);
+ for (int i = 0; i < numFields; i++)
+ {
+ CoreCLRField* field = New(StringAnsi(foundFields[i].name), foundFields[i].fieldHandle, foundFields[i].fieldType, foundFields[i].fieldAttributes, this);
+ _fields.Add(field);
+
+ CoreCLR::Free((void*)foundFields[i].name);
+ }
+ CoreCLR::Free(foundFields);
+
+ _cachedFields = true;
+ return _fields;
+ }
+
+ Array GetProperties()
+ {
+ if (_cachedProperties)
+ return _properties;
+
+ ClassProperty* foundProperties;
+ int numProperties;
+
+ CoreCLR::CallStaticMethodInternal(TEXT("GetClassProperties"), _typeHandle, &foundProperties, &numProperties);
+ for (int i = 0; i < numProperties; i++)
+ {
+ CoreCLRProperty* prop = New(StringAnsi(foundProperties[i].name), foundProperties[i].getterHandle, foundProperties[i].setterHandle, foundProperties[i].getterFlags, foundProperties[i].setterFlags, this);
+ _properties.Add(prop);
+
+ CoreCLR::Free((void*)foundProperties[i].name);
+ }
+ CoreCLR::Free(foundProperties);
+
+ _cachedProperties = true;
+ return _properties;
+ }
+
+ Array GetCustomAttributes()
+ {
+ if (_cachedAttributes)
+ return _attributes;
+
+ ClassAttribute* foundAttributes;
+ int numAttributes;
+
+ CoreCLR::CallStaticMethodInternal(TEXT("GetClassAttributes"), _typeHandle, &foundAttributes, &numAttributes);
+ for (int i = 0; i < numAttributes; i++)
+ {
+ CoreCLRClass* attributeClass = GetClass(foundAttributes[i].attributeTypeHandle);
+ CoreCLRCustomAttribute* attribute = New(StringAnsi(foundAttributes[i].name), foundAttributes[i].attributeHandle, this, attributeClass);
+ _attributes.Add(attribute);
+
+ CoreCLR::Free((void*)foundAttributes[i].name);
+ }
+ CoreCLR::Free(foundAttributes);
+
+ _cachedAttributes = true;
+ return _attributes;
+ }
+
+ Array GetInterfaces()
+ {
+ if (_cachedInterfaces)
+ return _interfaces;
+
+ void** foundInterfaces;
+ int numInterfaces;
+
+ CoreCLR::CallStaticMethodInternal(TEXT("GetClassInterfaces"), _typeHandle, &foundInterfaces, &numInterfaces);
+ for (int i = 0; i < numInterfaces; i++)
+ {
+ CoreCLRClass* interfaceClass = classHandles[foundInterfaces[i]];
+ _interfaces.Add(interfaceClass);
+ }
+ CoreCLR::Free(foundInterfaces);
+
+ _cachedInterfaces = true;
+ return _interfaces;
+ }
+};
+
+struct CoreCLRMethod
+{
+private:
+ StringAnsi _name;
+ int _numParams;
+ CoreCLRClass* _class;
+ void* _methodHandle;
+ bool _cachedParameters = false;
+ Array _parameterTypes;
+ void* _returnType;
+ uint32 _methodAttributes;
+
+public:
+ CoreCLRMethod(StringAnsi name, int numParams, void* methodHandle, uint32 flags, CoreCLRClass* klass)
+ :_name(name), _numParams(numParams), _methodHandle(methodHandle), _methodAttributes(flags), _class(klass)
+ {
+ }
+
+ const StringAnsi& GetName()
+ {
+ return _name;
+ }
+
+ CoreCLRClass* GetClass()
+ {
+ return _class;
+ }
+
+ uint32 GetAttributes()
+ {
+ return _methodAttributes;
+ }
+
+ int GetNumParameters()
+ {
+ return _numParams;
+ }
+
+ void* GetMethodHandle()
+ {
+ return _methodHandle;
+ }
+
+ Array GetParameterTypes()
+ {
+ if (!_cachedParameters)
+ CacheParameters();
+ return _parameterTypes;
+ }
+
+ void* GetReturnType()
+ {
+ if (!_cachedParameters)
+ CacheParameters();
+ return _returnType;
+ }
+
+ void CacheParameters()
+ {
+ _returnType = CoreCLR::CallStaticMethodInternal(TEXT("GetMethodReturnType"), _methodHandle);
+
+ void** parameterTypeHandles;
+ CoreCLR::CallStaticMethodInternal(TEXT("GetMethodParameterTypes"), _methodHandle, ¶meterTypeHandles);
+
+ _parameterTypes.SetCapacity(_numParams, false);
+
+ for (int i = 0; i < _numParams; i++)
+ {
+ _parameterTypes.Add(parameterTypeHandles[i]);
+ }
+ CoreCLR::Free(parameterTypeHandles);
+
+ _cachedParameters = true;
+ }
+};
+
+struct CoreCLRField
+{
+private:
+ StringAnsi _name;
+ CoreCLRClass* _class;
+ void* _fieldHandle;
+ void* _fieldType;
+ uint32 _fieldAttributes;
+
+public:
+ CoreCLRField(StringAnsi name, void* fieldHandle, void* fieldType, uint32 fieldAttributes, CoreCLRClass* klass)
+ :_name(name), _fieldHandle(fieldHandle), _fieldType(fieldType), _fieldAttributes(fieldAttributes), _class(klass)
+ {
+ }
+
+ const StringAnsi& GetName()
+ {
+ return _name;
+ }
+
+ void* GetType()
+ {
+ return _fieldType;
+ }
+
+ CoreCLRClass* GetClass()
+ {
+ return _class;
+ }
+
+ uint32 GetAttributes()
+ {
+ return _fieldAttributes;
+ }
+
+ void* GetHandle()
+ {
+ return _fieldHandle;
+ }
+};
+
+struct CoreCLRProperty
+{
+private:
+ StringAnsi _name;
+ CoreCLRClass* _class;
+ CoreCLRMethod* _getMethod;
+ CoreCLRMethod* _setMethod;
+
+public:
+ CoreCLRProperty(StringAnsi name, void* getter, void* setter, uint32 getterFlags, uint32 setterFlags, CoreCLRClass* klass)
+ :_name(name), _class(klass)
+ {
+ if (getter != nullptr)
+ _getMethod = New(StringAnsi(_name + "Get"), 1, getter, getterFlags, klass);
+ if (setter != nullptr)
+ _setMethod = New(StringAnsi(_name + "Set"), 1, setter, setterFlags, klass);
+ }
+
+ const StringAnsi& GetName()
+ {
+ return _name;
+ }
+
+ CoreCLRClass* GetClass()
+ {
+ return _class;
+ }
+
+ CoreCLRMethod* GetGetMethod()
+ {
+ return _getMethod;
+ }
+
+ CoreCLRMethod* GetSetMethod()
+ {
+ return _setMethod;
+ }
+};
+
+struct CoreCLRCustomAttribute
+{
+private:
+ StringAnsi _name;
+ void* _handle;
+ CoreCLRClass* _owningClass;
+ CoreCLRClass* _attributeClass;
+
+public:
+ CoreCLRCustomAttribute(StringAnsi name, void* handle, CoreCLRClass* owningClass, CoreCLRClass* attributeClass)
+ :_name(name), _handle(handle), _owningClass(owningClass), _attributeClass(attributeClass)
+ {
+ }
+
+ void* GetHandle()
+ {
+ return _handle;
+ }
+
+ CoreCLRClass* GetClass()
+ {
+ return _attributeClass;
+ }
+};
+
+CoreCLRAssembly* GetAssembly(void* assemblyHandle)
+{
+ CoreCLRAssembly* assembly;
+ if (assemblyHandles.TryGet(assemblyHandle, assembly))
+ return assembly;
+ return nullptr;
+}
+
+CoreCLRClass* GetClass(void* type)
+{
+ CoreCLRClass* klass;
+ if (classHandles.TryGet(type, klass))
+ return klass;
+ return nullptr;
+}
+
+CoreCLRClass* GetOrCreateClass(void* type)
+{
+ CoreCLRClass* klass;
+ if (!classHandles.TryGet(type, klass))
+ {
+ ManagedClass classInfo;
+ void* assemblyHandle;
+ CoreCLR::CallStaticMethodInternal(TEXT("GetManagedClassFromType"), type, &classInfo, &assemblyHandle);
+ CoreCLRAssembly* image = GetAssembly(assemblyHandle);
+ klass = New(classInfo.typeHandle, StringAnsi(classInfo.name), StringAnsi(classInfo.fullname), StringAnsi(classInfo.namespace_), classInfo.typeAttributes, image);
+ if (image != nullptr)
+ image->AddClass(klass);
+
+ if (type != classInfo.typeHandle)
+ CoreCLR::CallStaticMethodInternal(TEXT("GetManagedClassFromType"), type, &classInfo);
+ classHandles.Add(classInfo.typeHandle, klass);
+
+ CoreCLR::Free((void*)classInfo.name);
+ CoreCLR::Free((void*)classInfo.fullname);
+ CoreCLR::Free((void*)classInfo.namespace_);
+ }
+ ASSERT(klass != nullptr);
+ return klass;
+}
+
+gchandle CoreCLR::NewGCHandle(void* obj, bool pinned)
+{
+ return (gchandle)CoreCLR::CallStaticMethodInternal(TEXT("NewGCHandle"), obj, pinned);
+}
+
+gchandle CoreCLR::NewGCHandleWeakref(void* obj, bool track_resurrection)
+{
+ return (gchandle)CoreCLR::CallStaticMethodInternal(TEXT("NewGCHandleWeakref"), obj, track_resurrection);
+}
+
+void* CoreCLR::GetGCHandleTarget(const gchandle& gchandle)
+{
+ return (void*)gchandle;
+}
+
+void CoreCLR::FreeGCHandle(const gchandle& gchandle)
+{
+ CoreCLR::CallStaticMethodInternal(TEXT("FreeGCHandle"), (void*)gchandle);
+}
+
+const char* CoreCLR::GetClassFullname(void* klass)
+{
+ return ((CoreCLRClass*)klass)->GetFullname().Get();
+}
+
+/*
+ * loader.h
+*/
+
+MONO_API MonoMethodSignature* mono_method_signature(MonoMethod* method)
+{
+ return (MonoMethodSignature*)method;
+}
+
+MONO_API const char* mono_method_get_name(MonoMethod* method)
+{
+ return ((CoreCLRMethod*)method)->GetName().Get();
+}
+
+MONO_API MonoClass* mono_method_get_class(MonoMethod* method)
+{
+ return (MonoClass*)((CoreCLRMethod*)method)->GetClass();
+}
+
+MONO_API uint32 mono_method_get_flags(MonoMethod* method, uint32* iflags)
+{
+ return ((CoreCLRMethod*)method)->GetAttributes();
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY void mono_add_internal_call(const char* name, const void* method)
+{
+ // Ignored, prevents the linker from removing unused functions
+}
+
+/*
+ * objects.h
+*/
+
+MONO_API mono_unichar2* mono_string_chars(MonoString* s)
+{
+ _MonoString* str = (_MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("GetStringPointer"), s);
+ return str->chars;
+}
+
+MONO_API int mono_string_length(MonoString* s)
+{
+ _MonoString* str = (_MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("GetStringPointer"), s);
+ return str->length;
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_object_new(MonoDomain* domain, MonoClass* klass)
+{
+ return (MonoObject*)CoreCLR::CallStaticMethodInternal(TEXT("NewObject"), ((CoreCLRClass*)klass)->GetTypeHandle());
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoArray* mono_array_new(MonoDomain* domain, MonoClass* eclass, uintptr_t n)
+{
+ return (MonoArray*)CoreCLR::CallStaticMethodInternal(TEXT("NewArray"), ((CoreCLRClass*)eclass)->GetTypeHandle(), n);
+}
+
+MONO_API char* mono_array_addr_with_size(MonoArray* array, int size, uintptr_t idx)
+{
+ return (char*)CoreCLR::CallStaticMethodInternal(TEXT("GetArrayPointerToElement"), array, size, (int)idx);
+}
+
+MONO_API uintptr_t mono_array_length(MonoArray* array)
+{
+ return CoreCLR::CallStaticMethodInternal(TEXT("GetArrayLength"), array);
+}
+
+MONO_API MonoString* mono_string_empty(MonoDomain* domain)
+{
+ return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("GetStringEmpty"));
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_string_new_utf16(MonoDomain* domain, const mono_unichar2* text, int32_t len)
+{
+ return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("NewStringUTF16"), text, len);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_string_new(MonoDomain* domain, const char* text)
+{
+ return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("NewString"), text);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_string_new_len(MonoDomain* domain, const char* text, unsigned int length)
+{
+ return (MonoString*)CoreCLR::CallStaticMethodInternal(TEXT("NewStringLength"), text, length);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY char* mono_string_to_utf8(MonoString* string_obj)
+{
+ Char* strw = string_obj != nullptr ? mono_string_chars(string_obj) : nullptr;
+ auto len = string_obj != nullptr ? mono_string_length(string_obj) : 0;
+ ASSERT(len >= 0)
+ char* stra = (char*)CoreCLR::Allocate(sizeof(char) * (len + 1));
+ StringUtils::ConvertUTF162UTF8(strw, stra, len, len);
+ stra[len] = 0;
+
+ return stra;
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoString* mono_object_to_string(MonoObject* obj, MonoObject** exc)
+{
+ ASSERT(false);
+}
+
+MONO_API int mono_object_hash(MonoObject* obj)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_value_box(MonoDomain* domain, MonoClass* klass, void* val)
+{
+ return (MonoObject*)CoreCLR::CallStaticMethodInternal(TEXT("BoxValue"), ((CoreCLRClass*)klass)->GetTypeHandle(), val);
+}
+
+MONO_API void mono_value_copy(void* dest, /*const*/ void* src, MonoClass* klass)
+{
+ CoreCLRClass* mci = (CoreCLRClass*)klass;
+ Platform::MemoryCopy(dest, src, mci->GetSize());
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_object_get_class(MonoObject* obj)
+{
+ void* classHandle = CoreCLR::CallStaticMethodInternal(TEXT("GetObjectType"), obj);
+
+ CoreCLRClass* mi = GetOrCreateClass((void*)classHandle);
+
+ ASSERT(mi != nullptr)
+ return (MonoClass*)mi;
+}
+
+MONO_API void* mono_object_unbox(MonoObject* obj)
+{
+ return CoreCLR::CallStaticMethodInternal(TEXT("UnboxValue"), obj);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY void mono_raise_exception(MonoException* ex)
+{
+ CoreCLR::CallStaticMethodInternal(TEXT("RaiseException"), ex);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY void mono_runtime_object_init(MonoObject* this_obj)
+{
+ CoreCLR::CallStaticMethodInternal(TEXT("ObjectInit"), this_obj);
+}
+
+MONO_API MonoMethod* mono_object_get_virtual_method(MonoObject* obj, MonoMethod* method)
+{
+ return method;
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_runtime_invoke(MonoMethod* method, void* obj, void** params, MonoObject** exc)
+{
+ CoreCLRMethod* mi = (CoreCLRMethod*)method;
+ void* methodPtr = mi->GetMethodHandle();
+ ASSERT(methodPtr != nullptr)
+
+ static void* InvokeMethodPtr = CoreCLR::GetStaticMethodPointer(TEXT("InvokeMethod"));
+ return (MonoObject*)CoreCLR::CallStaticMethodInternalPointer(InvokeMethodPtr, obj, methodPtr, params, exc);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY void* mono_method_get_unmanaged_thunk(MonoMethod* method)
+{
+ CoreCLRMethod* mi = (CoreCLRMethod*)method;
+ void* methodPtr = mi->GetMethodHandle();
+ ASSERT(methodPtr != nullptr)
+
+ return CoreCLR::CallStaticMethodInternal(TEXT("GetMethodUnmanagedFunctionPointer"), methodPtr);
+}
+
+MONO_API void mono_field_set_value(MonoObject* obj, MonoClassField* field, void* value)
+{
+ CoreCLR::CallStaticMethodInternal(TEXT("FieldSetValue"), obj, ((CoreCLRField*)field)->GetHandle(), value);
+}
+
+MONO_API void mono_field_get_value(MonoObject* obj, MonoClassField* field, void* value)
+{
+ CoreCLR::CallStaticMethodInternal(TEXT("FieldGetValue"), obj, ((CoreCLRField*)field)->GetHandle(), value);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_field_get_value_object(MonoDomain* domain, MonoClassField* field, MonoObject* obj)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY void mono_property_set_value(MonoProperty* prop, void* obj, void** params, MonoObject** exc)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_property_get_value(MonoProperty* prop, void* obj, void** params, MonoObject** exc)
+{
+ ASSERT(false);
+}
+
+MONO_API void mono_gc_wbarrier_set_field(MonoObject* obj, void* field_ptr, MonoObject* value)
+{
+ ASSERT(false);
+}
+MONO_API void mono_gc_wbarrier_set_arrayref(MonoArray* arr, void* slot_ptr, MonoObject* value)
+{
+ CoreCLR::CallStaticMethodInternal(TEXT("SetArrayValueReference"), arr, slot_ptr, value);
+}
+MONO_API void mono_gc_wbarrier_generic_store(void* ptr, MonoObject* value)
+{
+ // Ignored
+ *((void**)ptr) = value;
+}
+MONO_API void mono_gc_wbarrier_value_copy(void* dest, /*const*/ void* src, int count, MonoClass* klass)
+{
+ // Ignored
+ int size = ((CoreCLRClass*)klass)->GetSize();
+ memcpy(dest, src, count * size);
+}
+
+/*
+ * appdomain.h
+*/
+
+MonoDomain* currentDomain = nullptr;
+
+MONO_API MonoDomain* mono_domain_get(void)
+{
+ return currentDomain;
+}
+
+MONO_API mono_bool mono_domain_set(MonoDomain* domain, mono_bool force)
+{
+ currentDomain = domain;
+ return true;
+}
+
+MONO_API MonoAssembly* mono_domain_assembly_open(MonoDomain* domain, const char* path)
+{
+ const char* name;
+ const char* fullname;
+ void* assemblyHandle = CoreCLR::CallStaticMethodInternal(TEXT("LoadAssemblyFromPath"), path, &name, &fullname);
+ CoreCLRAssembly* assembly = New(assemblyHandle, name, fullname);
+
+ CoreCLR::Free((void*)name);
+ CoreCLR::Free((void*)fullname);
+
+ return (MonoAssembly*)assembly;
+}
+
+static CoreCLRAssembly* corlibimage = nullptr;
+
+MONO_API MonoImage* mono_get_corlib(void)
+{
+ if (corlibimage == nullptr)
+ {
+ const char* name;
+ const char* fullname;
+ void* assemblyHandle = CoreCLR::CallStaticMethodInternal(TEXT("GetAssemblyByName"), "System.Private.CoreLib", &name, &fullname);
+ corlibimage = New(assemblyHandle, name, fullname);
+
+ CoreCLR::Free((void*)name);
+ CoreCLR::Free((void*)fullname);
+ }
+
+ return (MonoImage*)corlibimage;
+}
+
+#define CACHE_CLASS_BY_NAME(name) \
+nullptr; \
+if (klass == nullptr) \
+ for (CoreCLRClass* k : corlibimage->GetClasses()) \
+ if (k->GetFullname() == name) \
+ { \
+ klass = k; \
+ break; \
+ }
+
+MONO_API MonoClass* mono_get_object_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Object");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_byte_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Byte");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_void_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Void");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_boolean_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Boolean");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_sbyte_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.SByte");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_int16_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Int16");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_uint16_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.UInt16");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_int32_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Int32");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_uint32_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.UInt32");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_intptr_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.IntPtr");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_uintptr_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.UIntPtr");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_int64_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Int64");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_uint64_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.UInt64");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_single_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Single");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_double_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Double");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_char_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.Char");
+ return (MonoClass*)klass;
+}
+
+MONO_API MonoClass* mono_get_string_class(void)
+{
+ static CoreCLRClass* klass = CACHE_CLASS_BY_NAME("System.String");
+ return (MonoClass*)klass;
+}
+
+/*
+ * jit.h
+*/
+
+MONO_API char* mono_get_runtime_build_info(void)
+{
+ return CoreCLR::CallStaticMethodInternal(TEXT("GetRuntimeInformation"));
+}
+
+/*
+ * assembly.h
+*/
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoAssembly* mono_assembly_load_from_full(MonoImage* image, const char* fname, MonoImageOpenStatus* status, mono_bool refonly)
+{
+ auto assembly = (MonoAssembly*)((CoreCLRAssembly*)image);
+ *status = MONO_IMAGE_OK;
+ return assembly;
+}
+
+MONO_API void mono_assembly_close(MonoAssembly* assembly)
+{
+ CoreCLR::CallStaticMethodInternal(TEXT("CloseAssembly"), ((CoreCLRAssembly*)assembly)->GetHandle());
+
+ Delete((CoreCLRAssembly*)assembly);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoImage* mono_assembly_get_image(MonoAssembly* assembly)
+{
+ return (MonoImage*)((CoreCLRAssembly*)assembly);
+}
+
+/*
+ * threads.h
+*/
+
+static MonoThread* notImplMonoThreadValue = New();
+
+MONO_API MonoThread* mono_thread_current(void)
+{
+ // Ignored
+ return notImplMonoThreadValue;
+}
+
+MONO_API MonoThread* mono_thread_attach(MonoDomain* domain)
+{
+ // Ignored
+ return notImplMonoThreadValue;
+}
+
+MONO_API void mono_thread_exit(void)
+{
+ // Ignored
+}
+
+/*
+ * mono-debug.h
+*/
+
+MONO_API void mono_debug_open_image_from_memory(MonoImage* image, const mono_byte* raw_contents, int size)
+{
+ // Ignored
+}
+
+/*
+ * reflection.h
+*/
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoReflectionAssembly* mono_assembly_get_object(MonoDomain* domain, MonoAssembly* assembly)
+{
+ return (MonoReflectionAssembly*)CoreCLR::CallStaticMethodInternal(TEXT("GetAssemblyObject"), ((CoreCLRAssembly*)assembly)->GetFullname().Get());
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoReflectionType* mono_type_get_object(MonoDomain* domain, MonoType* type)
+{
+ return (MonoReflectionType*)type;
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoArray* mono_custom_attrs_construct(MonoCustomAttrInfo* cinfo)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoCustomAttrInfo* mono_custom_attrs_from_method(MonoMethod* method)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoCustomAttrInfo* mono_custom_attrs_from_class(MonoClass* klass)
+{
+ CoreCLRClass* mi = (CoreCLRClass*)klass;
+ MonoCustomAttrInfo* info = (MonoCustomAttrInfo*)New>(mi->GetCustomAttributes());
+ return info;
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoCustomAttrInfo* mono_custom_attrs_from_property(MonoClass* klass, MonoProperty* property)
+{
+ ASSERT(false);
+}
+MONO_API MONO_RT_EXTERNAL_ONLY MonoCustomAttrInfo* mono_custom_attrs_from_event(MonoClass* klass, MonoEvent* event)
+{
+ ASSERT(false);
+}
+MONO_API MONO_RT_EXTERNAL_ONLY MonoCustomAttrInfo* mono_custom_attrs_from_field(MonoClass* klass, MonoClassField* field)
+{
+ ASSERT(false);
+}
+
+MONO_API mono_bool mono_custom_attrs_has_attr(MonoCustomAttrInfo* ainfo, MonoClass* attr_klass)
+{
+ Array* attribs = (Array*)ainfo;
+ for (int i = 0; i < attribs->Count(); i++)
+ {
+ CoreCLRCustomAttribute* attrib = attribs->At(i);
+ if (attrib->GetClass() == (CoreCLRClass*)attr_klass)
+ return true;
+ }
+ return false;
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoObject* mono_custom_attrs_get_attr(MonoCustomAttrInfo* ainfo, MonoClass* attr_klass)
+{
+ Array* attribs = (Array*)ainfo;
+ for (int i = 0; i < attribs->Count(); i++)
+ {
+ CoreCLRCustomAttribute* attrib = attribs->At(i);
+ if (attrib->GetClass() == (CoreCLRClass*)attr_klass)
+ {
+ return (MonoObject*)(attrib)->GetHandle();
+ }
+ }
+ return nullptr;
+}
+
+MONO_API void mono_custom_attrs_free(MonoCustomAttrInfo* ainfo)
+{
+ Array* mcai = (Array*)ainfo;
+ Delete(mcai);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoType* mono_reflection_type_get_type(MonoReflectionType* reftype)
+{
+ return (MonoType*)reftype;
+}
+
+/*
+ * class.h
+*/
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_class_get(MonoImage* image, uint32 type_token)
+{
+ int index = type_token - 0x02000000 - 2; //MONO_TOKEN_TYPE_DEF
+ auto classes = ((CoreCLRAssembly*)image)->GetClasses();
+ return (MonoClass*)classes[index];
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_class_from_name(MonoImage* image, const char* name_space_, const char* name_)
+{
+ StringAnsi name_space(name_space_);
+ StringAnsi name(name_);
+
+ CoreCLRAssembly* mi = (CoreCLRAssembly*)image;
+ for (auto klass : mi->GetClasses())
+ {
+ if (klass->GetNamespace() == name_space && klass->GetName() == name)
+ return (MonoClass*)klass;
+ }
+
+ return nullptr;
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoMethod* mono_class_inflate_generic_method(MonoMethod* method, MonoGenericContext* context)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_array_class_get(MonoClass* element_class, uint32 rank)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClassField* mono_class_get_field_from_name(MonoClass* klass, const char* name)
+{
+ StringAnsi name2(name);
+ CoreCLRClass* mi = (CoreCLRClass*)klass;
+ for (auto field : mi->GetFields())
+ {
+ if (field->GetName() == name2)
+ return (MonoClassField*)field;
+ }
+ return nullptr;
+}
+
+MONO_API MonoProperty* mono_class_get_property_from_name(MonoClass* klass, const char* name)
+{
+ StringAnsi name2(name);
+ CoreCLRClass* mi = (CoreCLRClass*)klass;
+ for (auto prop : mi->GetProperties())
+ {
+ if (prop->GetName() == name2)
+ return (MonoProperty*)prop;
+ }
+ return nullptr;
+}
+
+MONO_API int32_t mono_class_instance_size(MonoClass* klass)
+{
+ ASSERT(false);
+}
+
+MONO_API int32_t mono_class_value_size(MonoClass* klass, uint32* align)
+{
+ return CoreCLR::CallStaticMethodInternal(TEXT("NativeSizeOf"), ((CoreCLRClass*)klass)->GetTypeHandle(), align);
+}
+
+MONO_API MonoClass* mono_class_from_mono_type(MonoType* type)
+{
+ CoreCLRClass* klass = GetOrCreateClass((void*)type);
+ return (MonoClass*)klass;
+}
+
+MONO_API mono_bool mono_class_is_subclass_of(MonoClass* klass, MonoClass* klassc, mono_bool check_interfaces)
+{
+ return CoreCLR::CallStaticMethodInternal(TEXT("TypeIsSubclassOf"), ((CoreCLRClass*)klass)->GetTypeHandle(), ((CoreCLRClass*)klassc)->GetTypeHandle(), check_interfaces);
+}
+
+MONO_API char* mono_type_get_name(MonoType* type)
+{
+ CoreCLRClass* mci = (CoreCLRClass*)mono_type_get_class(type);
+ return StringAnsi(mci->GetFullname()).Get();
+}
+
+MONO_API MonoImage* mono_class_get_image(MonoClass* klass)
+{
+ return (MonoImage*)((CoreCLRClass*)klass)->GetAssembly();
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_class_get_element_class(MonoClass* klass)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY mono_bool mono_class_is_valuetype(MonoClass* klass)
+{
+ return (mono_bool)CoreCLR::CallStaticMethodInternal(TEXT("TypeIsValueType"), ((CoreCLRClass*)klass)->GetTypeHandle());
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY mono_bool mono_class_is_enum(MonoClass* klass)
+{
+ return (mono_bool)CoreCLR::CallStaticMethodInternal(TEXT("TypeIsEnum"), ((CoreCLRClass*)klass)->GetTypeHandle());
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClass* mono_class_get_parent(MonoClass* klass)
+{
+ void* parentHandle = CoreCLR::CallStaticMethodInternal(TEXT("GetClassParent"), ((CoreCLRClass*)klass)->GetTypeHandle());
+ return (MonoClass*)classHandles[parentHandle];
+}
+
+MONO_API MonoClass* mono_class_get_nesting_type(MonoClass* klass)
+{
+ // Ignored
+ return nullptr;
+}
+
+MONO_API uint32 mono_class_get_flags(MonoClass* klass)
+{
+ return ((CoreCLRClass*)klass)->GetAttributes();
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY const char* mono_class_get_name(MonoClass* klass)
+{
+ return ((CoreCLRClass*)klass)->GetName().Get();
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY const char* mono_class_get_namespace(MonoClass* klass)
+{
+ return ((CoreCLRClass*)klass)->GetNamespace().Get();
+}
+
+MONO_API MonoType* mono_class_get_type(MonoClass* klass)
+{
+ return (MonoType*)((CoreCLRClass*)klass)->GetTypeHandle();
+}
+
+MONO_API uint32 mono_class_get_type_token(MonoClass* klass)
+{
+ return ((CoreCLRClass*)klass)->GetTypeToken();
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoClassField* mono_class_get_fields(MonoClass* klass, void** iter)
+{
+ auto fields = ((CoreCLRClass*)klass)->GetFields();
+ uintptr_t index = (uintptr_t)(*iter);
+ if (index >= 0 && index < fields.Count())
+ {
+ *iter = (void*)(index + 1);
+ return (MonoClassField*)fields[(int)index];
+ }
+ *iter = nullptr;
+ return nullptr;
+}
+
+MONO_API MonoMethod* mono_class_get_methods(MonoClass* klass, void** iter)
+{
+ auto methods = ((CoreCLRClass*)klass)->GetMethods();
+ uintptr_t index = (uintptr_t)(*iter);
+ if (index >= 0 && index < methods.Count())
+ {
+ *iter = (void*)(index + 1);
+ return (MonoMethod*)methods[(int)index];
+ }
+ *iter = nullptr;
+ return nullptr;
+}
+
+MONO_API MonoProperty* mono_class_get_properties(MonoClass* klass, void** iter)
+{
+ auto properties = ((CoreCLRClass*)klass)->GetProperties();
+ uintptr_t index = (uintptr_t)(*iter);
+ if (index >= 0 && index < properties.Count())
+ {
+ *iter = (void*)(index + 1);
+ return (MonoProperty*)properties[(int)index];
+ }
+ *iter = nullptr;
+ return nullptr;
+}
+
+MONO_API MonoEvent* mono_class_get_events(MonoClass* klass, void** iter)
+{
+ ASSERT(false);
+}
+
+MONO_API MonoClass* mono_class_get_interfaces(MonoClass* klass, void** iter)
+{
+ auto interfaces = ((CoreCLRClass*)klass)->GetInterfaces();
+ uintptr_t index = (uintptr_t)(*iter);
+ if (index >= 0 && index < interfaces.Count())
+ {
+ *iter = (void*)(index + 1);
+ return (MonoClass*)interfaces[(int)index];
+ }
+ *iter = nullptr;
+ return nullptr;
+}
+
+MONO_API const char* mono_field_get_name(MonoClassField* field)
+{
+ return ((CoreCLRField*)field)->GetName().Get();
+}
+
+MONO_API MonoType* mono_field_get_type(MonoClassField* field)
+{
+ return (MonoType*)((CoreCLRField*)field)->GetClass()->GetTypeHandle();
+}
+
+MONO_API MonoClass* mono_field_get_parent(MonoClassField* field)
+{
+ ASSERT(false);
+}
+
+MONO_API uint32 mono_field_get_flags(MonoClassField* field)
+{
+ return ((CoreCLRField*)field)->GetAttributes();
+}
+
+MONO_API uint32 mono_field_get_offset(MonoClassField* field)
+{
+ ASSERT(false);
+}
+
+MONO_API const char* mono_property_get_name(MonoProperty* prop)
+{
+ return ((CoreCLRProperty*)prop)->GetName().Get();
+}
+
+MONO_API MonoMethod* mono_property_get_set_method(MonoProperty* prop)
+{
+ return (MonoMethod*)((CoreCLRProperty*)prop)->GetSetMethod();
+}
+
+MONO_API MonoMethod* mono_property_get_get_method(MonoProperty* prop)
+{
+ return (MonoMethod*)((CoreCLRProperty*)prop)->GetGetMethod();
+}
+
+MONO_API MonoClass* mono_property_get_parent(MonoProperty* prop)
+{
+ return (MonoClass*)((CoreCLRProperty*)prop)->GetClass();
+}
+
+MONO_API const char* mono_event_get_name(MonoEvent* event)
+{
+ ASSERT(false);
+}
+
+MONO_API MonoMethod* mono_event_get_add_method(MonoEvent* event)
+{
+ ASSERT(false);
+}
+
+MONO_API MonoMethod* mono_event_get_remove_method(MonoEvent* event)
+{
+ ASSERT(false);
+}
+
+MONO_API MonoClass* mono_event_get_parent(MonoEvent* event)
+{
+ ASSERT(false);
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoMethod* mono_class_get_method_from_name(MonoClass* klass, const char* name, int param_count)
+{
+ for (CoreCLRMethod* method : ((CoreCLRClass*)klass)->GetMethods())
+ {
+ if (method->GetName() == name && method->GetNumParameters() == param_count)
+ return (MonoMethod*)method;
+ }
+
+ return nullptr;
+}
+
+/*
+ * mono-publib.h
+*/
+
+MONO_API void mono_free(void* ptr)
+{
+ if (ptr != nullptr)
+ CoreCLR::Free(ptr);
+}
+
+/*
+ * metadata.h
+*/
+
+MONO_API mono_bool mono_type_is_byref(MonoType* type)
+{
+ ASSERT(false);
+}
+
+MONO_API int mono_type_get_type(MonoType* type)
+{
+ return CoreCLR::CallStaticMethodInternal(TEXT("GetTypeMonoTypeEnum"), type);
+}
+
+MONO_API MonoClass* mono_type_get_class(MonoType* type)
+{
+ return (MonoClass*)classHandles[(void*)type];
+}
+
+MONO_API mono_bool mono_type_is_struct(MonoType* type)
+{
+ ASSERT(false);
+}
+
+MONO_API mono_bool mono_type_is_void(MonoType* type)
+{
+ ASSERT(false);
+}
+
+MONO_API mono_bool mono_type_is_pointer(MonoType* type)
+{
+ ASSERT(false);
+}
+
+MONO_API mono_bool mono_type_is_reference(MonoType* type)
+{
+ ASSERT(false);
+}
+
+MONO_API MonoType* mono_signature_get_return_type(MonoMethodSignature* sig)
+{
+ CoreCLRMethod* mi = (CoreCLRMethod*)sig;
+ return (MonoType*)mi->GetReturnType();
+}
+
+MONO_API MonoType* mono_signature_get_params(MonoMethodSignature* sig, void** iter)
+{
+ auto parameterTypes = ((CoreCLRMethod*)sig)->GetParameterTypes();
+ uintptr_t index = (uintptr_t)(*iter);
+ if (index >= 0 && index < parameterTypes.Count())
+ {
+ *iter = (void*)(index+1);
+ return (MonoType*)parameterTypes[(int)index];
+ }
+ *iter = nullptr;
+ return nullptr;
+}
+
+MONO_API uint32 mono_signature_get_param_count(MonoMethodSignature* sig)
+{
+ CoreCLRMethod* mi = (CoreCLRMethod*)sig;
+ return mi->GetNumParameters();
+}
+
+MONO_API mono_bool mono_signature_param_is_out(MonoMethodSignature* sig, int param_num)
+{
+ CoreCLRMethod* mi = (CoreCLRMethod*)sig;
+ return CoreCLR::CallStaticMethodInternal(TEXT("GetMethodParameterIsOut"), mi->GetMethodHandle(), param_num);
+}
+
+MONO_API int mono_type_stack_size(MonoType* type, int* alignment)
+{
+ ASSERT(false);
+}
+
+/*
+ * exception.h
+*/
+
+MONO_API MonoException* mono_exception_from_name_msg(MonoImage* image, const char* name_space, const char* name, const char* msg)
+{
+ ASSERT(false);
+}
+
+MONO_API MonoException* mono_get_exception_null_reference(void)
+{
+ return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetNullReferenceException"));
+}
+
+MONO_API MonoException* mono_get_exception_not_supported(const char* msg)
+{
+ return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetNotSupportedException"));
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoException* mono_get_exception_argument_null(const char* arg)
+{
+ return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetArgumentNullException"));
+}
+
+MONO_API MonoException* mono_get_exception_argument(const char* arg, const char* msg)
+{
+ return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetArgumentException"));
+}
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoException* mono_get_exception_argument_out_of_range(const char* arg)
+{
+ return (MonoException*)CoreCLR::CallStaticMethodInternal(TEXT("GetArgumentOutOfRangeException"));
+}
+
+/*
+ * image.h
+*/
+
+MONO_API MONO_RT_EXTERNAL_ONLY MonoImage* mono_image_open_from_data_with_name(char* data, uint32 data_len, mono_bool need_copy, MonoImageOpenStatus* status, mono_bool refonly, const char* path)
+{
+ const char* name;
+ const char* fullname;
+ void* assemblyHandle = CoreCLR::CallStaticMethodInternal(TEXT("LoadAssemblyImage"), data, data_len, path, &name, &fullname);
+ CoreCLRAssembly* assembly = New(assemblyHandle, name, fullname);
+
+ CoreCLR::Free((void*)name);
+ CoreCLR::Free((void*)fullname);
+
+ *status = MONO_IMAGE_OK;
+ return (MonoImage*)assembly;
+}
+
+MONO_API void mono_image_close(MonoImage* image)
+{
+ // Ignored
+}
+MONO_API const char* mono_image_get_name(MonoImage* image)
+{
+ return ((CoreCLRAssembly*)image)->GetName().Get();
+}
+MONO_API MonoAssembly* mono_image_get_assembly(MonoImage* image)
+{
+ return (MonoAssembly*)image;
+}
+MONO_API int mono_image_get_table_rows(MonoImage* image, int table_id)
+{
+ return ((CoreCLRAssembly*)image)->GetClasses().Count() + 1;
+}
+
+/*
+ * mono-gc.h
+*/
+
+MONO_API void mono_gc_collect(int generation)
+{
+ // Ignored
+}
+
+MONO_API int mono_gc_max_generation(void)
+{
+ // Ignored
+ return 0;
+}
+
+MONO_API MonoBoolean mono_gc_pending_finalizers(void)
+{
+ // Ignored
+ return false;
+}
+
+MONO_API void mono_gc_finalize_notify(void)
+{
+ // Ignored
+}
+
+#pragma warning(default : 4297)
diff --git a/Source/Engine/Scripting/InternalCalls.h b/Source/Engine/Scripting/InternalCalls.h
index 8e7ef9d27..2e8a09aa5 100644
--- a/Source/Engine/Scripting/InternalCalls.h
+++ b/Source/Engine/Scripting/InternalCalls.h
@@ -52,7 +52,9 @@ extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const vo
#else
-#define ADD_INTERNAL_CALL(fullName, method)
+extern void DotNetAddInternalCall(const wchar_t* fullName, void* function);
+
+#define ADD_INTERNAL_CALL(fullName, method) DotNetAddInternalCall(TEXT(fullName), (void*)(method))
#define INTERNAL_CALL_CHECK(obj)
#define INTERNAL_CALL_CHECK_EXP(expression)
#define INTERNAL_CALL_CHECK_RETURN(obj, defaultValue)
diff --git a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp
index d1657d9f3..3e636a65d 100644
--- a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp
+++ b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp
@@ -6,17 +6,20 @@
#include "Engine/Scripting/MException.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
-#if USE_MONO
-
namespace UtilsInternal
{
MonoObject* ExtractArrayFromList(MonoObject* obj)
{
+#if USE_MONO
auto klass = mono_object_get_class(obj);
auto field = mono_class_get_field_from_name(klass, "_items");
MonoObject* o;
mono_field_get_value(obj, field, &o);
return o;
+#else
+ SCRIPTING_EXPORT("FlaxEngine.Utils::Internal_ExtractArrayFromList")
+ return nullptr;
+#endif
}
}
@@ -24,6 +27,7 @@ namespace DebugLogHandlerInternal
{
void LogWrite(LogType level, MonoString* msgObj)
{
+ SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_LogWrite")
StringView msg;
MUtils::ToString(msgObj, msg);
Log::Logger::Write(level, msg);
@@ -31,6 +35,8 @@ namespace DebugLogHandlerInternal
void Log(LogType level, MonoString* msgObj, ScriptingObject* obj, MonoString* stackTrace)
{
+ SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_Log")
+
if (msgObj == nullptr)
return;
@@ -45,8 +51,11 @@ namespace DebugLogHandlerInternal
Log::Logger::Write(level, msg);
}
+
void LogException(MonoException* exception, ScriptingObject* obj)
{
+ SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_LogException")
+#if USE_MONO
if (exception == nullptr)
return;
@@ -57,13 +66,16 @@ namespace DebugLogHandlerInternal
// Print exception including inner exceptions
// TODO: maybe option for build to threat warnings and errors as fatal errors?
ex.Log(LogType::Warning, objName.GetText());
+#endif
}
+
}
namespace FlaxLogWriterInternal
{
void WriteStringToLog(MonoString* msgObj)
{
+ SCRIPTING_EXPORT("FlaxEngine.FlaxLogWriter::Internal_WriteStringToLog")
if (msgObj == nullptr)
return;
StringView msg;
@@ -72,8 +84,6 @@ namespace FlaxLogWriterInternal
}
}
-#endif
-
void registerFlaxEngineInternalCalls()
{
AnimGraphExecutor::initRuntime();
diff --git a/Source/Engine/Scripting/InternalCalls/ManagedDictionary.h b/Source/Engine/Scripting/InternalCalls/ManagedDictionary.h
index 1d123e067..05548667b 100644
--- a/Source/Engine/Scripting/InternalCalls/ManagedDictionary.h
+++ b/Source/Engine/Scripting/InternalCalls/ManagedDictionary.h
@@ -86,7 +86,11 @@ struct FLAXENGINE_API ManagedDictionary
CHECK_RETURN(makeGenericMethod, nullptr);
auto genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass->GetNative());
+#if USE_NETCORE
+ auto genericArgs = mono_array_new(domain, mono_get_intptr_class(), 2);
+#else
auto genericArgs = mono_array_new(domain, mono_get_object_class(), 2);
+#endif
mono_array_set(genericArgs, MonoReflectionType*, 0, mono_type_get_object(domain, keyType));
mono_array_set(genericArgs, MonoReflectionType*, 1, mono_type_get_object(domain, valueType));
diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp
index f7b46e5cc..2f482742d 100644
--- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp
+++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp
@@ -12,6 +12,10 @@
#include "Engine/Platform/Thread.h"
#include "Engine/Scripting/MException.h"
#include "Engine/Profiler/ProfilerCPU.h"
+#include "Engine/Platform/FileSystem.h"
+#if USE_NETCORE
+#include "Engine/Scripting/DotNet/CoreCLR.h"
+#endif
#if USE_MONO
#ifdef USE_MONO_AOT_MODULE
#include "Engine/Core/Types/TimeSpan.h"
@@ -52,6 +56,9 @@ MDomain* MCore::GetActiveDomain()
MDomain* MCore::CreateDomain(const MString& domainName)
{
+#if USE_NETCORE
+ return nullptr;
+#else
#if USE_MONO_AOT
LOG(Fatal, "Scripts can run only in single domain mode with AOT mode enabled.");
return nullptr;
@@ -74,10 +81,13 @@ MDomain* MCore::CreateDomain(const MString& domainName)
#endif
MDomains.Add(domain);
return domain;
+#endif
}
void MCore::UnloadDomain(const MString& domainName)
{
+#if USE_NETCORE
+#else
int32 i = 0;
for (; i < MDomains.Count(); i++)
{
@@ -103,9 +113,46 @@ void MCore::UnloadDomain(const MString& domainName)
#endif
Delete(domain);
MDomains.RemoveAtKeepOrder(i);
+#endif
}
-#if USE_MONO
+#if USE_NETCORE
+bool MCore::LoadEngine()
+{
+ const String csharpLibraryPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
+ const String csharpRuntimeConfigPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.runtimeconfig.json");
+ if (!FileSystem::FileExists(csharpLibraryPath))
+ LOG(Fatal, "LoadHostfxr failed");
+ if (!FileSystem::FileExists(csharpRuntimeConfigPath))
+ LOG(Fatal, "LoadHostfxr failed");
+
+ // Locate hostfxr and load it
+ if (!CoreCLR::LoadHostfxr(csharpLibraryPath))
+ LOG(Fatal, "LoadHostfxr failed");
+
+ // Initialize hosting component
+ if (!CoreCLR::InitHostfxr(csharpRuntimeConfigPath, csharpLibraryPath))
+ LOG(Fatal, "LoadAssembly failed");
+
+ const String hostExecutable = Platform::GetExecutableFilePath();
+ CoreCLR::CallStaticMethodInternal(TEXT("Init"), hostExecutable.Get());
+
+ MRootDomain = New("Root");
+ MDomains.Add(MRootDomain);
+
+ /*char* buildInfo = mono_get_runtime_build_info();
+ LOG(Info, "Managed runtime version: {0} (.NET)", String(buildInfo));
+ mono_free(buildInfo);*/
+
+ return false;
+}
+
+void MCore::UnloadEngine()
+{
+ MDomains.ClearDelete();
+ MRootDomain = nullptr;
+}
+#elif USE_MONO
#if 0
@@ -518,7 +565,7 @@ bool MCore::LoadEngine()
}
#endif
- // Init Mono
+ // Init managed runtime
#if PLATFORM_ANDROID
const char* monoVersion = "mobile";
#else
@@ -530,6 +577,7 @@ bool MCore::LoadEngine()
MRootDomain->_monoDomain = monoRootDomain;
MDomains.Add(MRootDomain);
+#if !USE_NETCORE
auto exePath = Platform::GetExecutableFilePath();
auto configDir = StringUtils::GetDirectoryName(exePath).ToStringAnsi();
auto configFilename = StringUtils::GetFileName(exePath).ToStringAnsi() + ".config";
@@ -542,10 +590,11 @@ bool MCore::LoadEngine()
#endif
mono_domain_set_config(monoRootDomain, configDir.Get(), configFilename.Get());
mono_thread_set_main(mono_thread_current());
+#endif
// Info
char* buildInfo = mono_get_runtime_build_info();
- LOG(Info, "Mono version: {0}", String(buildInfo));
+ LOG(Info, "Managed runtime version: {0} (Mono)", String(buildInfo));
mono_free(buildInfo);
return false;
@@ -672,7 +721,7 @@ void MCore::GC::WaitForPendingFinalizers()
#endif
}
-#if USE_MONO && PLATFORM_WIN32 && !USE_MONO_DYNAMIC_LIB
+#if USE_MONO && PLATFORM_WIN32 && !USE_MONO_DYNAMIC_LIB && !USE_NETCORE
// Export Mono functions
#pragma comment(linker, "/export:mono_add_internal_call")
diff --git a/Source/Engine/Scripting/ManagedCLR/MMethod.h b/Source/Engine/Scripting/ManagedCLR/MMethod.h
index 04d774c1e..eda326856 100644
--- a/Source/Engine/Scripting/ManagedCLR/MMethod.h
+++ b/Source/Engine/Scripting/ManagedCLR/MMethod.h
@@ -84,7 +84,7 @@ public:
///
///
/// This is the fastest way of calling managed code.
- /// Get thunk from class if you want to call static method. You to call it from method of a instance wrapper to call a specific instance.
+ /// Get thunk from class if you want to call static method. You need to call it from method of a instance wrapper to call a specific instance.
///
/// The method thunk pointer.
void* GetThunk();
diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
index ce54b77ac..c3fc9bb83 100644
--- a/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
+++ b/Source/Engine/Scripting/ManagedCLR/MUtils.cpp
@@ -24,6 +24,10 @@
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Content/Asset.h"
+#if USE_NETCORE
+#include "Engine/Scripting/DotNet/CoreCLR.h"
+#endif
+
#if USE_MONO
// Inlined mono private types to access MonoType internals
@@ -356,11 +360,17 @@ Variant MUtils::UnboxVariant(MonoObject* value)
return Variant::Null;
const auto& stdTypes = *StdTypesContainer::Instance();
const auto klass = mono_object_get_class(value);
+
+ MonoType* monoType = mono_class_get_type(klass);
+ const MonoTypeEnum monoTypeId = (MonoTypeEnum)mono_type_get_type(monoType);
+#if USE_NETCORE
+ void* unboxed = mono_object_unbox(value);
+#else
void* unboxed = (byte*)value + sizeof(MonoObject);
- const MonoType* monoType = mono_class_get_type(klass);
+#endif
// Fast type detection for in-built types
- switch (monoType->type)
+ switch (monoTypeId)
{
case MONO_TYPE_VOID:
return Variant(VariantType(VariantType::Void));
@@ -660,7 +670,11 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
case VariantType::Guid:
return mono_value_box(mono_domain_get(), stdTypes.GuidClass->GetNative(), (void*)&value.AsData);
case VariantType::String:
+#if USE_NETCORE
return (MonoObject*)MUtils::ToString((StringView)value);
+#else
+ return (MonoObject*)MUtils::ToString((StringView)value);
+#endif
case VariantType::Quaternion:
return mono_value_box(mono_domain_get(), stdTypes.QuaternionClass->GetNative(), (void*)&value.AsData);
case VariantType::BoundingSphere:
@@ -844,7 +858,11 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
return nullptr;
}
case VariantType::ManagedObject:
- return value.AsUint ? mono_gchandle_get_target(value.AsUint) : nullptr;
+#if USE_NETCORE
+ return value.AsUint64 ? MUtils::GetGCHandleTarget(value.AsUint64) : nullptr;
+#else
+ return value.AsUint ? MUtils::GetGCHandleTarget(value.AsUint) : nullptr;
+#endif
case VariantType::Typename:
{
const auto klass = Scripting::FindClassNative((StringAnsiView)value);
@@ -869,6 +887,9 @@ void MUtils::GetClassFullname(MonoObject* obj, MString& fullname)
void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
{
+#if USE_NETCORE
+ fullname = CoreCLR::GetClassFullname(monoClass);
+#else
static MString plusStr("+");
static MString dotStr(".");
@@ -906,6 +927,7 @@ void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
}
fullname += ']';
}
+#endif
}
void MUtils::GetClassFullname(MonoReflectionType* type, MString& fullname)
@@ -1138,7 +1160,8 @@ BytesContainer MUtils::LinkArray(MonoArray* arrayObj)
void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& failed)
{
// Convert Variant into matching managed type and return pointer to data for the method invocation
- switch (type.GetNative()->type)
+ MonoTypeEnum monoType = (MonoTypeEnum)mono_type_get_type(type.GetNative());
+ switch (monoType)
{
case MONO_TYPE_BOOLEAN:
if (value.Type.Type != VariantType::Bool)
@@ -1183,7 +1206,11 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
return MUtils::ToString((StringView)value);
case MONO_TYPE_VALUETYPE:
{
+#if !USE_NETCORE
MonoClass* klass = type.GetNative()->data.klass;
+#else
+ MonoClass* klass = mono_type_get_class(type.GetNative());
+#endif
if (mono_class_is_enum(klass))
{
if (value.Type.Type != VariantType::Enum)
@@ -1305,15 +1332,52 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
MonoObject* MUtils::ToManaged(const Version& value)
{
+#if USE_NETCORE
+ auto scriptingClass = Scripting::GetStaticClass();
+ CHECK_RETURN(scriptingClass, nullptr);
+ auto versionToManaged = scriptingClass->GetMethod("VersionToManaged", 4);
+ CHECK_RETURN(versionToManaged, nullptr);
+
+ int major = value.Major();
+ int minor = value.Minor();
+ int build = value.Build();
+ int revision = value.Revision();
+
+ void* params[4];
+ params[0] = &major;
+ params[1] = &minor;
+ params[2] = &build;
+ params[3] = &revision;
+ auto obj = versionToManaged->Invoke(nullptr, params, nullptr);
+#else
auto obj = mono_object_new(mono_domain_get(), Scripting::FindClassNative("System.Version"));
Platform::MemoryCopy((byte*)obj + sizeof(MonoObject), &value, sizeof(Version));
+#endif
return obj;
}
Version MUtils::ToNative(MonoObject* value)
{
if (value)
+#if USE_NETCORE
+ {
+ auto ver = Version();
+
+ auto scriptingClass = Scripting::GetStaticClass();
+ CHECK_RETURN(scriptingClass, ver);
+ auto versionToNative = scriptingClass->GetMethod("VersionToNative", 2);
+ CHECK_RETURN(versionToNative, ver);
+
+ void* params[2];
+ params[0] = value;
+ params[1] = &ver;
+ versionToNative->Invoke(nullptr, params, nullptr);
+ return ver;
+ }
+
+#else
return *(Version*)((byte*)value + sizeof(MonoObject));
+#endif
return Version();
}
diff --git a/Source/Engine/Scripting/ManagedCLR/MUtils.h b/Source/Engine/Scripting/ManagedCLR/MUtils.h
index c257c4bb2..db2766dbe 100644
--- a/Source/Engine/Scripting/ManagedCLR/MUtils.h
+++ b/Source/Engine/Scripting/ManagedCLR/MUtils.h
@@ -14,6 +14,11 @@
#include
#include
+#if USE_NETCORE
+#include "Engine/Scripting/DotNet/CoreCLR.h"
+#include "Engine/Core/Collections/BitArray.h"
+#endif
+
struct Version;
namespace MUtils
@@ -53,6 +58,19 @@ struct MConverter
void ToNativeArray(Array& result, MonoArray* data, int32 length);
};
+#if USE_NETCORE
+// Pass-through converter for ScriptingObjects (passed as GCHandles)
+template<>
+struct MConverter
+{
+ void Unbox(void*& result, MonoObject* data)
+ {
+ CHECK(data);
+ result = data;
+ }
+};
+#endif
+
// Converter for POD types (that can use raw memory copy).
template
struct MConverter, TNot::Type>>>::Value>::Type>
@@ -86,12 +104,22 @@ struct MConverter
{
MonoObject* Box(const String& data, MonoClass* klass)
{
+#if USE_NETCORE
+ MonoString* str = MUtils::ToString(data);
+ return mono_value_box(nullptr, klass, str);
+#else
return (MonoObject*)MUtils::ToString(data);
+#endif
}
void Unbox(String& result, MonoObject* data)
{
+#if USE_NETCORE
+ MonoString* str = (MonoString*)mono_object_unbox(data);
+ result = MUtils::ToString(str);
+#else
result = MUtils::ToString((MonoString*)data);
+#endif
}
void ToManagedArray(MonoArray* result, const Span& data)
@@ -564,6 +592,72 @@ namespace MUtils
return ToArray(Span(data.Get(), data.Count()), mono_get_string_class());
}
+#if USE_NETCORE
+ ///
+ /// Allocates new boolean array and copies data from the given unmanaged data container.
+ /// The managed runtime is responsible for releasing the returned array data.
+ ///
+ /// The input data.
+ /// The output array.
+ FORCE_INLINE bool* ToBoolArray(const Array& data)
+ {
+ bool* arr = (bool*)CoreCLR::Allocate(data.Count() * sizeof(bool));
+ memcpy(arr, data.Get(), data.Count() * sizeof(bool));
+ return arr;
+ }
+
+ ///
+ /// Allocates new boolean array and copies data from the given unmanaged data container.
+ /// The managed runtime is responsible for releasing the returned array data.
+ ///
+ /// The input data.
+ /// The output array.
+ FORCE_INLINE bool* ToBoolArray(const BitArray<>& data)
+ {
+ bool* arr = (bool*)CoreCLR::Allocate(data.Count() * sizeof(bool));
+ //memcpy(arr, data.Get(), data.Count() * sizeof(bool));
+ for (int i = 0; i < data.Count(); i++)
+ arr[i] = data[i];
+ return arr;
+ }
+#endif
+
+ FORCE_INLINE gchandle NewGCHandle(MonoObject* obj, bool pinned)
+ {
+#if USE_NETCORE
+ return CoreCLR::NewGCHandle(obj, pinned);
+#else
+ return mono_gchandle_new(obj, pinned);
+#endif
+ }
+
+ FORCE_INLINE gchandle NewGCHandleWeakref(MonoObject* obj, bool track_resurrection)
+ {
+#if USE_NETCORE
+ return CoreCLR::NewGCHandleWeakref(obj, track_resurrection);
+#else
+ return mono_gchandle_new_weak_ref(obj, track_resurrection);
+#endif
+ }
+
+ FORCE_INLINE MonoObject* GetGCHandleTarget(const gchandle& handle)
+ {
+#if USE_NETCORE
+ return (MonoObject*)CoreCLR::GetGCHandleTarget(handle);
+#else
+ return mono_gchandle_get_target(handle);
+#endif
+ }
+
+ FORCE_INLINE void FreeGCHandle(const gchandle& handle)
+ {
+#if USE_NETCORE
+ CoreCLR::FreeGCHandle(handle);
+#else
+ mono_gchandle_free(handle);
+#endif
+ }
+
extern void* VariantToManagedArgPtr(Variant& value, const MType& type, bool& failed);
extern MonoObject* ToManaged(const Version& value);
extern Version ToNative(MonoObject* value);
diff --git a/Source/Engine/Scripting/Object.cs b/Source/Engine/Scripting/Object.cs
index cc4b7e6e5..5847bb78c 100644
--- a/Source/Engine/Scripting/Object.cs
+++ b/Source/Engine/Scripting/Object.cs
@@ -2,6 +2,8 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
// ReSharper disable UnassignedReadonlyField
// ReSharper disable InconsistentNaming
@@ -13,13 +15,14 @@ namespace FlaxEngine
/// Base class for all objects Flax can reference. Every object has unique identifier.
///
[Serializable]
- public abstract class Object
+ [NativeMarshalling(typeof(ObjectMarshaller))]
+ public abstract partial class Object
{
///
/// The pointer to the unmanaged object (native C++ instance).
///
[NonSerialized]
- protected readonly IntPtr __unmanagedPtr;
+ internal readonly IntPtr __unmanagedPtr;
///
/// The object unique identifier.
@@ -248,35 +251,35 @@ namespace FlaxEngine
#region Internal Calls
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Object Internal_Create1(Type type);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Create1", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial Object Internal_Create1([MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Object Internal_Create2(string typeName);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Create2", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial Object Internal_Create2(string typeName);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_ManagedInstanceCreated(Object managedInstance);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_ManagedInstanceCreated(Object managedInstance);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_Destroy(IntPtr obj, float timeLeft);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_Destroy(IntPtr obj, float timeLeft);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern string Internal_GetTypeName(IntPtr obj);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial string Internal_GetTypeName(IntPtr obj);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Object Internal_FindObject(ref Guid id, Type type);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Object Internal_TryFindObject(ref Guid id, Type type);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial Object Internal_TryFindObject(ref Guid id, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void Internal_ChangeID(IntPtr obj, ref Guid id);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ChangeID", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial void Internal_ChangeID(IntPtr obj, ref Guid id);
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern IntPtr Internal_GetUnmanagedInterface(IntPtr obj, Type type);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_GetUnmanagedInterface", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ internal static partial IntPtr Internal_GetUnmanagedInterface(IntPtr obj, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
#endregion
}
diff --git a/Source/Engine/Scripting/Scripting.Internal.cpp b/Source/Engine/Scripting/Scripting.Internal.cpp
index 61f110282..3b5abfd9d 100644
--- a/Source/Engine/Scripting/Scripting.Internal.cpp
+++ b/Source/Engine/Scripting/Scripting.Internal.cpp
@@ -40,6 +40,7 @@ namespace ProfilerInternal
void BeginEvent(MonoString* nameObj)
{
+ SCRIPTING_EXPORT("FlaxEngine.Profiler::BeginEvent")
#if COMPILE_WITH_PROFILER
const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj));
ProfilerCPU::BeginEvent(*name);
@@ -78,6 +79,7 @@ namespace ProfilerInternal
void EndEvent()
{
+ SCRIPTING_EXPORT("FlaxEngine.Profiler::EndEvent")
#if COMPILE_WITH_PROFILER
#if TRACY_ENABLE
tracy::ScopedZone::End();
@@ -88,6 +90,7 @@ namespace ProfilerInternal
void BeginEventGPU(MonoString* nameObj)
{
+ SCRIPTING_EXPORT("FlaxEngine.Profiler::BeginEventGPU")
#if COMPILE_WITH_PROFILER
const auto index = ProfilerGPU::BeginEvent((const Char*)mono_string_chars(nameObj));
ManagedEventsGPU.Push(index);
@@ -96,6 +99,7 @@ namespace ProfilerInternal
void EndEventGPU()
{
+ SCRIPTING_EXPORT("FlaxEngine.Profiler::EndEventGPU")
#if COMPILE_WITH_PROFILER
const auto index = ManagedEventsGPU.Pop();
ProfilerGPU::EndEvent(index);
@@ -111,16 +115,19 @@ public:
#if USE_MONO
static bool HasGameModulesLoaded()
{
+ SCRIPTING_EXPORT("FlaxEngine.Scripting::HasGameModulesLoaded")
return Scripting::HasGameModulesLoaded();
}
static bool IsTypeFromGameScripts(MonoReflectionType* type)
{
+ SCRIPTING_EXPORT("FlaxEngine.Scripting::IsTypeFromGameScripts")
return Scripting::IsTypeFromGameScripts(Scripting::FindClass(MUtils::GetClass(type)));
}
static void FlushRemovedObjects()
{
+ SCRIPTING_EXPORT("FlaxEngine.Scripting::FlushRemovedObjects")
ASSERT(IsInMainThread());
ObjectsRemovalService::Flush();
}
diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp
index ac7a6f8e8..c44f71823 100644
--- a/Source/Engine/Scripting/Scripting.cpp
+++ b/Source/Engine/Scripting/Scripting.cpp
@@ -903,6 +903,7 @@ ScriptingObject* Scripting::FindObject(const MObject* managedInstance)
void Scripting::OnManagedInstanceDeleted(ScriptingObject* obj)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ManagedInstanceDeleted")
PROFILE_CPU();
ASSERT(obj);
diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs
index 6eff982d9..4a9276a83 100644
--- a/Source/Engine/Scripting/Scripting.cs
+++ b/Source/Engine/Scripting/Scripting.cs
@@ -5,6 +5,8 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
using System.Threading;
using System.Threading.Tasks;
using FlaxEngine.GUI;
@@ -65,7 +67,7 @@ namespace FlaxEngine
///
/// C# scripting service.
///
- public static class Scripting
+ public static partial class Scripting
{
private static readonly List UpdateActions = new List();
private static readonly MainThreadTaskScheduler MainThreadTaskScheduler = new MainThreadTaskScheduler();
@@ -207,6 +209,21 @@ namespace FlaxEngine
return result;
}
+ internal static IntPtr VersionToManaged(int major, int minor, int build, int revision)
+ {
+ Version version = new Version(major, minor, Math.Max(build, 0), Math.Max(revision, 0));
+ return GCHandle.ToIntPtr(GCHandle.Alloc(version));
+ }
+
+ internal static void VersionToNative(IntPtr versionHandle, IntPtr nativePtr)
+ {
+ Version version = (Version)GCHandle.FromIntPtr(versionHandle).Target;
+ Marshal.WriteInt32(nativePtr, 0, version.Major);
+ Marshal.WriteInt32(nativePtr, 4, version.Minor);
+ Marshal.WriteInt32(nativePtr, 8, version.Build);
+ Marshal.WriteInt32(nativePtr, 12, version.Revision);
+ }
+
private static void CreateGuiStyle()
{
var style = new Style
@@ -283,21 +300,23 @@ namespace FlaxEngine
/// Returns true if game scripts assembly has been loaded.
///
/// True if game scripts assembly is loaded, otherwise false.
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern bool HasGameModulesLoaded();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::HasGameModulesLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool HasGameModulesLoaded();
///
/// Returns true if given type is from one of the game scripts assemblies.
///
/// True if the type is from game assembly, otherwise false.
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern bool IsTypeFromGameScripts(Type type);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::IsTypeFromGameScripts", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ [return: MarshalAs(UnmanagedType.U1)]
+ public static partial bool IsTypeFromGameScripts([MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
///
/// Flushes the removed objects (disposed objects using Object.Destroy).
///
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void FlushRemovedObjects();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::FlushRemovedObjects", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ public static partial void FlushRemovedObjects();
}
///
@@ -306,32 +325,32 @@ namespace FlaxEngine
///
/// Profiler is available in the editor and Debug/Development builds. Release builds don't have profiling tools.
///
- public static class Profiler
+ public static partial class Profiler
{
///
/// Begins profiling a piece of code with a custom label.
///
/// The name of the event.
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void BeginEvent(string name);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::BeginEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ public static partial void BeginEvent(string name);
///
/// Ends profiling an event.
///
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void EndEvent();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::EndEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ public static partial void EndEvent();
///
/// Begins GPU profiling a piece of code with a custom label.
///
/// The name of the event.
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void BeginEventGPU(string name);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::BeginEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ public static partial void BeginEventGPU(string name);
///
/// Ends GPU profiling an event.
///
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void EndEventGPU();
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::EndEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
+ public static partial void EndEventGPU();
}
}
diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp
index ee3dfc438..d63140693 100644
--- a/Source/Engine/Scripting/ScriptingObject.cpp
+++ b/Source/Engine/Scripting/ScriptingObject.cpp
@@ -68,9 +68,13 @@ ScriptingObject* ScriptingObject::NewObject(const ScriptingTypeHandle& typeHandl
MObject* ScriptingObject::GetManagedInstance() const
{
+#if USE_NETCORE
+ const gchandle handle = Platform::AtomicRead((int64*)&_gcHandle);
+#elif USE_MONO
+ const gchandle handle = Platform::AtomicRead((int32*)&_gcHandle);
+#endif
#if USE_MONO
- const int32 handle = Platform::AtomicRead((int32*)&_gcHandle);
- return handle ? mono_gchandle_get_target(handle) : nullptr;
+ return handle ? MUtils::GetGCHandleTarget(handle) : nullptr;
#else
return nullptr;
#endif
@@ -211,7 +215,7 @@ void ScriptingObject::OnManagedInstanceDeleted()
if (_gcHandle)
{
#if USE_MONO
- mono_gchandle_free(_gcHandle);
+ MUtils::FreeGCHandle(_gcHandle);
#endif
_gcHandle = 0;
}
@@ -236,10 +240,15 @@ bool ScriptingObject::CreateManaged()
if (!managedInstance)
return true;
- // Prevent form object GC destruction
- auto handle = mono_gchandle_new(managedInstance, false);
+ // Prevent from object GC destruction
+ auto handle = MUtils::NewGCHandle(managedInstance, false);
+#if USE_NETCORE
+ auto oldHandle = Platform::InterlockedCompareExchange((int64*)&_gcHandle, *(int64*)&handle, 0);
+ if (*(uint64*)&oldHandle != 0)
+#else
auto oldHandle = Platform::InterlockedCompareExchange((int32*)&_gcHandle, *(int32*)&handle, 0);
if (*(uint32*)&oldHandle != 0)
+#endif
{
// Other thread already created the object before
if (const auto monoClass = GetClass())
@@ -252,7 +261,7 @@ bool ScriptingObject::CreateManaged()
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
}
}
- mono_gchandle_free(handle);
+ MUtils::FreeGCHandle(handle);
return true;
}
#endif
@@ -337,7 +346,7 @@ void ScriptingObject::DestroyManaged()
// Clear the handle
if (_gcHandle)
{
- mono_gchandle_free(_gcHandle);
+ MUtils::FreeGCHandle(_gcHandle);
_gcHandle = 0;
}
#else
@@ -448,9 +457,14 @@ bool ManagedScriptingObject::CreateManaged()
return true;
// Cache the GC handle to the object (used to track the target object because it can be moved in a memory)
- auto handle = mono_gchandle_new_weakref(managedInstance, false);
+ auto handle = MUtils::NewGCHandleWeakref(managedInstance, false);
+#if USE_NETCORE
+ auto oldHandle = Platform::InterlockedCompareExchange((int64*)&_gcHandle, *(int64*)&handle, 0);
+ if (*(uint64*)&oldHandle != 0)
+#else
auto oldHandle = Platform::InterlockedCompareExchange((int32*)&_gcHandle, *(int32*)&handle, 0);
if (*(uint32*)&oldHandle != 0)
+#endif
{
// Other thread already created the object before
if (const auto monoClass = GetClass())
@@ -463,7 +477,7 @@ bool ManagedScriptingObject::CreateManaged()
monoUnmanagedPtrField->SetValue(managedInstance, ¶m);
}
}
- mono_gchandle_free(handle);
+ MUtils::FreeGCHandle(handle);
return true;
}
#endif
@@ -487,6 +501,7 @@ public:
static MonoObject* Create1(MonoReflectionType* type)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Create1")
// Peek class for that type (handle generic class cases)
if (!type)
DebugLog::ThrowArgumentNull("type");
@@ -550,6 +565,7 @@ public:
static MonoObject* Create2(MonoString* typeNameObj)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Create2")
// Get typename
if (typeNameObj == nullptr)
DebugLog::ThrowArgumentNull("typeName");
@@ -587,6 +603,7 @@ public:
static void ManagedInstanceCreated(MonoObject* managedInstance)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ManagedInstanceCreated")
MonoClass* typeClass = mono_object_get_class(managedInstance);
// Get the assembly with that class
@@ -625,12 +642,12 @@ public:
if (auto* managedScriptingObject = dynamic_cast(obj))
{
// Managed
- managedScriptingObject->_gcHandle = mono_gchandle_new_weakref(managedInstance, false);
+ managedScriptingObject->_gcHandle = MUtils::NewGCHandleWeakref(managedInstance, false);
}
else
{
// Persistent
- obj->_gcHandle = mono_gchandle_new(managedInstance, false);
+ obj->_gcHandle = MUtils::NewGCHandle(managedInstance, false);
}
MClass* monoClass = obj->GetClass();
@@ -657,6 +674,7 @@ public:
static void Destroy(ScriptingObject* obj, float timeLeft)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Destroy")
// Use scaled game time for removing actors/scripts by the user (maybe expose it to the api?)
const bool useGameTime = timeLeft > ZeroTolerance;
@@ -666,12 +684,14 @@ public:
static MonoString* GetTypeName(ScriptingObject* obj)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_GetTypeName")
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
return MUtils::ToString(obj->GetType().Fullname);
}
static MonoObject* FindObject(Guid* id, MonoReflectionType* type)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_FindObject")
if (!id->IsValid())
return nullptr;
auto klass = MUtils::GetClass(type);
@@ -701,6 +721,7 @@ public:
static MonoObject* TryFindObject(Guid* id, MonoReflectionType* type)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_TryFindObject")
ScriptingObject* obj = Scripting::TryFindObject(*id);
if (obj && !obj->Is(MUtils::GetClass(type)))
obj = nullptr;
@@ -709,12 +730,14 @@ public:
static void ChangeID(ScriptingObject* obj, Guid* id)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ChangeID")
INTERNAL_CALL_CHECK(obj);
obj->ChangeID(*id);
}
static void* GetUnmanagedInterface(ScriptingObject* obj, MonoReflectionType* type)
{
+ SCRIPTING_EXPORT("FlaxEngine.Object::Internal_GetUnmanagedInterface")
if (obj && type)
{
auto typeClass = MUtils::GetClass(type);
diff --git a/Source/Engine/Scripting/ScriptingObject.h b/Source/Engine/Scripting/ScriptingObject.h
index 7db036e17..4b51d83a9 100644
--- a/Source/Engine/Scripting/ScriptingObject.h
+++ b/Source/Engine/Scripting/ScriptingObject.h
@@ -21,7 +21,7 @@ public:
protected:
- uint32 _gcHandle;
+ gchandle _gcHandle;
ScriptingTypeHandle _type;
Guid _id;
diff --git a/Source/Engine/Scripting/Types.h b/Source/Engine/Scripting/Types.h
index e88ba9a35..db3a37e4b 100644
--- a/Source/Engine/Scripting/Types.h
+++ b/Source/Engine/Scripting/Types.h
@@ -31,7 +31,7 @@ typedef void MObject;
#else
#define USE_MONO 1
-#define USE_NETCORE 0
+#define USE_NETCORE 1
// Enables using single (root) app domain for the user scripts
#define USE_SCRIPTING_SINGLE_DOMAIN 1
@@ -49,6 +49,17 @@ typedef void MObject;
#define USE_MONO_AOT_MODE MONO_AOT_MODE_NONE
#endif
+#if USE_NETCORE
+struct _MonoDomain {};
+struct _MonoThread {};
+#endif
+
+#if USE_NETCORE
+typedef unsigned long long gchandle;
+#else
+typedef uint32 gchandle;
+#endif
+
// Mono types declarations
typedef struct _MonoClass MonoClass;
typedef struct _MonoDomain MonoDomain;
diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs
index b5213e2c3..60eaad2d1 100644
--- a/Source/Engine/Utilities/Utils.cs
+++ b/Source/Engine/Utilities/Utils.cs
@@ -6,13 +6,15 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.Marshalling;
namespace FlaxEngine
{
///
/// Class with helper functions.
///
- public static class Utils
+ public static partial class Utils
{
///
/// Copies data from one memory location to another using an unmanaged memory pointers.
@@ -35,8 +37,8 @@ namespace FlaxEngine
/// The source location.
/// The destination location.
/// The length (amount of bytes to copy).
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void MemoryCopy(IntPtr destination, IntPtr source, ulong length);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::MemoryCopy")]
+ public static partial void MemoryCopy(IntPtr destination, IntPtr source, ulong length);
///
/// Clears the memory region with zeros.
@@ -44,8 +46,8 @@ namespace FlaxEngine
/// Uses low-level platform impl.
/// Destination memory address
/// Size of the memory to clear in bytes
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern void MemoryClear(IntPtr dst, ulong size);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::MemoryClear")]
+ public static partial void MemoryClear(IntPtr dst, ulong size);
///
/// Compares two blocks of the memory.
@@ -54,8 +56,8 @@ namespace FlaxEngine
/// The first buffer address.
/// The second buffer address.
/// Size of the memory to compare in bytes.
- [MethodImpl(MethodImplOptions.InternalCall)]
- public static extern int MemoryCompare(IntPtr buf1, IntPtr buf2, ulong size);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::MemoryCompare")]
+ public static partial int MemoryCompare(IntPtr buf1, IntPtr buf2, ulong size);
///
/// Rounds the floating point value up to 1 decimal place.
@@ -94,7 +96,11 @@ namespace FlaxEngine
/// The empty array object.
public static T[] GetEmptyArray()
{
+#if USE_NETCORE
+ return Array.Empty();
+#else
return Enumerable.Empty() as T[];
+#endif
}
///
@@ -209,10 +215,50 @@ namespace FlaxEngine
return result;
}
+ ///
+ /// Gets the location of the assembly.
+ ///
+ /// The assembly.
+ /// Path in the filesystem
+ public static string GetAssemblyLocation(Assembly assembly)
+ {
+#if USE_NETCORE
+ if (!string.IsNullOrEmpty(assembly.Location))
+ return assembly.Location;
+
+ if (NativeInterop.AssemblyLocations.TryGetValue(assembly.FullName, out string assemblyLocation))
+ return assemblyLocation;
+
+ return null;
+#else
+ return assembly.Location;
+#endif
+ }
+
+#if USE_MONO
internal static T[] ExtractArrayFromList(List list)
{
return list != null ? (T[])Internal_ExtractArrayFromList(list) : null;
}
+#else
+ private class ExtractArrayFromListContext
+ {
+ public static FieldInfo? itemsField;
+ }
+ internal static T[] ExtractArrayFromList(List list)
+ {
+ if (list == null)
+ return null;
+
+ if (ExtractArrayFromListContext.itemsField == null)
+ {
+ Type listType = typeof(List);
+ ExtractArrayFromListContext.itemsField = listType.GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
+ }
+
+ return (T[])ExtractArrayFromListContext.itemsField.GetValue(list); // boxing is slower;
+ }
+#endif
internal static Float2[] ConvertCollection(Vector2[] v)
{
@@ -295,8 +341,9 @@ namespace FlaxEngine
return result;
}
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern Array Internal_ExtractArrayFromList(object list);
+ [LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::Internal_ExtractArrayFromList")]
+ [return: MarshalUsing(typeof(FlaxEngine.SystemArrayMarshaller))]
+ internal static partial Array Internal_ExtractArrayFromList([MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))] object list);
///
/// Reads the color from the binary stream.
diff --git a/Source/Editor/Utilities/VariantUtils.cs b/Source/Engine/Utilities/VariantUtils.cs
similarity index 99%
rename from Source/Editor/Utilities/VariantUtils.cs
rename to Source/Engine/Utilities/VariantUtils.cs
index 5f2c524ac..30dce5990 100644
--- a/Source/Editor/Utilities/VariantUtils.cs
+++ b/Source/Engine/Utilities/VariantUtils.cs
@@ -4,11 +4,14 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
+#if FLAX_EDITOR
using FlaxEditor.Scripting;
+using FlaxEditor.Utilities;
+#endif
using FlaxEngine;
using Newtonsoft.Json;
-namespace FlaxEditor.Utilities
+namespace FlaxEngine.Utilities
{
///
/// Editor utilities and helper functions for Variant type.
@@ -76,6 +79,7 @@ namespace FlaxEditor.Utilities
#endif
}
+#if FLAX_EDITOR
internal static VariantType ToVariantType(this Type type)
{
VariantType variantType;
@@ -107,7 +111,9 @@ namespace FlaxEditor.Utilities
variantType = VariantType.Pointer;
else if (type == typeof(string))
variantType = VariantType.String;
- else if (type == typeof(Type) || type == typeof(ScriptType))
+ else if (type == typeof(Type))
+ variantType = VariantType.Typename;
+ else if (type == typeof(ScriptType))
variantType = VariantType.Typename;
else if (typeof(Asset).IsAssignableFrom(type))
variantType = VariantType.Asset;
@@ -1286,5 +1292,6 @@ namespace FlaxEditor.Utilities
stream.WriteEndObject();
}
+#endif
}
}
diff --git a/Source/Engine/Visject/VisjectScript.cpp b/Source/Engine/Visject/VisjectScript.cpp
new file mode 100644
index 000000000..1ae6cafda
--- /dev/null
+++ b/Source/Engine/Visject/VisjectScript.cpp
@@ -0,0 +1,6 @@
+#include "VisjectScript.h"
+
+VisjectScript::VisjectScript(const SpawnParams& params)
+ : Script(params)
+{
+}
diff --git a/Source/Engine/Visject/VisjectScript.h b/Source/Engine/Visject/VisjectScript.h
new file mode 100644
index 000000000..1e5389a77
--- /dev/null
+++ b/Source/Engine/Visject/VisjectScript.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
+
+#pragma once
+
+#include "Engine/Scripting/Script.h"
+
+///
+///
+///
+API_CLASS(Namespace = "FlaxEngine.Visject") class FLAXENGINE_API VisjectScript : public Script
+{
+ API_AUTO_SERIALIZATION();
+ DECLARE_SCRIPTING_TYPE(VisjectScript);
+};
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
index e4f8eb2aa..d42f662a9 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using BuildData = Flax.Build.Builder.BuildData;
namespace Flax.Build.Bindings
@@ -171,7 +172,11 @@ namespace Flax.Build.Bindings
// Skip for collections
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer" || typeInfo.Type == "Dictionary" || typeInfo.Type == "HashSet") && typeInfo.GenericArgs != null)
+#if !USE_NETCORE
return false;
+#else
+ return true;
+#endif
// Skip for special types
if (typeInfo.GenericArgs == null)
@@ -219,6 +224,40 @@ namespace Flax.Build.Bindings
return false;
}
+#if USE_NETCORE
+ ///
+ /// Check if structure contains unblittable types that would require custom marshaller for the structure.
+ ///
+ public static bool UseCustomMarshalling(BuildData buildData, StructureInfo structureInfo, ApiTypeInfo caller)
+ {
+ if (structureInfo.Fields.Any(x => !x.IsStatic &&
+ (x.Type.IsObjectRef || x.Type.Type == "Dictionary" || x.Type.Type == "Version")
+ && x.Type.Type != "uint8" && x.Type.Type != "byte"))
+ {
+ return true;
+ }
+
+ foreach (var field in structureInfo.Fields)
+ {
+ if (field.Type.Type == structureInfo.FullNameNative)
+ continue;
+ if (field.IsStatic)
+ continue;
+
+ if (field.Type.Type == "String")
+ return true;
+
+ var fieldApiType = FindApiTypeInfo(buildData, field.Type, caller);
+ if (fieldApiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, caller))
+ return true;
+ else if (fieldApiType is ClassInfo)
+ return true;
+ }
+
+ return false;
+ }
+#endif
+
///
/// Finds the API type information.
///
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
index 59a4df55e..2a4b6d1a8 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs
@@ -278,15 +278,15 @@ namespace Flax.Build.Bindings
}
// Object reference property
- if ((typeInfo.Type == "ScriptingObjectReference" ||
- typeInfo.Type == "AssetReference" ||
- typeInfo.Type == "WeakAssetReference" ||
- typeInfo.Type == "SoftAssetReference" ||
- typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
+ if (typeInfo.IsObjectRef)
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller);
// Array or Span or DataContainer
+#if USE_NETCORE
+ if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer" || typeInfo.Type == "MonoArray") && typeInfo.GenericArgs != null)
+#else
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer") && typeInfo.GenericArgs != null)
+#endif
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller) + "[]";
// Dictionary
@@ -361,11 +361,7 @@ namespace Flax.Build.Bindings
}
// Object reference property
- if ((typeInfo.Type == "ScriptingObjectReference" ||
- typeInfo.Type == "AssetReference" ||
- typeInfo.Type == "WeakAssetReference" ||
- typeInfo.Type == "SoftAssetReference" ||
- typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
+ if (typeInfo.IsObjectRef)
return "IntPtr";
// Function
@@ -419,11 +415,7 @@ namespace Flax.Build.Bindings
}
// Object reference property
- if ((typeInfo.Type == "ScriptingObjectReference" ||
- typeInfo.Type == "AssetReference" ||
- typeInfo.Type == "WeakAssetReference" ||
- typeInfo.Type == "SoftAssetReference" ||
- typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
+ if (typeInfo.IsObjectRef)
return "FlaxEngine.Object.GetUnmanagedPtr({0})";
// Default
@@ -443,8 +435,38 @@ namespace Flax.Build.Bindings
returnValueType = GenerateCSharpNativeToManaged(buildData, functionInfo.ReturnType, caller);
}
+#if USE_NETCORE
+ string returnMarshalType = "";
+ if (returnValueType == "bool")
+ returnMarshalType = "MarshalAs(UnmanagedType.U1)";
+ else if (returnValueType == "System.Type")
+ returnMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemTypeMarshaller))";
+ else if (returnValueType == "CultureInfo")
+ returnMarshalType = "MarshalUsing(typeof(FlaxEngine.CultureInfoMarshaller))";
+ else if (functionInfo.ReturnType.Type == "Variant")
+ returnMarshalType = "MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))";
+ else if (FindApiTypeInfo(buildData, functionInfo.ReturnType, caller)?.IsInterface ?? false)
+ {
+ // Interfaces are not supported by NativeMarshallingAttribute, marshal the parameter
+ returnMarshalType = $"MarshalUsing(typeof({returnValueType}Marshaller))";
+ }
+ else if (returnValueType == "byte[]")
+ returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 0)";
+ else if (returnValueType == "bool[]")
+ {
+ // Boolean arrays does not support custom marshalling for some unkown reason...
+ returnMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters?.Count ?? 0)})";
+ }
+#endif
+#if !USE_NETCORE
contents.AppendLine().Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]");
- contents.AppendLine().Append(indent).Append("internal static extern ");
+ contents.AppendLine().Append(indent).Append("internal static partial ");
+#else
+ contents.AppendLine().Append(indent).Append($"[LibraryImport(\"FlaxEngine\", EntryPoint = \"{caller.FullNameManaged}::Internal_{functionInfo.UniqueName}\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]");
+ if (!string.IsNullOrEmpty(returnMarshalType))
+ contents.AppendLine().Append(indent).Append($"[return: {returnMarshalType}]");
+ contents.AppendLine().Append(indent).Append("internal static partial ");
+#endif
contents.Append(returnValueType).Append(" Internal_").Append(functionInfo.UniqueName).Append('(');
var separator = false;
@@ -461,6 +483,30 @@ namespace Flax.Build.Bindings
separator = true;
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller);
+#if USE_NETCORE
+ string parameterMarshalType = "";
+ if (nativeType == "System.Type")
+ parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemTypeMarshaller))";
+ else if (parameterInfo.Type.Type == "CultureInfo")
+ parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.CultureInfoMarshaller))";
+ else if (parameterInfo.Type.Type == "Variant") // object
+ parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))";
+ else if (parameterInfo.Type.Type == "MonoArray")
+ parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemArrayMarshaller))";
+ else if (parameterInfo.Type.Type == "Array" && parameterInfo.Type.GenericArgs.Count > 0 && parameterInfo.Type.GenericArgs[0].Type == "bool")
+ parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == parameterInfo.Name + "Count"))})";
+ else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array")
+ parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
+ else if (parameterInfo.Type.Type == "Dictionary")
+ parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.DictionaryMarshaller<,>), ConstantElementCount = 0)";
+ else if (nativeType == "bool")
+ parameterMarshalType = "MarshalAs(UnmanagedType.U1)";
+ else if (nativeType == "char")
+ parameterMarshalType = "MarshalAs(UnmanagedType.I2)";
+
+ if (!string.IsNullOrEmpty(parameterMarshalType))
+ contents.Append($"[{parameterMarshalType}] ");
+#endif
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
@@ -484,6 +530,21 @@ namespace Flax.Build.Bindings
separator = true;
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller);
+#if USE_NETCORE
+ string parameterMarshalType = "";
+ if (parameterInfo.IsOut && parameterInfo.DefaultValue == "var resultAsRef")
+ {
+ if (functionInfo.Glue.UseResultReferenceCount)
+ parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
+ else if (parameterInfo.Type.Type == "Dictionary")
+ parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.DictionaryMarshaller<,>), ConstantElementCount = 0)";
+ }
+ if (nativeType == "System.Type")
+ parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemTypeMarshaller))";
+
+ if (!string.IsNullOrEmpty(parameterMarshalType))
+ contents.Append($"[{parameterMarshalType}] ");
+#endif
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
@@ -500,6 +561,17 @@ namespace Flax.Build.Bindings
private static void GenerateCSharpWrapperFunctionCall(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, FunctionInfo functionInfo, bool isSetter = false)
{
+#if USE_NETCORE
+ for (var i = 0; i < functionInfo.Parameters.Count; i++)
+ {
+ var parameterInfo = functionInfo.Parameters[i];
+ if (parameterInfo.Type.IsArray || parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "BytesContainer" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BitArray")
+ {
+ if (!parameterInfo.IsOut)
+ contents.Append($"var {parameterInfo.Name}Count = {(isSetter ? "value" : parameterInfo.Name)}?.Length ?? 0; ");
+ }
+ }
+#endif
if (functionInfo.Glue.UseReferenceForResult)
{
}
@@ -613,6 +685,9 @@ namespace Flax.Build.Bindings
{
// Write attribute for C++ calling code
contents.Append(indent).AppendLine("[Unmanaged]");
+
+ // Skip boilerplate code when using debugger
+ //contents.Append(indent).AppendLine("[System.Diagnostics.DebuggerStepThrough]");
}
if (isDeprecated || apiTypeInfo.IsDeprecated)
{
@@ -698,6 +773,15 @@ namespace Flax.Build.Bindings
// Class begin
GenerateCSharpAttributes(buildData, contents, indent, classInfo, useUnmanaged);
+#if USE_NETCORE
+ string marshallerName = "";
+ if (!classInfo.IsStatic)
+ {
+ marshallerName = classInfo.Name + "Marshaller";
+ contents.Append(indent).AppendLine($"[NativeMarshalling(typeof({marshallerName}))]");
+ //contents.Append(indent).AppendLine($"[NativeMarshalling(typeof(FlaxEngine.GCHandleMarshaller2<{classInfo.Name}>.GCHandleMarshaller3<{classInfo.Name}>))]");
+ }
+#endif
contents.Append(indent);
GenerateCSharpAccessLevel(contents, classInfo.Access);
if (classInfo.IsStatic)
@@ -862,11 +946,19 @@ namespace Flax.Build.Bindings
contents.Append(indent).Append('}').AppendLine();
contents.AppendLine();
+#if !USE_NETCORE
contents.Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]").AppendLine();
contents.Append(indent).Append($"internal static extern void Internal_{eventInfo.Name}_Bind(");
if (!eventInfo.IsStatic)
contents.Append("IntPtr obj, ");
contents.Append("bool bind);");
+#else
+ contents.Append(indent).Append($"[LibraryImport(\"FlaxEngine\", EntryPoint = \"{classInfo.FullNameManaged}::Internal_{eventInfo.Name}_Bind\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]").AppendLine();
+ contents.Append(indent).Append($"internal static partial void Internal_{eventInfo.Name}_Bind(");
+ if (!eventInfo.IsStatic)
+ contents.Append("IntPtr obj, ");
+ contents.Append("[MarshalAs(UnmanagedType.U1)] bool bind);");
+#endif
contents.AppendLine();
}
@@ -1100,6 +1192,64 @@ namespace Flax.Build.Bindings
indent = indent.Substring(0, indent.Length - 4);
contents.AppendLine(indent + "}");
+#if USE_NETCORE
+ if (!string.IsNullOrEmpty(marshallerName))
+ {
+ string marshallerDefinition = $$"""
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerName}}.ManagedToNative))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerName}}.ManagedToNative))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerName}}.ManagedToNative))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerName}}.NativeToManaged))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerName}}.NativeToManaged))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerName}}.NativeToManaged))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerName}}.Bidirectional))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerName}}.Bidirectional))]
+ [CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerName}}))]
+ internal static class {{marshallerName}}
+ {
+ public static class NativeToManaged
+ {
+ public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged);
+ public static void Free(IntPtr unmanaged) => GCHandleMarshaller.NativeToManaged.Free(unmanaged);
+ }
+ public static class ManagedToNative
+ {
+ public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
+ public static void Free(IntPtr unmanaged) => GCHandleMarshaller.ManagedToNative.Free(unmanaged);
+ }
+ public struct Bidirectional
+ {
+ GCHandleMarshaller.Bidirectional marsh;
+ public void FromManaged({{classInfo.Name}} managed) => marsh.FromManaged(managed);
+ public IntPtr ToUnmanaged() => marsh.ToUnmanaged();
+ public void FromUnmanaged(IntPtr unmanaged) => marsh.FromUnmanaged(unmanaged);
+ public {{classInfo.Name}} ToManaged() => ({{classInfo.Name}})marsh.ToManaged();
+ public void Free() => marsh.Free();
+ }
+ internal static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.ConvertToManaged(unmanaged);
+ internal static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
+ internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
+
+ internal static {{classInfo.Name}} ToManaged(IntPtr managed) => ({{classInfo.Name}})GCHandleMarshaller.ToManaged(managed);
+ internal static IntPtr ToNative({{classInfo.Name}} managed) => GCHandleMarshaller.ToNative(managed);
+ }
+ """;
+ contents.AppendLine(marshallerDefinition);
+
+ /*contents.AppendLine();
+
+ contents.Append(indent).AppendLine("/// ");
+ contents.Append(indent).AppendLine("/// ");
+ contents.Append(indent).AppendLine("/// ");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({classInfo.Name}), MarshalMode.Default, typeof({marshallerName}))]");
+ contents.Append(indent).AppendLine($"public static class {marshallerName}");
+ contents.Append(indent).AppendLine("{");
+ contents.Append(indent + " ").AppendLine($"internal static {classInfo.Name} ConvertToManaged(IntPtr unmanaged) => ({classInfo.Name})GCHandleMarshaller.ConvertToManaged(unmanaged);");
+ contents.Append(indent + " ").AppendLine($"internal static IntPtr ConvertToUnmanaged({classInfo.Name} managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);");
+ contents.Append(indent + " ").AppendLine("internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);");
+ contents.Append(indent).AppendLine("}");*/
+ }
+#endif
// Namespace end
if (!string.IsNullOrEmpty(classInfo.Namespace))
{
@@ -1119,13 +1269,318 @@ namespace Flax.Build.Bindings
contents.AppendLine("{");
indent += " ";
}
+#if USE_NETCORE
+ // Generate blittable structure
+ string structNativeMarshaling = "";
+ if (UseCustomMarshalling(buildData, structureInfo, structureInfo))
+ {
+ string marshallerName = structureInfo.Name + "Marshaller";
+ structNativeMarshaling = $"[NativeMarshalling(typeof({marshallerName}))]";
+ contents.Append(indent).AppendLine($"/// ");
+ contents.Append(indent).AppendLine($"/// Marshaller for unblittable type .");
+ contents.Append(indent).AppendLine($"/// ");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedIn, typeof({marshallerName}.ManagedToNative))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedOut, typeof({marshallerName}.ManagedToNative))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementIn, typeof({marshallerName}.ManagedToNative))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedOut, typeof({marshallerName}.NativeToManaged))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedIn, typeof({marshallerName}.NativeToManaged))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementOut, typeof({marshallerName}.NativeToManaged))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedRef, typeof({marshallerName}.Bidirectional))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedRef, typeof({marshallerName}.Bidirectional))]");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementRef, typeof({marshallerName}))]");
+ contents.Append(indent).AppendLine($"internal static unsafe class {marshallerName}");
+ contents.Append(indent).AppendLine("{");
+
+ indent += " ";
+
+ StringBuilder toManagedContent = new StringBuilder();
+ StringBuilder toNativeContent = new StringBuilder();
+ StringBuilder freeContents = new StringBuilder();
+ StringBuilder freeContents2 = new StringBuilder();
+
+ {
+ // Native struct begin
+ GenerateCSharpAttributes(buildData, contents, indent, structureInfo, true);
+ contents.Append(indent).AppendLine("[StructLayout(LayoutKind.Sequential)]");
+ contents.Append(indent);
+ contents.Append("internal struct ").Append(structureInfo.Name).Append("Internal");
+ if (structureInfo.BaseType != null && structureInfo.IsPod)
+ contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = structureInfo.BaseType.Name }, structureInfo));
+ contents.AppendLine();
+ contents.Append(indent + "{");
+ indent += " ";
+
+ toNativeContent.Append($"return new {structureInfo.Name}Internal() {{ ");
+ toManagedContent.Append($"return new {structureInfo.Name}() {{ ");
+
+ bool useSeparator = false;
+ foreach (var fieldInfo in structureInfo.Fields)
+ {
+ if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
+ continue;
+
+ contents.AppendLine();
+
+ string type, originalType;
+ if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
+ {
+ // Fixed-size array that needs to be inlined into structure instead of passing it as managed array
+ fieldInfo.Type.IsArray = false;
+ originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
+ fieldInfo.Type.IsArray = true;
+ }
+ else
+ originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
+
+ contents.Append(indent);
+ if (fieldInfo.Access == AccessLevel.Public)
+ contents.Append("public ");
+ else if (fieldInfo.Access == AccessLevel.Protected)
+ contents.Append("protected ");
+ else if (fieldInfo.Access == AccessLevel.Private)
+ contents.Append("private ");
+ if (fieldInfo.IsConstexpr)
+ contents.Append("const ");
+ else if (fieldInfo.IsStatic)
+ contents.Append("static ");
+
+ var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo);
+ bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
+ string internalTypeMarshaler = "";
+
+ if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
+ {
+ contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine();
+ for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
+ {
+ contents.AppendLine();
+ GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
+ contents.Append(indent);
+ if (fieldInfo.Access == AccessLevel.Public)
+ contents.Append("public ");
+ else if (fieldInfo.Access == AccessLevel.Protected)
+ contents.Append("protected ");
+ else if (fieldInfo.Access == AccessLevel.Private)
+ contents.Append("private ");
+ if (fieldInfo.IsStatic)
+ contents.Append("static ");
+ contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
+ }
+ }
+ else
+ {
+ if (fieldInfo.Type.IsObjectRef || fieldInfo.Type.Type == "Dictionary")
+ type = "IntPtr";
+ else if (fieldInfo.Type.IsPtr && !originalType.EndsWith("*"))
+ type = "IntPtr";
+ else if (fieldInfo.Type.Type == "Array")
+ {
+ type = "IntPtr";
+ apiType = FindApiTypeInfo(buildData, fieldInfo.Type.GenericArgs[0], structureInfo);
+ internalType = apiType is StructureInfo elementStructureInfo && UseCustomMarshalling(buildData, elementStructureInfo, structureInfo);
+ }
+ else if (fieldInfo.Type.Type == "Version")
+ type = "IntPtr";
+ else if (type == "string")
+ type = "IntPtr";
+ else if (type == "bool")
+ type = "byte";
+ else if (type == "object")
+ type = "VariantNative";
+ else if (internalType)
+ {
+ internalTypeMarshaler = type + "Marshaller";
+ type = $"{internalTypeMarshaler}.{type}Internal";
+ }
+ //else if (type == "Guid")
+ // type = "GuidNative";
+
+ contents.Append(type).Append(' ').Append(fieldInfo.Name);
+ contents.Append(';').AppendLine();
+ }
+
+ // Generate struct constructor/getter and deconstructor/setter function
+ if (fieldInfo.NoArray && fieldInfo.Type.IsArray)
+ continue;
+
+ if (type == "VariantNative")
+ continue; // FIXME
+
+ if (useSeparator)
+ {
+ toManagedContent.Append(", ");
+ toNativeContent.Append(", ");
+ freeContents2.Append("");
+ freeContents.Append("");
+ }
+ useSeparator = true;
+
+ toManagedContent.Append(fieldInfo.Name);
+ toManagedContent.Append(" = ");
+
+ toNativeContent.Append(fieldInfo.Name);
+ toNativeContent.Append(" = ");
+
+ if (fieldInfo.Type.IsObjectRef)
+ {
+ toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({fieldInfo.Type.GenericArgs[0].Type})GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
+ toNativeContent.Append($"managed.{fieldInfo.Name} != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak)) : IntPtr.Zero");
+ freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+
+ // ScriptingObject handle is passed from native side, do not release it
+ //freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ }
+ else if (fieldInfo.Type.Type == "ScriptingObject")
+ {
+ toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? (FlaxEngine.Object)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
+ toNativeContent.Append($"managed.{fieldInfo.Name} != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak)) : IntPtr.Zero");
+ freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ }
+ else if (fieldInfo.Type.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
+ {
+ toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({originalType})GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
+ toNativeContent.Append($"managed.{fieldInfo.Name} != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak)) : IntPtr.Zero");
+ freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ }
+ else if (fieldInfo.Type.Type == "Dictionary")
+ {
+ string dictionaryArgs = String.Join(", ",
+ fieldInfo.Type.GenericArgs.Select(x => CSharpNativeToManagedBasicTypes.ContainsKey(x.Type) ? CSharpNativeToManagedBasicTypes[x.Type] : x.Type).ToArray());
+ toManagedContent.Append(
+ $"managed.{fieldInfo.Name} != IntPtr.Zero ? (System.Collections.Generic.{fieldInfo.Type.Type}<{dictionaryArgs}>)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
+ toNativeContent.Append(
+ $"GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak))");
+ freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ }
+ else if (fieldInfo.Type.Type == "Array")
+ {
+ if (internalType)
+ {
+ string originalElementType = originalType.Substring(0, originalType.Length - 2);
+ string originalElementTypeMarshaller = originalElementType + "Marshaller";
+ toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.NativeArrayToManagedArray<{originalElementType}, {originalElementTypeMarshaller}.{originalElementType}Internal>(((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).array as {originalElementTypeMarshaller}.{originalElementType}Internal[], {originalElementTypeMarshaller}.ToManaged) : null");
+ toNativeContent.Append($"GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(managed.{fieldInfo.Name}), GCHandleType.Weak))");
+ }
+ else if (fieldInfo.Type.GenericArgs[0].IsObjectRef)
+ {
+ string originalElementType = originalType.Substring(0, originalType.Length - 2);
+ toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
+ toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(NativeInterop.ManagedArrayToGCHandleArray(managed.{fieldInfo.Name})), GCHandleType.Weak)) : IntPtr.Zero");
+ }
+ else
+ {
+ toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({originalType})(((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).array) : null");
+ toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(managed.{fieldInfo.Name}), GCHandleType.Weak)) : IntPtr.Zero");
+ }
+ freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Unsafe.Unbox(handle.Target).Release(); handle.Free(); }}");
+ freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Unsafe.Unbox(handle.Target).Release(); handle.Free(); }}");
+ }
+ else if (fieldInfo.Type.Type == "Version")
+ {
+ toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({originalType})GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
+ toNativeContent.Append($"GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak))");
+ freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
+ }
+ else if (originalType == "string")
+ {
+ toManagedContent.Append($"ManagedString.ToManaged(managed.{fieldInfo.Name})");
+ toNativeContent.Append($"ManagedString.ToNative(managed.{fieldInfo.Name})");
+ freeContents.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
+ freeContents2.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
+ }
+ else if (originalType == "bool")
+ {
+ toManagedContent.Append($"managed.{fieldInfo.Name} != 0");
+ toNativeContent.Append($"managed.{fieldInfo.Name} ? (byte)1 : (byte)0");
+ }
+ else if (internalType)
+ {
+ toManagedContent.Append($"{internalTypeMarshaler}.ToManaged(managed.{fieldInfo.Name})");
+ toNativeContent.Append($"{internalTypeMarshaler}.ToNative(managed.{fieldInfo.Name})");
+ freeContents.AppendLine($"{internalTypeMarshaler}.Free(unmanaged.{fieldInfo.Name});");
+ freeContents2.AppendLine($"{internalTypeMarshaler}.Free(unmanaged.{fieldInfo.Name});");
+ }
+ /*else if (originalType == "Guid")
+ {
+ toManagedContent.Append("(Guid)managed.").Append(fieldInfo.Name);
+ toNativeContent.Append("(GuidNative)managed.").Append(fieldInfo.Name);
+ }*/
+ else
+ {
+ toManagedContent.Append("managed.").Append(fieldInfo.Name);
+ toNativeContent.Append("managed.").Append(fieldInfo.Name);
+ }
+ }
+
+ // Native struct end
+ indent = indent.Substring(0, indent.Length - 4);
+ contents.AppendLine(indent + "}").AppendLine();
+
+ toManagedContent.AppendLine(" };");
+ toNativeContent.AppendLine(" };");
+ }
+
+ var indent2 = indent + " ";
+ var indent3 = indent2 + " ";
+
+ // NativeToManaged stateless shape
+ // NOTE: GCHandles of FlaxEngine.Object must not be released in this case
+ contents.Append(indent).AppendLine($"public static class NativeToManaged").Append(indent).AppendLine("{");
+ contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);");
+ contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged)");
+ contents.Append(indent2).AppendLine("{").Append(indent3).AppendLine(freeContents2.Replace("\n", "\n" + indent3).ToString().TrimEnd()).Append(indent2).AppendLine("}");
+ contents.Append(indent).AppendLine("}");
+
+ // ManagedToNative stateless shape
+ contents.Append(indent).AppendLine($"public static class ManagedToNative").Append(indent).AppendLine("{");
+ contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);");
+ contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged) => {marshallerName}.Free(unmanaged);");
+ contents.Append(indent).AppendLine("}");
+
+ // Bidirectional stateful shape
+ // NOTE: GCHandles of FlaxEngine.Object must not be released unless they were allocated by this marshaller
+ contents.Append(indent).AppendLine($"public struct Bidirectional").Append(indent).AppendLine("{");
+ contents.Append(indent2).AppendLine($"{structureInfo.Name} managed;");
+ contents.Append(indent2).AppendLine($"{structureInfo.Name}Internal? unmanaged;");
+ contents.Append(indent2).AppendLine($"public void FromManaged({structureInfo.Name} managed) {{ this.managed = managed; }}");
+ contents.Append(indent2).AppendLine($"public {structureInfo.Name}Internal ToUnmanaged() {{ unmanaged = {marshallerName}.ToNative(managed); return unmanaged.Value; }}");
+ //contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) {{ {marshallerName}.Free(this.unmanaged.Value); this.unmanaged = unmanaged; }}");
+ contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) {{ this.unmanaged = unmanaged; }}");
+ contents.Append(indent2).AppendLine($"public {structureInfo.Name} ToManaged() {{ managed = {marshallerName}.ToManaged(unmanaged.Value); unmanaged = null; return managed; }}");
+ contents.Append(indent2).AppendLine($"public void Free() {{ if (unmanaged.HasValue) {{ NativeToManaged.Free(unmanaged.Value); unmanaged = null; }} }}");
+ contents.Append(indent).AppendLine("}");
+
+ // Bidirectional stateless shape
+ contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => ToManaged(unmanaged);");
+ contents.Append(indent).AppendLine($"internal static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => ToNative(managed);");
+ contents.Append(indent).AppendLine($"internal static void Free({structureInfo.Name}Internal unmanaged)");
+ contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(freeContents.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
+
+ // Managed/native converters
+ contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ToManaged({structureInfo.Name}Internal managed)");
+ contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toManagedContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
+ contents.Append(indent).AppendLine($"internal static {structureInfo.Name}Internal ToNative({structureInfo.Name} managed)");
+ contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toNativeContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
+
+ indent = indent.Substring(0, indent.Length - 4);
+ contents.Append(indent).AppendLine("}");
+ }
+#endif
// Struct docs
GenerateCSharpComment(contents, indent, structureInfo.Comment);
// Struct begin
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, true);
contents.Append(indent).AppendLine("[StructLayout(LayoutKind.Sequential)]");
+#if USE_NETCORE
+ if (!string.IsNullOrEmpty(structNativeMarshaling))
+ contents.Append(indent).AppendLine(structNativeMarshaling);
+#endif
contents.Append(indent);
GenerateCSharpAccessLevel(contents, structureInfo.Access);
contents.Append("unsafe partial struct ").Append(structureInfo.Name);
@@ -1142,6 +1597,12 @@ namespace Flax.Build.Bindings
contents.AppendLine();
GenerateCSharpComment(contents, indent, fieldInfo.Comment);
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic, fieldInfo.DefaultValue, fieldInfo.Type);
+#if USE_NETCORE
+ if (fieldInfo.Type.Type == "String")
+ contents.Append(indent).AppendLine("[MarshalAs(UnmanagedType.LPWStr)]");
+ else if (fieldInfo.Type.Type == "bool")
+ contents.Append(indent).AppendLine("[MarshalAs(UnmanagedType.U1)]");
+#endif
contents.Append(indent);
GenerateCSharpAccessLevel(contents, fieldInfo.Access);
if (fieldInfo.IsConstexpr)
@@ -1153,21 +1614,32 @@ namespace Flax.Build.Bindings
if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
{
- // Fixed-size array that needs to be inlined into structure instead of passing it as managed array
fieldInfo.Type.IsArray = false;
type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
fieldInfo.Type.IsArray = true;
- contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine();
- for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
+#if USE_NETCORE
+ // Use fixed statement with primitive types of buffers
+ if (type == "char")
{
- contents.AppendLine();
- GenerateCSharpComment(contents, indent, fieldInfo.Comment);
- GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
- contents.Append(indent);
- GenerateCSharpAccessLevel(contents, fieldInfo.Access);
- if (fieldInfo.IsStatic)
- contents.Append("static ");
- contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
+ // char's are not blittable, store as short instead
+ contents.Append($"fixed short {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}]; // {type}*").AppendLine();
+ }
+ else
+#endif
+ {
+ // Padding in structs for fixed-size array
+ contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine();
+ for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
+ {
+ contents.AppendLine();
+ GenerateCSharpComment(contents, indent, fieldInfo.Comment);
+ GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
+ contents.Append(indent);
+ GenerateCSharpAccessLevel(contents, fieldInfo.Access);
+ if (fieldInfo.IsStatic)
+ contents.Append("static ");
+ contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
+ }
}
continue;
}
@@ -1393,6 +1865,23 @@ namespace Flax.Build.Bindings
contents.AppendLine(indent + "}");
if (!string.IsNullOrEmpty(interfaceInfo.Namespace))
contents.AppendLine("}");
+#if USE_NETCORE
+ {
+ string marshallerName = interfaceInfo.Name + "Marshaller";
+ contents.AppendLine();
+
+ contents.Append(indent).AppendLine("/// ");
+ contents.Append(indent).AppendLine("/// ");
+ contents.Append(indent).AppendLine("/// ");
+ contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({interfaceInfo.Name}), MarshalMode.Default, typeof({marshallerName}))]");
+ contents.Append(indent).AppendLine($"internal static class {marshallerName}");
+ contents.Append(indent).AppendLine("{");
+ contents.Append(indent + " ").AppendLine($"internal static {interfaceInfo.Name} ConvertToManaged(IntPtr unmanaged) => ({interfaceInfo.Name})GCHandleMarshaller.ConvertToManaged(unmanaged);");
+ contents.Append(indent + " ").AppendLine($"internal static IntPtr ConvertToUnmanaged({interfaceInfo.Name} managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);");
+ contents.Append(indent + " ").AppendLine("internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);");
+ contents.Append(indent).AppendLine("}");
+ }
+#endif
}
private static bool GenerateCSharpType(BuildData buildData, StringBuilder contents, string indent, object type)
@@ -1456,6 +1945,9 @@ namespace Flax.Build.Bindings
CSharpUsedNamespaces.Add("System.Globalization");
CSharpUsedNamespaces.Add("System.Runtime.CompilerServices");
CSharpUsedNamespaces.Add("System.Runtime.InteropServices");
+#if USE_NETCORE
+ CSharpUsedNamespaces.Add("System.Runtime.InteropServices.Marshalling");
+#endif
CSharpUsedNamespaces.Add("FlaxEngine");
// Process all API types from the file
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
index 1b54c9d81..fd1a84898 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs
@@ -440,6 +440,13 @@ namespace Flax.Build.Bindings
// Array or Span or DataContainer
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer") && typeInfo.GenericArgs != null)
{
+#if USE_NETCORE
+ if (typeInfo.GenericArgs[0].Type == "bool")
+ {
+ type = "bool*";
+ return "MUtils::ToBoolArray({0})";
+ }
+#endif
type = "MonoArray*";
return "MUtils::ToArray({0}, " + GenerateCppGetNativeClass(buildData, typeInfo.GenericArgs[0], caller, functionInfo) + ")";
}
@@ -472,8 +479,13 @@ namespace Flax.Build.Bindings
if (typeInfo.Type == "BitArray" && typeInfo.GenericArgs != null)
{
CppIncludeFiles.Add("Engine/Scripting/InternalCalls/ManagedBitArray.h");
+#if USE_NETCORE
+ type = "bool*";
+ return "MUtils::ToBoolArray({0})";
+#else
type = "MonoObject*";
return "ManagedBitArray::ToManaged({0})";
+#endif
}
// Function
@@ -817,6 +829,7 @@ namespace Flax.Build.Bindings
return true;
if (typeInfo.IsPtr || typeInfo.IsRef || typeInfo.IsArray || typeInfo.IsBitField || (typeInfo.GenericArgs != null && typeInfo.GenericArgs.Count != 0))
return false;
+#if !USE_NETCORE
if (CSharpNativeToManagedBasicTypes.ContainsKey(typeInfo.Type) || CSharpNativeToManagedBasicTypes.ContainsValue(typeInfo.Type))
return true;
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
@@ -825,6 +838,7 @@ namespace Flax.Build.Bindings
if (apiType.IsEnum)
return true;
}
+#endif
return false;
}
@@ -880,7 +894,38 @@ namespace Flax.Build.Bindings
},
IsOut = true,
});
+#if USE_NETCORE
+ if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer")
+ {
+ functionInfo.Glue.UseResultReferenceCount = true;
+ functionInfo.Glue.CustomParameters.Add(new FunctionInfo.ParameterInfo
+ {
+ Name = "resultAsRefCount",
+ DefaultValue = "var resultAsRefCount",
+ Type = new TypeInfo
+ {
+ Type = "int"
+ },
+ IsOut = true,
+ });
+ }
+#endif
}
+#if USE_NETCORE
+ else if (functionInfo.ReturnType.Type == "BitArray" || functionInfo.ReturnType.Type == "BytesContainer")//returnValueType == "byte[]")
+ {
+ functionInfo.Glue.CustomParameters.Add(new FunctionInfo.ParameterInfo
+ {
+ Name = "returnCount",
+ DefaultValue = "var returnCount",
+ Type = new TypeInfo
+ {
+ Type = "int"
+ },
+ IsOut = true,
+ });
+ }
+#endif
CppInternalCalls.Add(new KeyValuePair(functionInfo.UniqueName, functionInfo.UniqueName));
contents.AppendFormat(" static {0} {1}(", returnValueType, functionInfo.UniqueName);
@@ -945,6 +990,23 @@ namespace Flax.Build.Bindings
CppParamsThatNeedConversionWrappers[i] = GenerateCppWrapperNativeToManaged(buildData, parameterInfo.Type, caller, out CppParamsThatNeedConversionTypes[i], functionInfo);
}
}
+#if USE_NETCORE
+ if (parameterInfo.Type.IsArray || parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "BytesContainer" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BitArray")
+ {
+ // We need additional output parameters for array sizes
+ functionInfo.Glue.CustomParameters.Add(new FunctionInfo.ParameterInfo
+ {
+ Name = parameterInfo.Name + "Count",
+ DefaultValue = parameterInfo.IsOut ? "int _" : parameterInfo.Name + "Count",
+ Type = new TypeInfo
+ {
+ Type = "int"
+ },
+ IsOut = parameterInfo.IsOut,
+ IsRef = isRefOut || parameterInfo.Type.IsRef,
+ });
+ }
+#endif
}
for (var i = 0; i < functionInfo.Glue.CustomParameters.Count; i++)
@@ -965,6 +1027,9 @@ namespace Flax.Build.Bindings
contents.Append(')');
contents.AppendLine();
contents.AppendLine(" {");
+#if USE_NETCORE
+ contents.AppendLine(String.Format(" SCRIPTING_EXPORT(\"{0}\")", caller.FullNameManaged + "::Internal_" + functionInfo.UniqueName));
+#endif
if (!functionInfo.IsStatic)
contents.AppendLine(" if (obj == nullptr) DebugLog::ThrowNullReference();");
@@ -981,6 +1046,17 @@ namespace Flax.Build.Bindings
callBegin += "auto __result = ";
}
+#if USE_NETCORE
+ string callBegin2 = "";
+ if (functionInfo.Glue.UseResultReferenceCount)
+ {
+ callBegin2 = " ";
+ if (functionInfo.ReturnType.Type == "Span")
+ callBegin2 += "*resultAsRefCount = {0}.Length();";
+ else
+ callBegin2 += "*resultAsRefCount = {0}.Count();";
+ }
+#endif
string call;
if (functionInfo.IsStatic)
{
@@ -1017,6 +1093,16 @@ namespace Flax.Build.Bindings
}
else
{
+#if USE_NETCORE
+ // FIXME
+ if (parameterInfo.Type.Type == "Span" ||
+ parameterInfo.Type.Type == "Array" ||
+ parameterInfo.Type.Type == "DataContainer" ||
+ parameterInfo.Type.Type == "Dictionary")
+ {
+ name = '*' + name;
+ }
+#endif
// Convert value
param += string.Format(CppParamsWrappersCache[i], name);
}
@@ -1059,8 +1145,20 @@ namespace Flax.Build.Bindings
}
}
- contents.Append(callBegin);
- call = string.Format(callFormat, call, callParams);
+#if USE_NETCORE
+ if (!string.IsNullOrEmpty(callBegin2))
+ {
+ contents.Append(" ").Append("auto& callTemp = ").Append(string.Format(callFormat, call, callParams)).Append(";").AppendLine();
+ call = "callTemp";
+ contents.Append(callBegin);
+ callBegin2 = string.Format(callBegin2, call);
+ }
+ else
+#endif
+ {
+ contents.Append(callBegin);
+ call = string.Format(callFormat, call, callParams);
+ }
if (!string.IsNullOrEmpty(returnValueConvert))
{
contents.AppendFormat(returnValueConvert, call);
@@ -1072,6 +1170,13 @@ namespace Flax.Build.Bindings
contents.Append(';');
contents.AppendLine();
+#if USE_NETCORE
+ if (!string.IsNullOrEmpty(callBegin2))
+ {
+ contents.Append(callBegin2);
+ contents.AppendLine();
+ }
+#endif
// Convert special parameters back to managed world
if (!useInlinedReturn)
@@ -1094,6 +1199,12 @@ namespace Flax.Build.Bindings
if (apiType.IsClass)
{
contents.AppendFormat(" mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine();
+#if USE_NETCORE
+ if (parameterInfo.Type.Type == "Array")
+ {
+ contents.AppendFormat(" *{0}Count = {1}.Count();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine();
+ }
+#endif
continue;
}
if (apiType.IsStruct && !apiType.IsPod)
@@ -1109,9 +1220,10 @@ namespace Flax.Build.Bindings
if (parameterInfo.Type.Type == "BytesContainer" && parameterInfo.Type.GenericArgs == null)
{
contents.AppendFormat(" mono_gc_wbarrier_generic_store({0}, (MonoObject*){1});", parameterInfo.Name, value).AppendLine();
+ contents.AppendFormat(" *{0}Count = {1}.Length();", parameterInfo.Name, parameterInfo.Name + "Temp").AppendLine();
continue;
}
-
+
throw new Exception($"Unsupported type of parameter '{parameterInfo}' in method '{functionInfo}' to be passed using 'out'");
}
}
@@ -1646,6 +1758,9 @@ namespace Flax.Build.Bindings
contents.AppendFormat("{0}* obj, ", classTypeNameNative);
contents.Append("bool bind)").AppendLine();
contents.Append(" {").AppendLine();
+#if USE_NETCORE
+ contents.AppendLine(String.Format(" SCRIPTING_EXPORT(\"{0}\")", classTypeNameManagedInternalCall + "::Internal_" + eventInfo.Name + "_Bind"));
+#endif
contents.Append(" Function CustomParameters;
}
diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs
index 5f3e774c9..d52f34538 100644
--- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs
+++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs
@@ -33,6 +33,15 @@ namespace Flax.Build.Bindings
///
public bool IsConstRef => IsRef && IsConst;
+ ///
+ /// Gets a value indicating whether this type is a reference to another object.
+ ///
+ public bool IsObjectRef => (Type == "ScriptingObjectReference" ||
+ Type == "AssetReference" ||
+ Type == "WeakAssetReference" ||
+ Type == "SoftAssetReference" ||
+ Type == "SoftObjectReference") && GenericArgs != null;
+
public TypeInfo()
{
}
diff --git a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs
index 236ac1113..f034817f1 100644
--- a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs
+++ b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs
@@ -9,6 +9,7 @@ using Flax.Build.Graph;
using Flax.Build.Bindings;
using Mono.Cecil;
using Mono.Cecil.Cil;
+using Task = Flax.Build.Graph.Task;
namespace Flax.Build.Plugins
{
diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj
index 183b5f0a2..c7f73bc77 100644
--- a/Source/Tools/Flax.Build/Flax.Build.csproj
+++ b/Source/Tools/Flax.Build/Flax.Build.csproj
@@ -1,190 +1,39 @@
-
-
-
+
- Debug
- AnyCPU
- {C99AAF92-D4AD-4847-9EE0-B11E68E93E1E}
Exe
- Flax.Build
- Flax.Build
- v4.5.2
- 7.3
- 512
- true
-
+ net7.0
+ enable
+ disable
+ Debug;Release
+ AnyCPU
+ ..\..\..\Binaries\Tools
+ ..\..\..\Binaries\Tools
+ false
+ win-x64
+ true
+ USE_NETCORE
-
- AnyCPU
- true
- full
- false
- false
- ..\..\..\Binaries\Tools\
- ..\..\..\Binaries\Tools\Flax.Build.xml
- ..\..\..\Cache\Intermediate\Flax.Build\Debug
- DEBUG;TRACE
- prompt
- 4
- true
+
+
true
- 1591
-
- AnyCPU
- pdbonly
- true
- false
- ..\..\..\Binaries\Tools\
- ..\..\..\Binaries\Tools\Flax.Build.xml
- ..\..\..\Cache\Intermediate\Flax.Build\Release
- TRACE
- prompt
- 4
- true
+
+
true
- 1591
+
-
- ..\..\..\Source\Platforms\DotNet\Ionic.Zip.Reduced.dll
-
-
- ..\..\..\Source\Platforms\DotNet\Mono.Cecil.dll
-
-
- ..\..\..\Source\Platforms\DotNet\Microsoft.VisualStudio.Setup.Configuration.Interop.dll
- True
-
-
- False
- ..\..\..\Source\Platforms\DotNet\Newtonsoft.Json.dll
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
+
+
diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs
index a9a97bac0..b61019af5 100644
--- a/Source/Tools/Flax.Build/Utilities/Utilities.cs
+++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs
@@ -41,7 +41,11 @@ namespace Flax.Build
/// The empty array object.
public static T[] GetEmptyArray()
{
+#if USE_NETCORE
+ return Array.Empty();
+#else
return Enumerable.Empty() as T[];
+#endif
}
///