Merge pull request #24714 from jtattermusch/better_unity_check

Improve PlatformApis check for detecting Unity
pull/24750/head
Jan Tattermusch 4 years ago committed by GitHub
commit 2ebd3c3c02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/csharp/Grpc.Core/Internal/NativeExtension.cs
  2. 126
      src/csharp/Grpc.Core/Internal/PlatformApis.cs

@ -129,14 +129,12 @@ namespace Grpc.Core.Internal
/// </summary> /// </summary>
private static NativeMethods LoadNativeMethodsUnity() private static NativeMethods LoadNativeMethodsUnity()
{ {
switch (PlatformApis.GetUnityRuntimePlatform()) if (PlatformApis.IsUnityIOS)
{ {
case "IPhonePlayer": return new NativeMethods(new NativeMethods.DllImportsFromStaticLib());
return new NativeMethods(new NativeMethods.DllImportsFromStaticLib());
default:
// most other platforms load unity plugins as a shared library
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib());
} }
// most other platforms load unity plugins as a shared library
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib());
} }
/// <summary> /// <summary>

@ -20,6 +20,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
@ -32,7 +33,11 @@ namespace Grpc.Core.Internal
/// </summary> /// </summary>
internal static class PlatformApis internal static class PlatformApis
{ {
const string UnityEngineApplicationClassName = "UnityEngine.Application, UnityEngine"; const string UnityEngineAssemblyName = "UnityEngine";
const string UnityEngineApplicationClassName = "UnityEngine.Application";
const string UnityIPhonePlayer = "IPhonePlayer";
const string XamarinAndroidObjectClassName = "Java.Lang.Object, Mono.Android"; const string XamarinAndroidObjectClassName = "Java.Lang.Object, Mono.Android";
const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS"; const string XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
@ -41,8 +46,7 @@ namespace Grpc.Core.Internal
static readonly bool isWindows; static readonly bool isWindows;
static readonly bool isMono; static readonly bool isMono;
static readonly bool isNetCore; static readonly bool isNetCore;
static readonly bool isUnity; static readonly string unityApplicationPlatform;
static readonly bool isUnityIOS;
static readonly bool isXamarin; static readonly bool isXamarin;
static readonly bool isXamarinIOS; static readonly bool isXamarinIOS;
static readonly bool isXamarinAndroid; static readonly bool isXamarinAndroid;
@ -70,21 +74,7 @@ namespace Grpc.Core.Internal
isMono = Type.GetType("Mono.Runtime") != null; isMono = Type.GetType("Mono.Runtime") != null;
// Unity // Unity
var unityApplicationClass = Type.GetType(UnityEngineApplicationClassName); unityApplicationPlatform = TryGetUnityApplicationPlatform();
if (unityApplicationClass != null)
{
isUnity = true;
// Consult value of Application.platform via reflection
// https://docs.unity3d.com/ScriptReference/Application-platform.html
var platformProperty = unityApplicationClass.GetTypeInfo().GetProperty("platform");
var unityRuntimePlatform = platformProperty?.GetValue(null)?.ToString();
isUnityIOS = (unityRuntimePlatform == "IPhonePlayer");
}
else
{
isUnity = false;
isUnityIOS = false;
}
// Xamarin // Xamarin
isXamarinIOS = Type.GetType(XamarinIOSObjectClassName) != null; isXamarinIOS = Type.GetType(XamarinIOSObjectClassName) != null;
@ -92,79 +82,46 @@ namespace Grpc.Core.Internal
isXamarin = isXamarinIOS || isXamarinAndroid; isXamarin = isXamarinIOS || isXamarinAndroid;
} }
public static bool IsLinux public static bool IsLinux => isLinux;
{
get { return isLinux; }
}
public static bool IsMacOSX public static bool IsMacOSX => isMacOSX;
{
get { return isMacOSX; }
}
public static bool IsWindows public static bool IsWindows => isWindows;
{
get { return isWindows; }
}
public static bool IsMono public static bool IsMono => isMono;
{
get { return isMono; }
}
/// <summary> /// <summary>
/// true if running on Unity platform. /// true if running on Unity platform.
/// </summary> /// </summary>
public static bool IsUnity public static bool IsUnity => unityApplicationPlatform != null;
{
get { return isUnity; }
}
/// <summary> /// <summary>
/// true if running on Unity iOS, false otherwise. /// true if running on Unity iOS, false otherwise.
/// </summary> /// </summary>
public static bool IsUnityIOS public static bool IsUnityIOS => unityApplicationPlatform == UnityIPhonePlayer;
{
get { return isUnityIOS; }
}
/// <summary> /// <summary>
/// true if running on a Xamarin platform (either Xamarin.Android or Xamarin.iOS), /// true if running on a Xamarin platform (either Xamarin.Android or Xamarin.iOS),
/// false otherwise. /// false otherwise.
/// </summary> /// </summary>
public static bool IsXamarin public static bool IsXamarin => isXamarin;
{
get { return isXamarin; }
}
/// <summary> /// <summary>
/// true if running on Xamarin.iOS, false otherwise. /// true if running on Xamarin.iOS, false otherwise.
/// </summary> /// </summary>
public static bool IsXamarinIOS public static bool IsXamarinIOS => isXamarinIOS;
{
get { return isXamarinIOS; }
}
/// <summary> /// <summary>
/// true if running on Xamarin.Android, false otherwise. /// true if running on Xamarin.Android, false otherwise.
/// </summary> /// </summary>
public static bool IsXamarinAndroid public static bool IsXamarinAndroid => isXamarinAndroid;
{
get { return isXamarinAndroid; }
}
/// <summary> /// <summary>
/// true if running on .NET Core (CoreCLR), false otherwise. /// true if running on .NET Core (CoreCLR), false otherwise.
/// </summary> /// </summary>
public static bool IsNetCore public static bool IsNetCore => isNetCore;
{
get { return isNetCore; }
}
public static bool Is64Bit public static bool Is64Bit => IntPtr.Size == 8;
{
get { return IntPtr.Size == 8; }
}
/// <summary> /// <summary>
/// Returns <c>UnityEngine.Application.platform</c> as a string. /// Returns <c>UnityEngine.Application.platform</c> as a string.
@ -172,14 +129,49 @@ namespace Grpc.Core.Internal
/// Value is obtained via reflection to avoid compile-time dependency on Unity. /// Value is obtained via reflection to avoid compile-time dependency on Unity.
/// This method should only be called if <c>IsUnity</c> is <c>true</c>. /// This method should only be called if <c>IsUnity</c> is <c>true</c>.
/// </summary> /// </summary>
public static string GetUnityRuntimePlatform() public static string GetUnityApplicationPlatform()
{ {
GrpcPreconditions.CheckState(IsUnity, "Not running on Unity."); GrpcPreconditions.CheckState(IsUnity, "Not running on Unity.");
#if NETSTANDARD return unityApplicationPlatform;
return Type.GetType(UnityEngineApplicationClassName).GetTypeInfo().GetProperty("platform").GetValue(null).ToString(); }
#else
return Type.GetType(UnityEngineApplicationClassName).GetProperty("platform").GetValue(null).ToString(); /// <summary>
/// Returns <c>UnityEngine.Application.platform</c> as a string or <c>null</c>
/// if not running on Unity.
/// Value is obtained via reflection to avoid compile-time dependency on Unity.
/// </summary>
static string TryGetUnityApplicationPlatform()
{
Assembly unityAssembly = null;
#if !NETSTANDARD1_5
// On netstandard1.5, AppDomain is not available and we just short-circuit the logic there.
// This is fine because only the net45 or netstandard2.0 version Grpc.Core assembly is going to used in Unity.
// NOTE: Instead of trying to load the UnityEngine.Application class via <c>Type.GetType()</c>
// we are using a more sneaky approach to avoid inadvertently loading the UnityEngine
// assembly (that might be available even when we are not actually on Unity, resulting
// in false positive). See https://github.com/grpc/grpc/issues/18801
unityAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(assembly => assembly.GetName().Name == UnityEngineAssemblyName);
#endif #endif
var applicationClass = unityAssembly?.GetType(UnityEngineApplicationClassName);
var platformProperty = applicationClass?.GetTypeInfo().GetProperty("platform", BindingFlags.Static | BindingFlags.Public);
try
{
// Consult value of Application.platform via reflection
// https://docs.unity3d.com/ScriptReference/Application-platform.html
return platformProperty?.GetValue(null)?.ToString();
}
catch (TargetInvocationException)
{
// The getter for Application.platform is defined as "extern", so if UnityEngine assembly is loaded outside of a Unity application,
// the definition for the getter will be missing - note that this is a sneaky trick that helps us tell a real Unity application from a non-unity
// application which just happens to have loaded the UnityEngine.dll assembly.
// https://github.com/Unity-Technologies/UnityCsReference/blob/61f92bd79ae862c4465d35270f9d1d57befd1761/Runtime/Export/Application/Application.bindings.cs#L375
// See https://github.com/grpc/grpc/issues/23334
// If TargetInvocationException was thrown, it most likely means that the method definition for the extern method is missing,
// and we are going to interpret this as "not running on Unity".
return null;
}
} }
[DllImport("libc")] [DllImport("libc")]

Loading…
Cancel
Save