From 243b6897912afe667edc50803b9eb18e4fbdd750 Mon Sep 17 00:00:00 2001 From: tj_devel709 Date: Fri, 1 Apr 2022 11:04:51 -0500 Subject: [PATCH 1/4] adding Nullability --- src/GameplayKit/GKBehavior.cs | 4 +++- src/GameplayKit/GKCompat.cs | 2 ++ src/GameplayKit/GKComponentSystem.cs | 4 +++- src/GameplayKit/GKCompositeBehavior.cs | 4 +++- src/GameplayKit/GKEntity.cs | 4 +++- src/GameplayKit/GKGameModel.cs | 2 ++ src/GameplayKit/GKGridGraph.cs | 6 ++++-- src/GameplayKit/GKObstacleGraph.cs | 6 ++++-- src/GameplayKit/GKPath.cs | 2 ++ src/GameplayKit/GKPolygonObstacle.cs | 2 ++ src/GameplayKit/GKPrimitives.cs | 2 ++ src/GameplayKit/GKState.cs | 2 ++ src/GameplayKit/GKStateMachine.cs | 6 ++++-- src/GameplayKit/NSArray_GameplayKit.cs | 2 ++ 14 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/GameplayKit/GKBehavior.cs b/src/GameplayKit/GKBehavior.cs index bf39623d3caf..cfc7c4bff468 100644 --- a/src/GameplayKit/GKBehavior.cs +++ b/src/GameplayKit/GKBehavior.cs @@ -7,6 +7,8 @@ // Copyright 2015 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; @@ -18,7 +20,7 @@ public GKGoal this [nuint index] { } public NSNumber this [GKGoal goal] { - get { return ObjectForKeyedSubscript (goal); } + get { return ObjectForKeyedSubscript (goal)!; } set { SetObject (value, goal); } } } diff --git a/src/GameplayKit/GKCompat.cs b/src/GameplayKit/GKCompat.cs index 8b64c9d2e959..002d069bd6f2 100644 --- a/src/GameplayKit/GKCompat.cs +++ b/src/GameplayKit/GKCompat.cs @@ -1,5 +1,7 @@ // Compatibility stubs +#nullable enable + using System; using Foundation; using ObjCRuntime; diff --git a/src/GameplayKit/GKComponentSystem.cs b/src/GameplayKit/GKComponentSystem.cs index 115dd03dc5ff..64023bf2caa3 100644 --- a/src/GameplayKit/GKComponentSystem.cs +++ b/src/GameplayKit/GKComponentSystem.cs @@ -7,6 +7,8 @@ // Copyright 2015 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; @@ -19,7 +21,7 @@ public GKComponentSystem () { } - public Type ComponentType { + public Type? ComponentType { get { return Class.Lookup (ComponentClass); } } diff --git a/src/GameplayKit/GKCompositeBehavior.cs b/src/GameplayKit/GKCompositeBehavior.cs index 56a52d70259c..8905013b3990 100644 --- a/src/GameplayKit/GKCompositeBehavior.cs +++ b/src/GameplayKit/GKCompositeBehavior.cs @@ -7,13 +7,15 @@ // Copyright 2016 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; namespace GameplayKit { public partial class GKCompositeBehavior { - public GKBehavior this [nuint index] { + public new GKBehavior this [nuint index] { get { return ObjectAtIndexedSubscript (index); } } diff --git a/src/GameplayKit/GKEntity.cs b/src/GameplayKit/GKEntity.cs index 3235f00c29cf..9c9828905aec 100644 --- a/src/GameplayKit/GKEntity.cs +++ b/src/GameplayKit/GKEntity.cs @@ -7,6 +7,8 @@ // Copyright 2015 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; @@ -19,7 +21,7 @@ public void RemoveComponent (Type componentType) RemoveComponent (GKState.GetClass (componentType, "componentType")); } - public GKComponent GetComponent (Type componentType) + public GKComponent? GetComponent (Type componentType) { return GetComponent (GKState.GetClass (componentType, "componentType")); } diff --git a/src/GameplayKit/GKGameModel.cs b/src/GameplayKit/GKGameModel.cs index 287f2431b1b5..5cc5cbd361de 100644 --- a/src/GameplayKit/GKGameModel.cs +++ b/src/GameplayKit/GKGameModel.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; namespace GameplayKit { diff --git a/src/GameplayKit/GKGridGraph.cs b/src/GameplayKit/GKGridGraph.cs index f7e3675ec56c..87b52af4f3f3 100644 --- a/src/GameplayKit/GKGridGraph.cs +++ b/src/GameplayKit/GKGridGraph.cs @@ -7,6 +7,8 @@ // Copyright 2016 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using ObjCRuntime; #if NET @@ -19,12 +21,12 @@ namespace GameplayKit { public partial class GKGridGraph { #if !NET - public virtual GKGridGraphNode GetNodeAt (Vector2i position) + public virtual GKGridGraphNode? GetNodeAt (Vector2i position) { return GetNodeAt (position); } #endif - public NodeType GetNodeAt (Vector2i position) where NodeType : GKGridGraphNode + public NodeType? GetNodeAt (Vector2i position) where NodeType : GKGridGraphNode { return Runtime.GetNSObject (_GetNodeAt (position)); } diff --git a/src/GameplayKit/GKObstacleGraph.cs b/src/GameplayKit/GKObstacleGraph.cs index dc20e37a99f1..2e625fe8c16c 100644 --- a/src/GameplayKit/GKObstacleGraph.cs +++ b/src/GameplayKit/GKObstacleGraph.cs @@ -7,6 +7,8 @@ // Copyright 2016 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; @@ -53,12 +55,12 @@ public GKObstacleGraph (NSCoder coder) : base (coder) { } - public static GKObstacleGraph FromObstacles (GKPolygonObstacle [] obstacles, float bufferRadius) + public static new GKObstacleGraph? FromObstacles (GKPolygonObstacle [] obstacles, float bufferRadius) { return Runtime.GetNSObject > (GraphWithObstacles (obstacles, bufferRadius, new Class (typeof (NodeType)))); } - public NodeType [] GetNodes (GKPolygonObstacle obstacle) + public new NodeType [] GetNodes (GKPolygonObstacle obstacle) { return NSArray.ArrayFromHandle (_GetNodes (obstacle)); } diff --git a/src/GameplayKit/GKPath.cs b/src/GameplayKit/GKPath.cs index 94e6237ed701..63dc2f03e59b 100644 --- a/src/GameplayKit/GKPath.cs +++ b/src/GameplayKit/GKPath.cs @@ -7,6 +7,8 @@ // Copyright 2015 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; diff --git a/src/GameplayKit/GKPolygonObstacle.cs b/src/GameplayKit/GKPolygonObstacle.cs index 9b3c44d95753..fd1c731b0fd8 100644 --- a/src/GameplayKit/GKPolygonObstacle.cs +++ b/src/GameplayKit/GKPolygonObstacle.cs @@ -7,6 +7,8 @@ // Copyright 2015 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; diff --git a/src/GameplayKit/GKPrimitives.cs b/src/GameplayKit/GKPrimitives.cs index ebfbbb544ef9..9ebafe33de86 100644 --- a/src/GameplayKit/GKPrimitives.cs +++ b/src/GameplayKit/GKPrimitives.cs @@ -7,6 +7,8 @@ // Copyright 2016 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using System.Runtime.InteropServices; using ObjCRuntime; diff --git a/src/GameplayKit/GKState.cs b/src/GameplayKit/GKState.cs index 9e3a65671571..57340c5cf4e1 100644 --- a/src/GameplayKit/GKState.cs +++ b/src/GameplayKit/GKState.cs @@ -7,6 +7,8 @@ // Copyright 2015 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; diff --git a/src/GameplayKit/GKStateMachine.cs b/src/GameplayKit/GKStateMachine.cs index 5c258c567d9f..05322dd4a098 100644 --- a/src/GameplayKit/GKStateMachine.cs +++ b/src/GameplayKit/GKStateMachine.cs @@ -7,6 +7,8 @@ // Copyright 2015 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; @@ -14,12 +16,12 @@ namespace GameplayKit { public partial class GKStateMachine : NSObject { - public GKState GetState (Type stateType) + public GKState? GetState (Type stateType) { return GetState (GKState.GetClass (stateType, "stateType")); } - public GKState GetState (GKState state) + public GKState? GetState (GKState state) { return GetState (GKState.GetClass (state, "state")); } diff --git a/src/GameplayKit/NSArray_GameplayKit.cs b/src/GameplayKit/NSArray_GameplayKit.cs index 49cc9d19c40c..2f48cda55969 100644 --- a/src/GameplayKit/NSArray_GameplayKit.cs +++ b/src/GameplayKit/NSArray_GameplayKit.cs @@ -7,6 +7,8 @@ // Copyright 2016 Xamarin Inc. All rights reserved. // +#nullable enable + using System; using Foundation; using ObjCRuntime; From 89f8375a4e5a1aec7445801a9d32a194666d0c84 Mon Sep 17 00:00:00 2001 From: tj_devel709 Date: Fri, 1 Apr 2022 12:13:27 -0500 Subject: [PATCH 2/4] throw better null exceptions --- src/GameplayKit/GKPath.cs | 8 ++++---- src/GameplayKit/GKPolygonObstacle.cs | 4 ++-- src/GameplayKit/GKPrimitives.cs | 2 +- src/GameplayKit/GKState.cs | 4 ++-- src/GameplayKit/NSArray_GameplayKit.cs | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/GameplayKit/GKPath.cs b/src/GameplayKit/GKPath.cs index 63dc2f03e59b..6871d8a34e5b 100644 --- a/src/GameplayKit/GKPath.cs +++ b/src/GameplayKit/GKPath.cs @@ -28,7 +28,7 @@ public partial class GKPath { public static GKPath FromPoints (Vector2[] points, float radius, bool cyclical) { if (points == null) - throw new ArgumentNullException ("points"); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; try { @@ -45,7 +45,7 @@ public static GKPath FromPoints (Vector2[] points, float radius, bool cyclical) public GKPath (Vector2 [] points, float radius, bool cyclical) { if (points == null) - throw new ArgumentNullException ("points"); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; try { @@ -70,7 +70,7 @@ public GKPath (Vector2 [] points, float radius, bool cyclical) public static GKPath FromPoints (Vector3 [] points, float radius, bool cyclical) { if (points == null) - throw new ArgumentNullException ("points"); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; try { @@ -96,7 +96,7 @@ public static GKPath FromPoints (Vector3 [] points, float radius, bool cyclical) public GKPath (Vector3 [] points, float radius, bool cyclical) { if (points == null) - throw new ArgumentNullException ("points"); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; try { diff --git a/src/GameplayKit/GKPolygonObstacle.cs b/src/GameplayKit/GKPolygonObstacle.cs index fd1c731b0fd8..dfaafb680be6 100644 --- a/src/GameplayKit/GKPolygonObstacle.cs +++ b/src/GameplayKit/GKPolygonObstacle.cs @@ -25,7 +25,7 @@ public partial class GKPolygonObstacle { public static GKPolygonObstacle FromPoints (Vector2 [] points) { if (points == null) - throw new ArgumentNullException ("points"); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var size = Marshal.SizeOf (typeof (Vector2)); var length = points.Length * size; @@ -48,7 +48,7 @@ public static GKPolygonObstacle FromPoints (Vector2 [] points) static unsafe IntPtr GetPointer (Vector2[] points) { if (points == null) - throw new ArgumentNullException ("points"); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); if (ctor_pointer != IntPtr.Zero) { // This can occur of a previous call to the base ctor threw an exception diff --git a/src/GameplayKit/GKPrimitives.cs b/src/GameplayKit/GKPrimitives.cs index 9ebafe33de86..24b77e5ef6f6 100644 --- a/src/GameplayKit/GKPrimitives.cs +++ b/src/GameplayKit/GKPrimitives.cs @@ -72,7 +72,7 @@ public Vector3 [] Points { } set { if (value == null) - throw new ArgumentNullException (nameof (value)); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (value)); if (value.Length != 3) throw new ArgumentOutOfRangeException (nameof (value), "The length of the Value array must be 3"); points = value; diff --git a/src/GameplayKit/GKState.cs b/src/GameplayKit/GKState.cs index 57340c5cf4e1..1c6b45a0ea18 100644 --- a/src/GameplayKit/GKState.cs +++ b/src/GameplayKit/GKState.cs @@ -19,7 +19,7 @@ public partial class GKState { internal static Class GetClass (Type type, string parameterName) { if (type == null) - throw new ArgumentNullException (parameterName); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (parameterName)); var klass = new Class (type); // most API do not accept null so we throw in managed code instead of crashing the app @@ -32,7 +32,7 @@ internal static Class GetClass (Type type, string parameterName) internal static Class GetClass (NSObject instance, string parameterName) { if (instance == null) - throw new ArgumentNullException (parameterName); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (parameterName)); var klass = instance.Class; if ((klass == null) || (klass.Handle == IntPtr.Zero)) diff --git a/src/GameplayKit/NSArray_GameplayKit.cs b/src/GameplayKit/NSArray_GameplayKit.cs index 2f48cda55969..dc16ad86597e 100644 --- a/src/GameplayKit/NSArray_GameplayKit.cs +++ b/src/GameplayKit/NSArray_GameplayKit.cs @@ -30,7 +30,7 @@ public static class NSArray_GameplayKit { public static T [] GetShuffledArray (this NSArray This, GKRandomSource randomSource) where T : class, INativeObject { if (randomSource == null) - throw new ArgumentNullException (nameof (randomSource)); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (randomSource)); return NSArray.ArrayFromHandle (Messaging.IntPtr_objc_msgSend_IntPtr (This.Handle, Selector.GetHandle ("shuffledArrayWithRandomSource:"), randomSource.Handle)); } From be3d02fa85da7b9b1fa978cc44f0d95a516294b0 Mon Sep 17 00:00:00 2001 From: tj_devel709 Date: Fri, 1 Apr 2022 12:14:48 -0500 Subject: [PATCH 3/4] use is null --- src/GameplayKit/GKPath.cs | 8 ++++---- src/GameplayKit/GKPolygonObstacle.cs | 4 ++-- src/GameplayKit/GKPrimitives.cs | 2 +- src/GameplayKit/GKState.cs | 6 +++--- src/GameplayKit/NSArray_GameplayKit.cs | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/GameplayKit/GKPath.cs b/src/GameplayKit/GKPath.cs index 6871d8a34e5b..d3959f0bf6d1 100644 --- a/src/GameplayKit/GKPath.cs +++ b/src/GameplayKit/GKPath.cs @@ -27,7 +27,7 @@ public partial class GKPath { public static GKPath FromPoints (Vector2[] points, float radius, bool cyclical) { - if (points == null) + if (points is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; @@ -44,7 +44,7 @@ public static GKPath FromPoints (Vector2[] points, float radius, bool cyclical) [DesignatedInitializer] public GKPath (Vector2 [] points, float radius, bool cyclical) { - if (points == null) + if (points is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; @@ -69,7 +69,7 @@ public GKPath (Vector2 [] points, float radius, bool cyclical) #endif public static GKPath FromPoints (Vector3 [] points, float radius, bool cyclical) { - if (points == null) + if (points is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; @@ -95,7 +95,7 @@ public static GKPath FromPoints (Vector3 [] points, float radius, bool cyclical) #endif public GKPath (Vector3 [] points, float radius, bool cyclical) { - if (points == null) + if (points is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var buffer = IntPtr.Zero; diff --git a/src/GameplayKit/GKPolygonObstacle.cs b/src/GameplayKit/GKPolygonObstacle.cs index dfaafb680be6..c469723a3071 100644 --- a/src/GameplayKit/GKPolygonObstacle.cs +++ b/src/GameplayKit/GKPolygonObstacle.cs @@ -24,7 +24,7 @@ public partial class GKPolygonObstacle { public static GKPolygonObstacle FromPoints (Vector2 [] points) { - if (points == null) + if (points is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); var size = Marshal.SizeOf (typeof (Vector2)); @@ -47,7 +47,7 @@ public static GKPolygonObstacle FromPoints (Vector2 [] points) static unsafe IntPtr GetPointer (Vector2[] points) { - if (points == null) + if (points is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (points)); if (ctor_pointer != IntPtr.Zero) { diff --git a/src/GameplayKit/GKPrimitives.cs b/src/GameplayKit/GKPrimitives.cs index 24b77e5ef6f6..1e554d418b6c 100644 --- a/src/GameplayKit/GKPrimitives.cs +++ b/src/GameplayKit/GKPrimitives.cs @@ -71,7 +71,7 @@ public Vector3 [] Points { return points ?? (points = new Vector3 [3]); } set { - if (value == null) + if (value is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (value)); if (value.Length != 3) throw new ArgumentOutOfRangeException (nameof (value), "The length of the Value array must be 3"); diff --git a/src/GameplayKit/GKState.cs b/src/GameplayKit/GKState.cs index 1c6b45a0ea18..c89638d03517 100644 --- a/src/GameplayKit/GKState.cs +++ b/src/GameplayKit/GKState.cs @@ -18,7 +18,7 @@ public partial class GKState { internal static Class GetClass (Type type, string parameterName) { - if (type == null) + if (type is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (parameterName)); var klass = new Class (type); @@ -31,11 +31,11 @@ internal static Class GetClass (Type type, string parameterName) internal static Class GetClass (NSObject instance, string parameterName) { - if (instance == null) + if (instance is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (parameterName)); var klass = instance.Class; - if ((klass == null) || (klass.Handle == IntPtr.Zero)) + if ((klass is null) || (klass.Handle == IntPtr.Zero)) throw new ArgumentException ("Not an type exposed to ObjC", parameterName); return klass; diff --git a/src/GameplayKit/NSArray_GameplayKit.cs b/src/GameplayKit/NSArray_GameplayKit.cs index dc16ad86597e..3de454a80136 100644 --- a/src/GameplayKit/NSArray_GameplayKit.cs +++ b/src/GameplayKit/NSArray_GameplayKit.cs @@ -29,7 +29,7 @@ public static class NSArray_GameplayKit { [Export ("shuffledArrayWithRandomSource:")] public static T [] GetShuffledArray (this NSArray This, GKRandomSource randomSource) where T : class, INativeObject { - if (randomSource == null) + if (randomSource is null) ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (randomSource)); return NSArray.ArrayFromHandle (Messaging.IntPtr_objc_msgSend_IntPtr (This.Handle, Selector.GetHandle ("shuffledArrayWithRandomSource:"), randomSource.Handle)); } From f0eb8106ae2e4e799b78a18039c9f2c10d1cb0a7 Mon Sep 17 00:00:00 2001 From: tj_devel709 Date: Mon, 11 Apr 2022 15:08:35 -0500 Subject: [PATCH 4/4] Added test --- src/GameplayKit/GKBehavior.cs | 5 ++- .../GameplayKit/GKBehaviorTests.cs | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/monotouch-test/GameplayKit/GKBehaviorTests.cs diff --git a/src/GameplayKit/GKBehavior.cs b/src/GameplayKit/GKBehavior.cs index cfc7c4bff468..de4eb67527bf 100644 --- a/src/GameplayKit/GKBehavior.cs +++ b/src/GameplayKit/GKBehavior.cs @@ -20,7 +20,10 @@ public GKGoal this [nuint index] { } public NSNumber this [GKGoal goal] { - get { return ObjectForKeyedSubscript (goal)!; } + // The docs show that ObjectForKeyedSubscript should return 0.0 if the GKGoal is not + // available but actually returns null: https://developer.apple.com/documentation/gameplaykit/gkbehavior/1388723-objectforkeyedsubscript?language=objc + // radar filed here: https://feedbackassistant.apple.com/feedback/9979863 + get { return ObjectForKeyedSubscript (goal) ?? throw new ArgumentOutOfRangeException (nameof (goal)); } set { SetObject (value, goal); } } } diff --git a/tests/monotouch-test/GameplayKit/GKBehaviorTests.cs b/tests/monotouch-test/GameplayKit/GKBehaviorTests.cs new file mode 100644 index 000000000000..b5d9f97d5874 --- /dev/null +++ b/tests/monotouch-test/GameplayKit/GKBehaviorTests.cs @@ -0,0 +1,42 @@ +// +// Unit tests for GKBehavior +// +// Authors: +// TJ Lambert +// +// +// Copyright 2022 Xamarin Inc. All rights reserved. +// + +#if !__WATCHOS__ + +using System; +using NUnit.Framework; + +using Foundation; +using GameplayKit; + +namespace MonoTouchFixtures.GamePlayKit { + [TestFixture] + [Preserve (AllMembers = true)] + public class GKBehaviorTests { + + [Test] + public void ObjectForKeyedSubscriptTest () + { + // Create 2 GKGoals to use in the gkBehavior + var goal1 = GKGoal.GetGoalToReachTargetSpeed (10); + var goal2 = GKGoal.GetGoalToReachTargetSpeed (15); + + // Create a GKGoal that will not be included in gkBehavior + var goal3 = GKGoal.GetGoalToReachTargetSpeed (15); + + // Create a GKBehavior from those first 2 goals + var gkBehavior = GKBehavior.FromGoals (new GKGoal [] { goal1, goal2 }); + + // Searching the gkBehavior for a non-exisitant goal should throw an ArgumentOutOfRangeException + Assert.Throws ( () => { _ = gkBehavior[goal3]; }, "The ObjectForKeyedSubscript indexer should throw ArgumentOutOfRangeException if the goal is not in the behavior's list of GKGoals."); + } + } +} +#endif