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>
private static NativeMethods LoadNativeMethodsUnity()
{
switch (PlatformApis.GetUnityRuntimePlatform())
if (PlatformApis.IsUnityIOS)
{
case "IPhonePlayer":
return new NativeMethods(new NativeMethods.DllImportsFromStaticLib());
default:
// most other platforms load unity plugins as a shared library
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib());
return new NativeMethods(new NativeMethods.DllImportsFromStaticLib());
}
// most other platforms load unity plugins as a shared library
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib());
}
/// <summary>

@ -20,6 +20,7 @@ using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
@ -32,7 +33,11 @@ namespace Grpc.Core.Internal
/// </summary>
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 XamarinIOSObjectClassName = "Foundation.NSObject, Xamarin.iOS";
@ -41,8 +46,7 @@ namespace Grpc.Core.Internal
static readonly bool isWindows;
static readonly bool isMono;
static readonly bool isNetCore;
static readonly bool isUnity;
static readonly bool isUnityIOS;
static readonly string unityApplicationPlatform;
static readonly bool isXamarin;
static readonly bool isXamarinIOS;
static readonly bool isXamarinAndroid;
@ -70,21 +74,7 @@ namespace Grpc.Core.Internal
isMono = Type.GetType("Mono.Runtime") != null;
// Unity
var unityApplicationClass = Type.GetType(UnityEngineApplicationClassName);
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;
}
unityApplicationPlatform = TryGetUnityApplicationPlatform();
// Xamarin
isXamarinIOS = Type.GetType(XamarinIOSObjectClassName) != null;
@ -92,79 +82,46 @@ namespace Grpc.Core.Internal
isXamarin = isXamarinIOS || isXamarinAndroid;
}
public static bool IsLinux
{
get { return isLinux; }
}
public static bool IsLinux => isLinux;
public static bool IsMacOSX
{
get { return isMacOSX; }
}
public static bool IsMacOSX => isMacOSX;
public static bool IsWindows
{
get { return isWindows; }
}
public static bool IsWindows => isWindows;
public static bool IsMono
{
get { return isMono; }
}
public static bool IsMono => isMono;
/// <summary>
/// true if running on Unity platform.
/// </summary>
public static bool IsUnity
{
get { return isUnity; }
}
public static bool IsUnity => unityApplicationPlatform != null;
/// <summary>
/// true if running on Unity iOS, false otherwise.
/// </summary>
public static bool IsUnityIOS
{
get { return isUnityIOS; }
}
public static bool IsUnityIOS => unityApplicationPlatform == UnityIPhonePlayer;
/// <summary>
/// true if running on a Xamarin platform (either Xamarin.Android or Xamarin.iOS),
/// false otherwise.
/// </summary>
public static bool IsXamarin
{
get { return isXamarin; }
}
public static bool IsXamarin => isXamarin;
/// <summary>
/// true if running on Xamarin.iOS, false otherwise.
/// </summary>
public static bool IsXamarinIOS
{
get { return isXamarinIOS; }
}
public static bool IsXamarinIOS => isXamarinIOS;
/// <summary>
/// true if running on Xamarin.Android, false otherwise.
/// </summary>
public static bool IsXamarinAndroid
{
get { return isXamarinAndroid; }
}
public static bool IsXamarinAndroid => isXamarinAndroid;
/// <summary>
/// true if running on .NET Core (CoreCLR), false otherwise.
/// </summary>
public static bool IsNetCore
{
get { return isNetCore; }
}
public static bool IsNetCore => isNetCore;
public static bool Is64Bit
{
get { return IntPtr.Size == 8; }
}
public static bool Is64Bit => IntPtr.Size == 8;
/// <summary>
/// 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.
/// This method should only be called if <c>IsUnity</c> is <c>true</c>.
/// </summary>
public static string GetUnityRuntimePlatform()
public static string GetUnityApplicationPlatform()
{
GrpcPreconditions.CheckState(IsUnity, "Not running on Unity.");
#if NETSTANDARD
return Type.GetType(UnityEngineApplicationClassName).GetTypeInfo().GetProperty("platform").GetValue(null).ToString();
#else
return Type.GetType(UnityEngineApplicationClassName).GetProperty("platform").GetValue(null).ToString();
return unityApplicationPlatform;
}
/// <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
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")]

Loading…
Cancel
Save