From 376ab71bb0cfbde06330b7f3a5612a0a55743f0b Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Fri, 10 Feb 2017 11:13:45 -0800 Subject: [PATCH 1/5] [C#][iOS] Fix callbacks on AOT Based on the idea of #386 --- csharp/Facebook.Yoga/Native.cs | 26 ++++++--- csharp/Facebook.Yoga/YogaLogger.cs | 34 +++++++----- csharp/Facebook.Yoga/YogaNode.cs | 85 +++++++++++++++++++++++++++--- 3 files changed, 120 insertions(+), 25 deletions(-) diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index 2c0d25ac..5b81b013 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -58,6 +58,14 @@ namespace Facebook.Yoga [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern int YGNodeGetInstanceCount(); +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr YGNodeGetContext(IntPtr node); + + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void YGNodeSetContext(YGNodeHandle node, IntPtr managed); +#endif + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGSetExperimentalFeatureEnabled( YogaExperimentalFeature feature, @@ -68,16 +76,20 @@ namespace Facebook.Yoga YogaExperimentalFeature feature); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeInsertChild(YGNodeHandle node, YGNodeHandle child, uint index); + public static extern void YGNodeInsertChild( + YGNodeHandle node, + YGNodeHandle child, + uint index); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeRemoveChild(YGNodeHandle node, YGNodeHandle child); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeCalculateLayout(YGNodeHandle node, - float availableWidth, - float availableHeight, - YogaDirection parentDirection); + public static extern void YGNodeCalculateLayout( + YGNodeHandle node, + float availableWidth, + float availableHeight, + YogaDirection parentDirection); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGNodeMarkDirty(YGNodeHandle node); @@ -105,7 +117,9 @@ namespace Facebook.Yoga [MarshalAs(UnmanagedType.FunctionPtr)] YogaBaselineFunc baselineFunc); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeSetHasNewLayout(YGNodeHandle node, [MarshalAs(UnmanagedType.I1)] bool hasNewLayout); + public static extern void YGNodeSetHasNewLayout( + YGNodeHandle node, + [MarshalAs(UnmanagedType.I1)] bool hasNewLayout); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] diff --git a/csharp/Facebook.Yoga/YogaLogger.cs b/csharp/Facebook.Yoga/YogaLogger.cs index e5d79b79..bd3af941 100644 --- a/csharp/Facebook.Yoga/YogaLogger.cs +++ b/csharp/Facebook.Yoga/YogaLogger.cs @@ -10,6 +10,10 @@ using System; using System.Runtime.InteropServices; +#if __IOS__ +using ObjCRuntime; +#endif + namespace Facebook.Yoga { internal static class YogaLogger @@ -18,26 +22,30 @@ namespace Facebook.Yoga public delegate void Func(YogaLogLevel level, string message); private static bool _initialized; - private static Func _managedLogger = null; public static Func Logger = null; +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(Func))] +#endif + public static void LoggerInternal(YogaLogLevel level, string message) + { + if (Logger != null) + { + Logger(level, message); + } + + if (level == YogaLogLevel.Error) + { + throw new InvalidOperationException(message); + } + } + public static void Initialize() { if (!_initialized) { - _managedLogger = (level, message) => { - if (Logger != null) - { - Logger(level, message); - } - - if (level == YogaLogLevel.Error) - { - throw new InvalidOperationException(message); - } - }; - Native.YGInteropSetLogger(_managedLogger); + Native.YGInteropSetLogger(LoggerInternal); _initialized = true; } } diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 59b6ae08..2ae2b54f 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -12,6 +12,11 @@ using System.Collections; using System.Collections.Generic; using System.Text; +#if __IOS__ +using System.Runtime.InteropServices; +using ObjCRuntime; +#endif + namespace Facebook.Yoga { public partial class YogaNode : IEnumerable @@ -20,10 +25,11 @@ namespace Facebook.Yoga private WeakReference _parent; private List _children; private MeasureFunction _measureFunction; - private YogaMeasureFunc _ygMeasureFunc; private BaselineFunction _baselineFunction; - private YogaBaselineFunc _ygBaselineFunc; private object _data; +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + private GCHandle _managed; +#endif public YogaNode() { @@ -542,18 +548,58 @@ namespace Facebook.Yoga return _children != null ? _children.IndexOf(node) : -1; } +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + private void SetContext() + { + if (!_managed.IsAllocated) + { + _managed = GCHandle.Alloc(this, GCHandleType.Weak); + Native.YGNodeSetContext(_ygNode, GCHandle.ToIntPtr(_managed)); + } + } + + private static YogaNode GetManaged(IntPtr ygNodePtr) + { + var node = GCHandle.FromIntPtr(Native.YGNodeGetContext(ygNodePtr)).Target as YogaNode; + if (node == null) + { + throw new InvalidOperationException("YogaNode is already deallocated"); + } + + return node; + } +#endif + public void SetMeasureFunction(MeasureFunction measureFunction) { _measureFunction = measureFunction; - _ygMeasureFunc = measureFunction != null ? MeasureInternal : (YogaMeasureFunc)null; - Native.YGNodeSetMeasureFunc(_ygNode, _ygMeasureFunc); + YogaMeasureFunc func = null; + if (measureFunction != null) + { +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + SetContext(); + func = MeasureInternalIOS; +#else + func = MeasureInternal; +#endif + } + Native.YGNodeSetMeasureFunc(_ygNode, func); } public void SetBaselineFunction(BaselineFunction baselineFunction) { _baselineFunction = baselineFunction; - _ygBaselineFunc = baselineFunction != null ? BaselineInternal : (YogaBaselineFunc)null; - Native.YGNodeSetBaselineFunc(_ygNode, _ygBaselineFunc); + YogaBaselineFunc func = null; + if (baselineFunction != null) + { +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + SetContext(); + func = BaselineInternalIOS; +#else + func = BaselineInternal; +#endif + } + Native.YGNodeSetBaselineFunc(_ygNode, func); } public void CalculateLayout() @@ -565,6 +611,20 @@ namespace Facebook.Yoga Native.YGNodeStyleGetDirection(_ygNode)); } +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(YogaMeasureFunc))] + private static YogaSize MeasureInternalIOS( + IntPtr ygNodePtr, + float width, + YogaMeasureMode widthMode, + float height, + YogaMeasureMode heightMode) + { + var node = GetManaged(ygNodePtr); + return node.MeasureInternal(IntPtr.Zero, width, widthMode, height, heightMode); + } +#endif + private YogaSize MeasureInternal( IntPtr node, float width, @@ -572,6 +632,7 @@ namespace Facebook.Yoga float height, YogaMeasureMode heightMode) { + if (_measureFunction == null) { throw new InvalidOperationException("Measure function is not defined."); @@ -580,6 +641,18 @@ namespace Facebook.Yoga return _measureFunction(this, width, widthMode, height, heightMode); } +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(YogaBaselineFunc))] + private static float BaselineInternalIOS( + IntPtr ygNodePtr, + float width, + float height) + { + var node = GetManaged(ygNodePtr); + return node.BaselineInternal(IntPtr.Zero, width, height); + } +#endif + private float BaselineInternal(IntPtr node, float width, float height) { if (_baselineFunction == null) -- 2.50.1.windows.1 From 1ceffea28ed06ec52d282d9d6cada983bf0a9f91 Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Fri, 10 Feb 2017 14:51:38 -0800 Subject: [PATCH 2/5] Free GCHandle --- csharp/Facebook.Yoga/Native.cs | 33 ++++++++++++++++++- csharp/Facebook.Yoga/YogaNode.cs | 37 ++++------------------ csharp/tests/Facebook.Yoga/YogaNodeTest.cs | 2 ++ 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index 5b81b013..e6ecaefe 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -22,6 +22,8 @@ namespace Facebook.Yoga internal class YGNodeHandle : SafeHandle { + private GCHandle _managed; + private YGNodeHandle() : base(IntPtr.Zero, true) { } @@ -36,10 +38,39 @@ namespace Facebook.Yoga protected override bool ReleaseHandle() { +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + if (_managed.IsAllocated) + { + _managed.Free(); + } +#endif Native.YGNodeFree(this.handle); GC.KeepAlive(this); return true; } + +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + public void SetContext(YogaNode node) + { + if (!_managed.IsAllocated) + { + _managed = GCHandle.Alloc(node, GCHandleType.Weak); + Native.YGNodeSetContext(this.handle, GCHandle.ToIntPtr(_managed)); + } + } + + public static YogaNode GetManaged(IntPtr ygNodePtr) + { + var node = + GCHandle.FromIntPtr(Native.YGNodeGetContext(ygNodePtr)).Target as YogaNode; + if (node == null) + { + throw new InvalidOperationException("YogaNode is already deallocated"); + } + + return node; + } +#endif } [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] @@ -63,7 +94,7 @@ namespace Facebook.Yoga public static extern IntPtr YGNodeGetContext(IntPtr node); [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeSetContext(YGNodeHandle node, IntPtr managed); + public static extern void YGNodeSetContext(IntPtr node, IntPtr managed); #endif [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 2ae2b54f..a83d91b9 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -12,8 +12,10 @@ using System.Collections; using System.Collections.Generic; using System.Text; -#if __IOS__ +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ using System.Runtime.InteropServices; +#endif +#if __IOS__ using ObjCRuntime; #endif @@ -27,9 +29,6 @@ namespace Facebook.Yoga private MeasureFunction _measureFunction; private BaselineFunction _baselineFunction; private object _data; -#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ - private GCHandle _managed; -#endif public YogaNode() { @@ -548,28 +547,6 @@ namespace Facebook.Yoga return _children != null ? _children.IndexOf(node) : -1; } -#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ - private void SetContext() - { - if (!_managed.IsAllocated) - { - _managed = GCHandle.Alloc(this, GCHandleType.Weak); - Native.YGNodeSetContext(_ygNode, GCHandle.ToIntPtr(_managed)); - } - } - - private static YogaNode GetManaged(IntPtr ygNodePtr) - { - var node = GCHandle.FromIntPtr(Native.YGNodeGetContext(ygNodePtr)).Target as YogaNode; - if (node == null) - { - throw new InvalidOperationException("YogaNode is already deallocated"); - } - - return node; - } -#endif - public void SetMeasureFunction(MeasureFunction measureFunction) { _measureFunction = measureFunction; @@ -577,7 +554,7 @@ namespace Facebook.Yoga if (measureFunction != null) { #if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ - SetContext(); + _ygNode.SetContext(this); func = MeasureInternalIOS; #else func = MeasureInternal; @@ -593,7 +570,7 @@ namespace Facebook.Yoga if (baselineFunction != null) { #if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ - SetContext(); + _ygNode.SetContext(this); func = BaselineInternalIOS; #else func = BaselineInternal; @@ -620,7 +597,7 @@ namespace Facebook.Yoga float height, YogaMeasureMode heightMode) { - var node = GetManaged(ygNodePtr); + var node = Native.YGNodeHandle.GetManaged(ygNodePtr); return node.MeasureInternal(IntPtr.Zero, width, widthMode, height, heightMode); } #endif @@ -648,7 +625,7 @@ namespace Facebook.Yoga float width, float height) { - var node = GetManaged(ygNodePtr); + var node = Native.YGNodeHandle.GetManaged(ygNodePtr); return node.BaselineInternal(IntPtr.Zero, width, height); } #endif diff --git a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs index 291fb4af..ac505845 100644 --- a/csharp/tests/Facebook.Yoga/YogaNodeTest.cs +++ b/csharp/tests/Facebook.Yoga/YogaNodeTest.cs @@ -365,6 +365,7 @@ namespace Facebook.Yoga child = null; } +#if !__IOS__ [Test] public void TestParentDestructor() { @@ -386,6 +387,7 @@ namespace Facebook.Yoga Assert.AreEqual(instanceCount + 1, YogaNode.GetInstanceCount()); parent.Insert(0, child); } +#endif [Test] public void TestClearWithChildDestructor() -- 2.50.1.windows.1 From 2861f14f1f7d4d7b7bbf33e66887f0a167554211 Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Fri, 10 Feb 2017 15:02:07 -0800 Subject: [PATCH 3/5] Remove empty line --- csharp/Facebook.Yoga/YogaNode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index a83d91b9..23a86996 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -609,7 +609,6 @@ namespace Facebook.Yoga float height, YogaMeasureMode heightMode) { - if (_measureFunction == null) { throw new InvalidOperationException("Measure function is not defined."); -- 2.50.1.windows.1 From 3304c1ae5ddb3fe613c60722e7f71d6c492da42f Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Fri, 10 Feb 2017 15:21:34 -0800 Subject: [PATCH 4/5] fix if and cosmetic changes --- csharp/Facebook.Yoga/Native.cs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index e6ecaefe..59dd84c7 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -22,7 +22,9 @@ namespace Facebook.Yoga internal class YGNodeHandle : SafeHandle { +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ private GCHandle _managed; +#endif private YGNodeHandle() : base(IntPtr.Zero, true) { @@ -89,14 +91,6 @@ namespace Facebook.Yoga [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern int YGNodeGetInstanceCount(); -#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ - [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr YGNodeGetContext(IntPtr node); - - [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeSetContext(IntPtr node, IntPtr managed); -#endif - [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] public static extern void YGSetExperimentalFeatureEnabled( YogaExperimentalFeature feature, @@ -373,5 +367,17 @@ namespace Facebook.Yoga public static extern YogaDirection YGNodeLayoutGetDirection(YGNodeHandle node); #endregion + + #region IOS + +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr YGNodeGetContext(IntPtr node); + + [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void YGNodeSetContext(IntPtr node, IntPtr managed); +#endif + + #endregion } } -- 2.50.1.windows.1 From e11a84423db726d03c367e99582eb14ddc150d94 Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Mon, 13 Feb 2017 09:00:27 -0800 Subject: [PATCH 5/5] Prevent GC delegates --- csharp/Facebook.Yoga/YogaLogger.cs | 3 ++- csharp/Facebook.Yoga/YogaNode.cs | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/csharp/Facebook.Yoga/YogaLogger.cs b/csharp/Facebook.Yoga/YogaLogger.cs index bd3af941..b05ad381 100644 --- a/csharp/Facebook.Yoga/YogaLogger.cs +++ b/csharp/Facebook.Yoga/YogaLogger.cs @@ -23,6 +23,7 @@ namespace Facebook.Yoga private static bool _initialized; + public static Func _loggerInternal = LoggerInternal; public static Func Logger = null; #if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ @@ -45,7 +46,7 @@ namespace Facebook.Yoga { if (!_initialized) { - Native.YGInteropSetLogger(LoggerInternal); + Native.YGInteropSetLogger(_loggerInternal); _initialized = true; } } diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 23a86996..b748a35e 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -29,6 +29,13 @@ namespace Facebook.Yoga private MeasureFunction _measureFunction; private BaselineFunction _baselineFunction; private object _data; +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + private static YogaMeasureFunc _measureInternalIOS = MeasureInternalIOS; + private static YogaBaselineFunc _baselineInternalIOS = BaselineInternalIOS; +#else + private YogaMeasureFunc _measureInternal; + private YogaBaselineFunc _baselineInternal; +#endif public YogaNode() { @@ -555,9 +562,10 @@ namespace Facebook.Yoga { #if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ _ygNode.SetContext(this); - func = MeasureInternalIOS; + func = _measureInternalIOS; #else - func = MeasureInternal; + _measureInternal = MeasureInternal; + func = _measureInternal; #endif } Native.YGNodeSetMeasureFunc(_ygNode, func); @@ -571,9 +579,10 @@ namespace Facebook.Yoga { #if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ _ygNode.SetContext(this); - func = BaselineInternalIOS; + func = _baselineInternalIOS; #else - func = BaselineInternal; + _baselineInternal = BaselineInternal; + func = _baselineInternal; #endif } Native.YGNodeSetBaselineFunc(_ygNode, func); -- 2.50.1.windows.1