Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions com.unity.netcode.gameobjects/Editor/CodeGen/CodeGenHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,148 @@ public static bool HasInterface(this TypeReference typeReference, string interfa
}
}

/// <summary>
/// Checks a generic method instantiation against the generic constraints of the method it instantiates.
/// Mono does not enforce these on the calls codegen emits, but CoreCLR does, and it rejects the entire
/// calling method if any single call in it violates them.
/// </summary>
/// <param name="method">The instantiation to validate.</param>
/// <param name="violation">Why the instantiation is invalid, or null when it is valid.</param>
/// <returns>True if every generic argument satisfies its parameter's constraints.</returns>
public static bool SatisfiesGenericConstraints(this GenericInstanceMethod method, out string violation)
{
var genericParameters = method.ElementMethod.Resolve().GenericParameters;
for (var i = 0; i < genericParameters.Count; ++i)
{
var parameter = genericParameters[i];
var argument = method.GenericArguments[i];
violation = GetConstraintViolation(parameter, argument, method.GenericArguments);
if (violation != null)
{
violation = $"{argument.FullName} {violation} (required by {parameter.Name} on {method.ElementMethod.Name})";
return false;
}
}

violation = null;
return true;
}

private static string GetConstraintViolation(GenericParameter parameter, TypeReference argument, IList<TypeReference> methodArguments)
{
var resolved = argument.Resolve();
if (resolved == null)
{
return "could not be resolved";
}

if (parameter.HasNotNullableValueTypeConstraint && (!resolved.IsValueType || resolved.FullName == typeof(Nullable<>).FullName))
{
return "is not a non-nullable value type";
}

if (parameter.HasReferenceTypeConstraint && resolved.IsValueType)
{
return "is not a reference type";
}

if (parameter.HasDefaultConstructorConstraint && !resolved.IsValueType && (resolved.IsAbstract || !HasPublicParameterlessConstructor(resolved)))
{
return "does not have a public parameterless constructor";
}

foreach (var constraint in parameter.Constraints)
{
#if CECIL_CONSTRAINTS_ARE_TYPE_REFERENCES
var constraintType = constraint;
#else
var constraintType = constraint.ConstraintType;
#endif
// The unmanaged constraint is encoded as System.ValueType modreq(UnmanagedType); the runtime only checks the value type part.
if (constraintType is RequiredModifierType modifierType)
{
constraintType = modifierType.ElementType;
}

constraintType = SubstituteGenericArguments(constraintType, GenericParameterType.Method, methodArguments);
if (constraintType.FullName == typeof(ValueType).FullName)
{
continue;
}

if (!IsAssignableTo(argument, constraintType.FullName))
{
return $"does not implement or derive from {constraintType.FullName}";
}
}

return null;
}

private static bool HasPublicParameterlessConstructor(TypeDefinition type)
{
foreach (var method in type.Methods)
{
if (method.IsConstructor && !method.IsStatic && method.IsPublic && !method.HasParameters)
{
return true;
}
}

return false;
}

private static bool IsAssignableTo(TypeReference type, string targetFullName)
{
if (type.FullName == targetFullName)
{
return true;
}

var resolved = type.Resolve();
if (resolved == null)
{
return false;
}

// Interfaces and the base type are declared against the definition's own generic parameters, so close them over this instance's arguments.
var typeArguments = (type as GenericInstanceType)?.GenericArguments;
foreach (var implemented in resolved.Interfaces)
{
if (IsAssignableTo(SubstituteGenericArguments(implemented.InterfaceType, GenericParameterType.Type, typeArguments), targetFullName))
{
return true;
}
}

return resolved.BaseType != null && IsAssignableTo(SubstituteGenericArguments(resolved.BaseType, GenericParameterType.Type, typeArguments), targetFullName);
}

private static TypeReference SubstituteGenericArguments(TypeReference type, GenericParameterType parameterType, IList<TypeReference> arguments)
{
if (arguments == null)
{
return type;
}

if (type is GenericParameter genericParameter && genericParameter.Type == parameterType)
{
return arguments[genericParameter.Position];
}

if (type is GenericInstanceType genericInstance)
{
var substituted = new GenericInstanceType(genericInstance.ElementType);
foreach (var argument in genericInstance.GenericArguments)
{
substituted.GenericArguments.Add(SubstituteGenericArguments(argument, parameterType, arguments));
}
return substituted;
}

return type;
}

public static bool IsSerializable(this TypeReference typeReference)
{
var typeSystem = typeReference.Module.TypeSystem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,19 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
equalityMethod.GenericArguments.Add(type);
}

// A single call violating its constraints makes CoreCLR reject this whole method, dropping every registration in the assembly.
if (serializeMethod != null && !serializeMethod.SatisfiesGenericConstraints(out var serializeViolation))
{
m_Diagnostics.AddError($"{type} cannot be used in a network variable - {serializeViolation}.");
continue;
}

