From 4fe0b810e1ce5a910ed682da2af6dc36756ded05 Mon Sep 17 00:00:00 2001 From: Kazuki Sakamoto Date: Thu, 16 Feb 2017 11:07:51 -0800 Subject: [PATCH] Fix callbacks on AOT Summary: Based on the idea of #386 by rmarinho Closes https://github.com/facebook/yoga/pull/388 Reviewed By: emilsjolander Differential Revision: D4570778 Pulled By: splhack fbshipit-source-id: 362983deaf6f040c42b3db8205b711e7e5e60eaf --- csharp/Facebook.Yoga/Native.cs | 70 +++++++++++++++---- csharp/Facebook.Yoga/YogaLogger.cs | 29 ++++---- csharp/Facebook.Yoga/YogaNode.cs | 105 ++++++++++++++++++----------- 3 files changed, 139 insertions(+), 65 deletions(-) diff --git a/csharp/Facebook.Yoga/Native.cs b/csharp/Facebook.Yoga/Native.cs index 83efce85..3f3db639 100644 --- a/csharp/Facebook.Yoga/Native.cs +++ b/csharp/Facebook.Yoga/Native.cs @@ -22,6 +22,10 @@ namespace Facebook.Yoga internal class YGNodeHandle : SafeHandle { +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + private GCHandle _managed; +#endif + private YGNodeHandle() : base(IntPtr.Zero, true) { } @@ -36,10 +40,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)] @@ -68,16 +101,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,19 +142,14 @@ 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)] public static extern bool YGNodeGetHasNewLayout(YGNodeHandle node); - [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void YGNodeSetContext(YGNodeHandle node, IntPtr context); - - [DllImport(DllName, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr YGNodeGetContext(IntPtr node); - - #endregion #region YG_NODE_STYLE_PROPERTY @@ -347,5 +379,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 } } diff --git a/csharp/Facebook.Yoga/YogaLogger.cs b/csharp/Facebook.Yoga/YogaLogger.cs index 68d946e6..1d7f8e0a 100644 --- a/csharp/Facebook.Yoga/YogaLogger.cs +++ b/csharp/Facebook.Yoga/YogaLogger.cs @@ -8,29 +8,26 @@ */ using System; +using System.Runtime.InteropServices; + +#if __IOS__ +using ObjCRuntime; +#endif namespace Facebook.Yoga { internal static class YogaLogger { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void Func(YogaLogLevel level, string message); private static bool _initialized; + private static Func _managedLogger = LoggerInternal; public static Func Logger = null; - public static void Initialize() - { - if (!_initialized) - { - - Native.YGInteropSetLogger(LoggerInternal); - _initialized = true; - } - } - -#if __IOS__ - [ObjCRuntime.MonoPInvokeCallback(typeof(Func))] +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(Func))] #endif public static void LoggerInternal(YogaLogLevel level, string message) { @@ -45,5 +42,13 @@ namespace Facebook.Yoga } } + public static void Initialize() + { + if (!_initialized) + { + Native.YGInteropSetLogger(_managedLogger); + _initialized = true; + } + } } } diff --git a/csharp/Facebook.Yoga/YogaNode.cs b/csharp/Facebook.Yoga/YogaNode.cs index 0cfdb4c5..8bf1c8b8 100644 --- a/csharp/Facebook.Yoga/YogaNode.cs +++ b/csharp/Facebook.Yoga/YogaNode.cs @@ -10,9 +10,15 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Runtime.InteropServices; using System.Text; +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ +using System.Runtime.InteropServices; +#endif +#if __IOS__ +using ObjCRuntime; +#endif + namespace Facebook.Yoga { public partial class YogaNode : IEnumerable @@ -21,10 +27,15 @@ 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 static YogaMeasureFunc _managedMeasure = MeasureInternalIOS; + private static YogaBaselineFunc _managedBaseline = BaselineInternalIOS; +#else + private YogaMeasureFunc _managedMeasure; + private YogaBaselineFunc _managedBaseline; +#endif public YogaNode() { @@ -545,7 +556,7 @@ namespace Facebook.Yoga { while (_children.Count > 0) { - RemoveAt(_children.Count - 1); + RemoveAt(_children.Count-1); } } } @@ -558,23 +569,29 @@ namespace Facebook.Yoga public void SetMeasureFunction(MeasureFunction measureFunction) { _measureFunction = measureFunction; - _ygMeasureFunc = measureFunction != null ? MeasureInternal : (YogaMeasureFunc)null; - - var handle = GCHandle.Alloc(this); - Native.YGNodeSetContext(_ygNode, GCHandle.ToIntPtr(handle)); - - Native.YGNodeSetMeasureFunc(_ygNode, _ygMeasureFunc); + if (measureFunction != null) + { +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + _ygNode.SetContext(this); +#else + _managedMeasure = MeasureInternal; +#endif + } + Native.YGNodeSetMeasureFunc(_ygNode, _managedMeasure); } public void SetBaselineFunction(BaselineFunction baselineFunction) { _baselineFunction = baselineFunction; - _ygBaselineFunc = baselineFunction != null ? BaselineInternal : (YogaBaselineFunc)null; - - var handle = GCHandle.Alloc(this); - Native.YGNodeSetContext(_ygNode, GCHandle.ToIntPtr(handle)); - - Native.YGNodeSetBaselineFunc(_ygNode, _ygBaselineFunc); + if (baselineFunction != null) + { +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + _ygNode.SetContext(this); +#else + _managedBaseline = BaselineInternal; +#endif + } + Native.YGNodeSetBaselineFunc(_ygNode, _managedBaseline); } public void CalculateLayout() @@ -586,55 +603,63 @@ namespace Facebook.Yoga Native.YGNodeStyleGetDirection(_ygNode)); } -#if __IOS__ - [ObjCRuntime.MonoPInvokeCallback(typeof(YogaMeasureFunc))] +#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 = Native.YGNodeHandle.GetManaged(ygNodePtr); + return node.MeasureInternal(IntPtr.Zero, width, widthMode, height, heightMode); + } #endif - private static YogaSize MeasureInternal( + + private YogaSize MeasureInternal( IntPtr node, float width, YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode) { - - var yogaNodePointer = Native.YGNodeGetContext(node); - - YogaNode yogaNode = GCHandle.FromIntPtr(yogaNodePointer).Target as YogaNode; - - var measureFunction = yogaNode._measureFunction; - if (measureFunction == null) + if (_measureFunction == null) { throw new InvalidOperationException("Measure function is not defined."); } - return measureFunction(yogaNode, width, widthMode, height, heightMode); + return _measureFunction(this, width, widthMode, height, heightMode); } - -#if __IOS__ - [ObjCRuntime.MonoPInvokeCallback(typeof(YogaBaselineFunc))] -#endif - private static float BaselineInternal(IntPtr node, float width, float height) +#if (UNITY_IOS && !UNITY_EDITOR) || __IOS__ + [MonoPInvokeCallback(typeof(YogaBaselineFunc))] + private static float BaselineInternalIOS( + IntPtr ygNodePtr, + float width, + float height) { - var yogaNodePointer = Native.YGNodeGetContext(node); + var node = Native.YGNodeHandle.GetManaged(ygNodePtr); + return node.BaselineInternal(IntPtr.Zero, width, height); + } +#endif - YogaNode yogaNode = GCHandle.FromIntPtr(yogaNodePointer).Target as YogaNode; - - var baselineFunction = yogaNode._baselineFunction; - if (baselineFunction == null) + private float BaselineInternal(IntPtr node, float width, float height) + { + if (_baselineFunction == null) { throw new InvalidOperationException("Baseline function is not defined."); } - return baselineFunction(yogaNode, width, height); + return _baselineFunction(this, width, height); } public string Print(YogaPrintOptions options = - YogaPrintOptions.Layout | YogaPrintOptions.Style | YogaPrintOptions.Children) + YogaPrintOptions.Layout|YogaPrintOptions.Style|YogaPrintOptions.Children) { StringBuilder sb = new StringBuilder(); YogaLogger.Func orig = YogaLogger.Logger; - YogaLogger.Logger = (level, message) => { sb.Append(message); }; + YogaLogger.Logger = (level, message) => {sb.Append(message);}; Native.YGNodePrint(_ygNode, options); YogaLogger.Logger = orig; return sb.ToString();