private static void ValidateInternal(object obj, object contextObject, bool recursive, ref List <IValidationError> validationErrors, HashSet <object> validatedObjects = null) { if (obj == null) { return; } if (validatedObjects != null) { if (validatedObjects.Contains(obj)) { return; } validatedObjects.Add(obj); } Type objectType = obj.GetType(); var whitelistedNamespaces = ValidatorWhitelistedNamespaceProvider.GetWhitelistedNamespaces(); // if whitelisted asset exists - use whitelisting instead of blacklisting if (whitelistedNamespaces.Count > 0) { int ignoredNamespacesCount = ValidatorIgnoredNamespaceProvider.GetIgnoredNamespaces().Count; if (ignoredNamespacesCount > 0) { Debug.LogWarning("Both ValidatorIgnoredNamespace + ValidatorWhitelistedNamespace exist in project (mutually exclusive) - will only use whitelisted!"); } if (string.IsNullOrEmpty(objectType.Namespace)) { // No namespace means no validation in whitelist format return; } foreach (var whitelistedNamespace in whitelistedNamespaces) { if (!objectType.Namespace.Contains(whitelistedNamespace.Namespace)) { return; } } } else { if (!string.IsNullOrEmpty(objectType.Namespace)) { // allow user defined ignores for namespaces foreach (var validatorIgnoredNamespace in ValidatorIgnoredNamespaceProvider.GetIgnoredNamespaces()) { if (validatorIgnoredNamespace == null) { Debug.LogWarning("Bad state - validatorIgnoredNamespace is null!"); continue; } if (objectType.Namespace.Contains(validatorIgnoredNamespace.Namespace)) { return; } } } } foreach (FieldInfo fieldInfo in TypeUtil.GetInspectorFields(objectType) .Where(f => typeof(UnityEventBase).IsAssignableFrom(f.FieldType)) .Where(f => !Attribute.IsDefined(f, typeof(OptionalAttribute)) && !Attribute.IsDefined(f, typeof(HideInInspector)))) { // NOTE (darren): check UnityEvents for all classes UnityEventBase unityEvent = (UnityEventBase)fieldInfo.GetValue(obj); if (unityEvent == null) { Debug.LogError("Unexpected null UnityEvent in GameObjectValidator!"); continue; } for (int i = 0; i < unityEvent.GetPersistentEventCount(); i++) { MethodInfo methodInfo = unityEvent.GetMethodInfoForIndex(i); if (methodInfo == null) { validationErrors = validationErrors ?? new List <IValidationError>(); validationErrors.Add(ValidationErrorFactory.Create(obj, objectType, fieldInfo, contextObject)); break; } } } bool whitelisted = ValidatorUnityWhitelist.IsTypeWhitelisted(objectType); if (kUnityAssemblies.Contains(objectType.Assembly) && !whitelisted) { return; } IEnumerable <MemberInfo> membersToCheck = null; if (whitelisted) { membersToCheck = ValidatorUnityWhitelist.GetWhitelistedMembersFor(objectType); } else { membersToCheck = TypeUtil.GetInspectorFields(objectType) .Where(f => !ValidatorBlacklistedClassProvider.GetBlacklistedClasses().Any(b => b.Class == f.DeclaringType.Name)) .Where(f => !Attribute.IsDefined(f, typeof(OptionalAttribute)) && !Attribute.IsDefined(f, typeof(HideInInspector))) .Where(f => !kUnityAssemblies.Contains(f.DeclaringType.Assembly)).Cast <MemberInfo>(); // NOTE (darren): this is to ignore fields that declared in super-classes out of our control (Unity) } foreach (MemberInfo memberInfo in membersToCheck) { IEnumerable <Predicate <object> > predicates = ValidatorPredicates.GetOptionalPredicatesFor(memberInfo); if (predicates != null) { bool shouldValidate = predicates.All(p => p.Invoke(obj)); if (!shouldValidate) { continue; } } IList <UnityEngine.Object> unityEngineObjects = GetUnityEngineObjects(memberInfo, obj); // TODO (darren): don't alloc memory for List<> if not necessary if (unityEngineObjects == null || unityEngineObjects.Count <= 0) { // NOTE (darren): if this is not a UnityEngine.Object // we might still have to recursively look through its fields // which might contain UnityEngine.Objects if (recursive) { IList <object> memberObjects = GetMemberObjects(memberInfo, obj); foreach (object memberObj in memberObjects) { // NOTE (darren): the LocalId is broken here because we lost // a reference to the original GameObject being validated // (as contextObject may be the scene for example). // Leaving this as-is as it's an edge case and nice-to-have. ValidateInternal(memberObj, contextObject, recursive, ref validationErrors, validatedObjects); } } continue; } int index = 0; foreach (UnityEngine.Object memberObject in unityEngineObjects) { if (memberObject == null) { validationErrors = validationErrors ?? new List <IValidationError>(); if (unityEngineObjects.Count > 1) { validationErrors.Add(ValidationErrorFactory.Create(obj, objectType, memberInfo, contextObject, index)); } else { validationErrors.Add(ValidationErrorFactory.Create(obj, objectType, memberInfo, contextObject)); } index++; continue; } if (recursive) { GameObject memberObjectAsGameObject = memberObject as GameObject; if (memberObjectAsGameObject != null) { PrefabType prefabType = PrefabUtility.GetPrefabType(memberObjectAsGameObject); if (prefabType == PrefabType.Prefab) { // switch context to the prefab we just recursed to object newContextObject = memberObjectAsGameObject; validatedObjects = validatedObjects ?? new HashSet <object>() { obj }; ValidateGameObjectInternal(memberObjectAsGameObject, newContextObject, recursive, ref validationErrors, validatedObjects); } } ScriptableObject memberObjectAsScriptableObject = memberObject as ScriptableObject; if (memberObjectAsScriptableObject != null) { // switch context to the scriptable object we just recursed to object newContextObject = memberObjectAsScriptableObject; validatedObjects = validatedObjects ?? new HashSet <object>() { obj }; ValidateInternal(memberObjectAsScriptableObject, newContextObject, recursive, ref validationErrors, validatedObjects); } } index++; } } }
private static bool IsIgnored(object obj, object contextObject) { if (obj == null) { return(true); } // Skip user defined ignored asset paths: var ignoredAssetPaths = ValidatorIgnoredAssetPathProvider.GetIgnoredAssetPaths(); if (ignoredAssetPaths.Count > 0) { UnityEngine.Object unityContextObject = contextObject as UnityEngine.Object; if (unityContextObject != null) { string assetPath = AssetDatabase.GetAssetPath(unityContextObject); if (!string.IsNullOrEmpty(assetPath)) { if (ignoredAssetPaths.Any(ignoredAssetPath => assetPath.StartsWith(ignoredAssetPath.Path))) { return(true); } } } } Type objectType = obj.GetType(); var whitelistedNamespaces = ValidatorWhitelistedNamespaceProvider.GetWhitelistedNamespaces(); // if whitelisted asset exists - use whitelisting instead of blacklisting if (whitelistedNamespaces.Count > 0) { int ignoredNamespacesCount = ValidatorIgnoredNamespaceProvider.GetIgnoredNamespaces().Count; if (ignoredNamespacesCount > 0) { Debug.LogWarning("Both ValidatorIgnoredNamespace + ValidatorWhitelistedNamespace exist in project (mutually exclusive) - will only use whitelisted!"); } if (string.IsNullOrEmpty(objectType.Namespace)) { // No namespace means no validation in whitelist format return(true); } if (!whitelistedNamespaces.Any(whitelistedNamespace => objectType.Namespace.Contains(whitelistedNamespace.Namespace))) { // It is not allowed to skip basic UnityEngine objects, such as GameObject, because the validation // always starts with one of those. if (objectType.Namespace != typeof(GameObject).Namespace) { return(true); } } } else { if (!string.IsNullOrEmpty(objectType.Namespace)) { // allow user defined ignores for namespaces foreach (var validatorIgnoredNamespace in ValidatorIgnoredNamespaceProvider.GetIgnoredNamespaces()) { if (validatorIgnoredNamespace == null) { Debug.LogWarning("Bad state - validatorIgnoredNamespace is null!"); continue; } if (objectType.Namespace.Contains(validatorIgnoredNamespace.Namespace)) { // It is not allowed to skip basic UnityEngine objects, such as GameObject, because the // validation always starts with one of those. if (objectType.Namespace == typeof(GameObject).Namespace) { Debug.LogWarning($"Bad state - Cannot ignore namespace '{typeof(GameObject).Namespace}'"); continue; } else { return(true); } } } } } return(false); }