if (!equalityMethod.SatisfiesGenericConstraints(out var equalityViolation))
{
m_Diagnostics.AddError($"{type} cannot be used in a network variable - {equalityViolation}.");
continue;
}

if (serializeMethod != null)
{
instructions.Add(processor.Create(OpCodes.Call, m_MainModule.ImportReference(serializeMethod)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ internal static bool EqualityEqualsList<TValueType>(ref List<TValueType> a, ref
return true;
}

internal static bool EqualityEqualsHashSet<TValueType>(ref HashSet<TValueType> a, ref HashSet<TValueType> b) where TValueType : IEquatable<TValueType>
internal static bool EqualityEqualsHashSet<TValueType>(ref HashSet<TValueType> a, ref HashSet<TValueType> b)
{
if (a == null != (b == null))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ public static void InitializeEqualityChecker_List<T>()
/// Registers an unmanaged type that will be checked for equality using T.Equals()
/// </summary>
/// <typeparam name="T">The type of elements in the HashSet</typeparam>
public static void InitializeEqualityChecker_HashSet<T>() where T : IEquatable<T>
public static void InitializeEqualityChecker_HashSet<T>()
{
NetworkVariableSerialization<HashSet<T>>.AreEqual = NetworkVariableEquality<T>.EqualityEqualsHashSet;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ internal class BuildTests
// Increased the Build test timeout from 3 to 10 minutes.
[Timeout(900000)]
[Test]
#if UNITY_EDITOR_LINUX
// Temporary: BuildPlayer hangs past the job timeout on the ubuntu CI agents (6000.7 and trunk), while Windows and macOS pass.
[Ignore("BuildPlayer hangs on the ubuntu CI agents; temporarily disabled on Linux editors until the ubuntu CI issue is resolved.")]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add Jira ticket to remember about this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#endif
public void BasicBuildTest()
{
var execAssembly = Assembly.GetExecutingAssembly();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ protected override void OnServerAndClientsCreated()


[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO NetworkVariable serialization codegen not generated for some types on CoreCLR (falls back to FallbackSerializer), see https://jira.unity3d.com/browse/UUM-149592")]
#endif
public IEnumerator DeferredDespawning()
{
// Setup for test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public NetworkClientAndPlayerObjectTests(HostOrServer hostOrServer) : base(hostO
protected override IEnumerator OnTearDown()
{
m_PlayerPrefabs.Clear();
m_ChangedPlayerPrefabs.Clear();
return base.OnTearDown();
}

Expand All @@ -47,7 +48,7 @@ protected override void OnNewClientCreated(NetworkManager networkManager)
{
if (m_DistributedAuthority)
{
networkManager.OnFetchLocalPlayerPrefabToSpawn = FetchPlayerPrefabToSpawn;
networkManager.OnFetchLocalPlayerPrefabToSpawn = () => FetchPlayerPrefabToSpawn(networkManager);
}
base.OnNewClientCreated(networkManager);
}
Expand All @@ -56,11 +57,10 @@ protected override void OnNewClientCreated(NetworkManager networkManager)
/// Only for distributed authority mode
/// </summary>
/// <returns>a unique player prefab for the player</returns>
private GameObject FetchPlayerPrefabToSpawn()
private GameObject FetchPlayerPrefabToSpawn(NetworkManager networkManager)
{
var prefabObject = GetRandomPlayerPrefab();
var clientId = m_ClientNetworkManagers[m_ClientNetworkManagers.Length - 1].LocalClientId;
m_ChangedPlayerPrefabs.Add(clientId, prefabObject.GlobalObjectIdHash);
m_ChangedPlayerPrefabs.Add(networkManager.LocalClientId, prefabObject.GlobalObjectIdHash);
return prefabObject.gameObject;
}

Expand Down Expand Up @@ -155,9 +155,6 @@ private bool AllNetworkClientsValidated()
/// Validates the same thing when a client late joins and when a client disconnects.
/// </summary>
[UnityTest]
#if ENABLE_CORECLR
[Explicit("ValidateNetworkClients throws duplicate-key ArgumentException on CoreCLR (DAHost), see https://jira.unity3d.com/browse/UUM-149595")]
#endif
public IEnumerator ValidateNetworkClients()
{
// Validate the initial clients created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,6 @@ protected override void OnNewClientCreated(NetworkManager networkManager)
}

[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO NetworkVariable serialization codegen not generated for some types on CoreCLR (falls back to FallbackSerializer), see https://jira.unity3d.com/browse/UUM-149592")]
#endif
public IEnumerator SwitchTransportTest()
{
var authority = GetAuthorityNetworkManager();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
using System.Collections;
using System.Text;
#if ENABLE_CORECLR
using NUnit.Framework;
#endif
using Unity.Netcode;
using Unity.Netcode.TestHelpers.Runtime;
using UnityEngine;
Expand Down Expand Up @@ -176,9 +173,6 @@ private bool ValidateAllAreEqual(StringBuilder errorLog)
}

[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO NetworkVariable serialization codegen not generated for some types on CoreCLR (falls back to FallbackSerializer), see https://jira.unity3d.com/browse/UUM-149592")]
#endif
public IEnumerator TestHealthCode()
{
var authority = GetAuthorityNetworkManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public void NotMeRpc()

[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.DAHost)]
[UnityCoreClrExplicitDisabled("https://jira.unity3d.com/browse/UUM-149591", "NGO multi-instance test sessions fail to start/connect or time out on CoreCLR")]
internal class MessageReceiveAllocationTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ protected override void OnNewClientCreated(NetworkManager networkManager)
}

[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO multi-instance test sessions fail to start/connect or time out on CoreCLR, see https://jira.unity3d.com/browse/UUM-149591")]
#endif
public IEnumerator NetworkObjectDeserializationFailure()
{
m_CurrentLogLevel = LogLevel.Nothing;
Expand Down Expand Up @@ -280,9 +277,6 @@ private void ValidateNetworkBehaviourWithNetworkVariables(NetworkObject authorit
/// will still be initialized properly
/// </summary>
[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO multi-instance test sessions fail to start/connect or time out on CoreCLR, see https://jira.unity3d.com/browse/UUM-149591")]
#endif
public IEnumerator NetworkBehaviourSynchronization()
{
var authority = GetAuthorityNetworkManager();
Expand Down Expand Up @@ -325,9 +319,6 @@ public IEnumerator NetworkBehaviourSynchronization()
/// A basic validation for the NetworkBehaviour.OnSynchronize method
/// </summary>
[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO multi-instance test sessions fail to start/connect or time out on CoreCLR, see https://jira.unity3d.com/browse/UUM-149591")]
#endif
public IEnumerator NetworkBehaviourOnSynchronize()
{
var authority = GetAuthorityNetworkManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,6 @@ void TestForceUpdateCallback(NetworkListEvent<int> _)

// don't extend this please
[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO NetworkVariable serialization codegen not generated for some types on CoreCLR (falls back to FallbackSerializer), see https://jira.unity3d.com/browse/UUM-149592")]
#endif
public IEnumerator LegacyPredicateTesting()
{
var authority = GetAuthorityNetworkManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ public void SetReanticipateValueRpc(float f)
}
}

#if ENABLE_CORECLR
[Explicit("NGO multi-instance test sessions fail to start/connect or time out on CoreCLR, see https://jira.unity3d.com/browse/UUM-149591")]
#endif
internal class NetworkVariableAnticipationTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ namespace Unity.Netcode.RuntimeTests
[TestFixture(HostOrServer.Host, CollectionTypes.Dictionary)]
[TestFixture(HostOrServer.Server, CollectionTypes.List)]
[TestFixture(HostOrServer.Server, CollectionTypes.Dictionary)]
#if ENABLE_CORECLR
[Explicit("NGO NetworkVariable serialization codegen not generated for some types on CoreCLR (falls back to FallbackSerializer), see https://jira.unity3d.com/browse/UUM-149592")]
#endif
internal class NetworkVariableCollectionsChangingTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ namespace Unity.Netcode.RuntimeTests
/// </summary>
[TestFixture(HostOrServer.Host)]
[TestFixture(HostOrServer.Server)]
#if ENABLE_CORECLR
[Explicit("NGO multi-instance test sessions fail to start/connect or time out on CoreCLR, see https://jira.unity3d.com/browse/UUM-149591")]
#endif
internal class NetworkVariableCollectionsTests : NetcodeIntegrationTest
{
protected override int NumberOfClients => 2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,6 @@ private bool ChangedValueMatches(StringBuilder errorLog)
/// instances invoke <see cref="NetworkBehaviour.OnNetworkSpawn"/>.
/// </summary>
[UnityTest]
#if ENABLE_CORECLR
[Explicit("NGO NetworkVariable serialization codegen not generated for some types on CoreCLR (falls back to FallbackSerializer), see https://jira.unity3d.com/browse/UUM-149592")]
#endif
public IEnumerator ApplyValueDuringSpawnSequence()
{
var authority = GetAuthorityNetworkManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
namespace Unity.Netcode.RuntimeTests
{
[TestFixtureSource(nameof(TestDataSource))]
#if ENABLE_CORECLR
[Explicit("NGO NetworkVariable serialization codegen not generated for some types on CoreCLR (falls back to FallbackSerializer), see https://jira.unity3d.com/browse/UUM-149592")]
#endif
internal class NetworkVariablePermissionTests : NetcodeIntegrationTest
{
public static IEnumerable<TestFixtureData> TestDataSource()
Expand Down
Loading
Loading