protected override IValue GetElementValueAt(IArrayValueRole <TValue> collection, int index, IValueFetchOptions options) { var elementReference = collection.GetElementReference(index); return(GetElementValue(elementReference, options)); }
private bool TryCopySerializedProperty(IObjectValueRole <TValue> serializedPropertyRole, IValueFetchOptions options, out IObjectValueRole <TValue> copiedSerializedPropertyRole) { copiedSerializedPropertyRole = null; // Get a copy of the property, so we can call Next(true) without updating the current instance var copyMethod = MetadataTypeLiteEx.LookupInstanceMethodSafe( mySerializedPropertyRole.ReifiedType.MetadataType, MethodSelectors.SerializedProperty_Copy, false); if (copyMethod == null) { myLogger.Warn("Cannot find Copy method on SerializedProperty"); return(false); } // CallInstanceMethod always returns not null (VoidValue if it fails) copiedSerializedPropertyRole = new SimpleValueReference <TValue>( serializedPropertyRole.CallInstanceMethod(copyMethod), mySerializedPropertyRole.ValueReference.OriginatingFrame, myValueServices.RoleFactory) .AsObjectSafe(options); if (copiedSerializedPropertyRole == null) { myLogger.Warn("Unable to Copy serializedProperty"); return(false); } return(true); }
private bool TryInvokeGetIterator(IObjectValueRole <TValue> serializedObjectRole, IValueFetchOptions options, out IObjectValueRole <TValue> returnedSerializedPropertyRole) { returnedSerializedPropertyRole = null; var method = MetadataTypeLiteEx.LookupInstanceMethodSafe(serializedObjectRole.ReifiedType.MetadataType, MethodSelectors.SerializedObject_GetIterator, false); if (method == null) { myLogger.Warn("Cannot find GetIterator method on SerializedObject"); return(false); } returnedSerializedPropertyRole = new SimpleValueReference <TValue>( serializedObjectRole.CallInstanceMethod(method), serializedObjectRole.ValueReference.OriginatingFrame, myValueServices.RoleFactory) .AsObjectSafe(options); if (returnedSerializedPropertyRole == null) { myLogger.Warn("Unable to invoke GetIterator"); return(false); } return(true); }
private IEnumerable <IValueEntity> SplitIntoChunks(TRole collection, int chunkStartIndex, int length, IValueFetchOptions options, CancellationToken token) { // Chunks of chunks var step = myChunkSize; var div = length / step; while (div > myChunkSize || div == myChunkSize && length % step > 0) { token.ThrowIfCancellationRequested(); step *= myChunkSize; div = length / step; } for (var i = 0; i < length; i += step) { token.ThrowIfCancellationRequested(); var startIndex = chunkStartIndex + i; var endIndex = Math.Min(chunkStartIndex + i + step, chunkStartIndex + length) - 1; var chunkLength = endIndex - startIndex + 1; var name = $"[{i}..{endIndex}]"; yield return(new SimpleEntityGroup(name, GetChunkedChildren(collection, startIndex, chunkLength, options, token))); } }
private string GetComponentName(IValueReference <TValue> componentValue, [CanBeNull] IReifiedType <TValue> objectNamesType, [CanBeNull] IMetadataMethodLite getInspectorTitleMethod, IStackFrame frame, IValueFetchOptions options, IValueServicesFacade <TValue> services) { if (objectNamesType != null && getInspectorTitleMethod != null) { try { var inspectorTitle = objectNamesType.CallStaticMethod(frame, options, getInspectorTitleMethod, componentValue.GetValue(options)); var stringValueRole = new SimpleValueReference <TValue>(inspectorTitle, frame, services.RoleFactory) .AsStringSafe(options); if (stringValueRole != null) { return(stringValueRole.GetString()); } } catch (Exception e) { myLogger.Error(e, "Unable to fetch object names for {0}", componentValue); } } return(componentValue.GetPrimaryRole(options).ReifiedType.MetadataType.ShortName); }
public virtual void SetValue(TValue value, IValueFetchOptions options) { if ((DefaultFlags & ValueFlags.IsReadOnly) != 0) { throw ValueErrors.ReadOnlyReference(); } myValueReferenceImplementation.SetValue(value, options); }
private IEnumerable <IValueEntity> GetChildrenLazy(TRole collection, int startIndex, int length, IValueFetchOptions options, CancellationToken token) { for (var i = 0; i < length; i++) { token.ThrowIfCancellationRequested(); yield return(GetElementValueAt(collection, startIndex + i, options)); } }
protected IEnumerable <IValueEntity> GetChunkedChildren(TRole collection, int startIndex, int length, IValueFetchOptions options, CancellationToken token) { if (length > myChunkSize) { return(SplitIntoChunks(collection, startIndex, length, options, token)); } // Get an enumerable of child values. Calculating this is expensive, so do it on demand inside an iterator. // When a chunk group is created, it receives an enumerable of values. If we're not careful, each chunk's // children are eagerly evaluated and the whole point of chunking is lost. // The default array children renderer uses an array for this enumerable, but populates it with lightweight // array element value references that calculate the actual value on demand, via ICachedValueReference. // If we hit problems with this simple lazy enumerable, we could adopt a similar approach. return(GetChildrenLazy(collection, startIndex, length, options, token)); }
public static string GetIntValueAsPrintableChar <TValue>(IValueReference <TValue> intValueReference, IValueFetchOptions options) where TValue : class { var primitiveValue = intValueReference.AsPrimitiveSafe(options)?.GetPrimitive(); if (primitiveValue != null) { var value = primitiveValue as long? ?? primitiveValue as int?; if (value < char.MaxValue) { return($"'{ToPrintableChar((char) value)}'"); } } return(null); }
private IEnumerable <IValueReference <TValue> > DecorateCharacterValue(IEnumerable <IValueReference <TValue> > references, IValueFetchOptions options) { foreach (var reference in references) { if (reference.DefaultName == "intValue" || reference.DefaultName == "longValue") { var extraDetail = SerializedPropertyHelper.GetIntValueAsPrintableChar(reference, options); if (extraDetail != null) { yield return(new ExtraDetailValueReferenceDecorator <TValue>(reference, ValueServices.RoleFactory, extraDetail)); continue; } } yield return(reference); } }
private IEnumerable <IValueEntity> GetChildrenImpl(IValueFetchOptions options) { if (!TryInvokeGetIterator(mySerializedObjectRole, options, out var serializedPropertyRole)) { yield break; } var name = serializedPropertyRole.GetInstancePropertyReference("name") ?.AsStringSafe(options)?.GetString() ?? "Child"; // Tell the value presenter to hide the name field, as we're using it for the key name. Also hide the // type presentation - of course it's a SerializedProperty yield return(new CalculatedValueReferenceDecorator <TValue>(serializedPropertyRole.ValueReference, myValueServices.RoleFactory, name, false, false).ToValue(myValueServices)); // Technically, we should now repeatedly call Copy() and Next(false) until Next returns false so that we // show all child properties of the SerializedObject. But empirically, there is only one direct child of // SerializedObject, called "Base", with a depth of -1. We'll avoid the unnecessary method invocations, // unless it turns out to be an actual issue. }
public static string GetThrownExceptionMessage <TValue>(EvaluatorExceptionThrownException <TValue> exception, IStackFrame frame, IValueServicesFacade <TValue> valueServices, IValueFetchOptions valueFetchOptions, ILogger logger) where TValue : class { try { var reference = new SimpleValueReference <TValue>(exception.Exception, frame, valueServices.RoleFactory); return(reference.AsObjectSafe(valueFetchOptions) ?.GetInstancePropertyReference("Message", true) ?.AsStringSafe(valueFetchOptions)?.GetString()); } catch (Exception e) { // Argh! Exception thrown while trying to get information about a thrown exception! logger.LogExceptionSilently(e); return(null); } }
private bool TryInvokeNext(IObjectValueRole <TValue> serializedPropertyRole, IMetadataMethodLite nextMethod, TValue boolArg, IValueFetchOptions options, out bool returnValue) { returnValue = false; var returnValueRole = new SimpleValueReference <TValue>( serializedPropertyRole.CallInstanceMethod(nextMethod, boolArg), mySerializedPropertyRole.ValueReference.OriginatingFrame, myValueServices.RoleFactory) .AsPrimitiveSafe(options); if (returnValueRole == null) { myLogger.Warn("Unable to call Next on serializedProperty"); return(false); } returnValue = returnValueRole.GetPrimitive <bool>(); return(true); }
private string GetComponentName(IValueReference <TValue> componentValue, [CanBeNull] IReifiedType <TValue> objectNamesType, [CanBeNull] IMetadataMethodLite getInspectorTitleMethod, IStackFrame frame, IValueFetchOptions options, IValueServicesFacade <TValue> services, out bool isNameFromValue) { if (objectNamesType != null && getInspectorTitleMethod != null) { try { var inspectorTitle = objectNamesType.CallStaticMethod(frame, options, getInspectorTitleMethod, componentValue.GetValue(options)); var stringValueRole = new SimpleValueReference <TValue>(inspectorTitle, frame, services.RoleFactory) .AsStringSafe(options); if (stringValueRole != null) { isNameFromValue = true; return(stringValueRole.GetString()); } } catch (EvaluatorAbortedException e) { myLogger.LogExceptionSilently(e); } catch (Exception e) { myLogger.Warn(e, ExceptionOrigin.Algorithmic, $"Unable to fetch object names for {componentValue}"); } } isNameFromValue = false; return(componentValue.GetPrimaryRole(options).ReifiedType.MetadataType.ShortName); }
public TValue GetValue(IValueFetchOptions options) => myOriginalReference.GetValue(options);
private IValue GetElementValue(IValueReference <TValue> elementReference, IValueFetchOptions options) { var elementRole = elementReference.AsObjectSafe(options); if (elementRole == null) { return(null); } var isNameFromValue = true; var name = elementRole.GetInstancePropertyReference("name", true)?.AsStringSafe(options) ?.GetString(); if (name == null) { name = "Game Object"; isNameFromValue = false; } // Tell the value presenter to hide the name field, if we're using it for the key. Also hide the default // type presentation - we know it's a GameObject, it's under a group called "Game Objects" return(new CalculatedValueReferenceDecorator <TValue>(elementRole.ValueReference, myValueServices.RoleFactory, name, !isNameFromValue, false).ToValue(myValueServices)); }
protected override IValue GetElementValueAt(IObjectValueRole <TValue> collection, int index, IValueFetchOptions options) { try { var frame = myGameObjectRole.ValueReference.OriginatingFrame; var indexValue = myValueServices.ValueFactory.CreatePrimitive(frame, options, index); var childTransformValue = collection.CallInstanceMethod(myGetChildMethod, indexValue); var childTransform = new SimpleValueReference <TValue>(childTransformValue, frame, myValueServices.RoleFactory).AsObjectSafe(options); var gameObject = childTransform?.GetInstancePropertyReference("gameObject", true) ?.AsObjectSafe(options); if (gameObject == null) { return(new ErrorValue("Game Object", "Unable to find child gameObject, or value is null")); } var name = gameObject.GetInstancePropertyReference("name", true)?.AsStringSafe(options) ?.GetString() ?? "Game Object"; // Tell the value presenter to not show the name field, we're already showing it as the key. Also don't // show the type - a GameObject's child can only be a GameObject return(new CalculatedValueReferenceDecorator <TValue>(gameObject.ValueReference, myValueServices.RoleFactory, name, false, false).ToValue(myValueServices)); } catch (Exception e) { // We must always return a value, as we're effectively showing the contents of an array here. We're // possibly also being evaluated lazily, thanks to chunked arrays, so can't rely on the caller // catching exceptions. myLogger.LogExceptionSilently(e); return(myValueServices.ValueRenderers.GetValueStubForException(e, "Game Object", collection.ValueReference.OriginatingFrame) as IValue ?? new ErrorValue("Game Object", "Unable to retrieve child game object")); } }
protected override IValue GetElementValueAt(IObjectValueRole <TValue> collection, int index, IValueFetchOptions options) { var name = $"[{index}]"; try { var frame = mySerializedPropertyRole.ValueReference.OriginatingFrame; var indexValue = myValueServices.ValueFactory.CreatePrimitive(frame, options, index); var childSerializedPropertyValue = collection.CallInstanceMethod(myGetElementMethod, indexValue); var valueReference = new SimpleValueReference <TValue>(childSerializedPropertyValue, mySerializedPropertyRole.ReifiedType.MetadataType, name, ValueOriginKind.ArrayElement, ValueFlags.None | ValueFlags.IsReadOnly, frame, myValueServices.RoleFactory); // Tell the value presenter to hide the name, because it's always "data" (DefaultName is the key name) // Also hide the type presentation - they can only ever be SerializedProperty instances return(new CalculatedValueReferenceDecorator <TValue>(valueReference, myValueServices.RoleFactory, valueReference.DefaultName, false, false).ToValue(myValueServices)); } catch (Exception e) { // We must always return a value, as we're effectively showing the contents of an array here. We're // possibly also being evaluated lazily, thanks to chunked arrays, so can't rely on the caller // catching exceptions. myLogger.LogExceptionSilently(e); return(myValueServices.ValueRenderers.GetValueStubForException(e, name, collection.ValueReference.OriginatingFrame) as IValue ?? new ErrorValue(name, "Unable to retrieve child serialized property")); } }
private IEnumerable <IValueEntity> GetChildrenImpl(IValueFetchOptions options, CancellationToken token) { // SerializedProperty is a view over a serialized stream. Calling Next() or GetEnumerator().MoveNext() // will update this view. We need to work with copies, so that the original value isn't updated if (!TryCopySerializedProperty(mySerializedPropertyRole, options, out var cursor)) { yield break; } // Get the depth of this "parent" property. We'll call Next() until children's depth changes if (!Util.TryEvaluatePrimitiveProperty(cursor, "depth", options, out int initialDepth)) { myLogger.Warn("Unable to evaluate initial depth on serializedProperty"); yield break; } var nextMethod = MetadataTypeLiteEx.LookupInstanceMethodSafe(cursor.ReifiedType.MetadataType, MethodSelectors.SerializedProperty_Next, false); if (nextMethod == null) { myLogger.Warn("Cannot find Next method on SerializedProperty"); yield break; } var trueValue = myValueServices.ValueFactory.CreatePrimitive( mySerializedPropertyRole.ValueReference.OriginatingFrame, options, true); var falseValue = myValueServices.ValueFactory.CreatePrimitive( mySerializedPropertyRole.ValueReference.OriginatingFrame, options, false); // Call cursor.Next(true). Our cursor is now a view over the first child if (!TryInvokeNext(cursor, nextMethod, trueValue, options, out var nextResult)) { yield break; } var count = 0; while (nextResult) { token.ThrowIfCancellationRequested(); if (!Util.TryEvaluatePrimitiveProperty(cursor, "depth", options, out int thisDepth)) { myLogger.Warn("Unable to evaluate initial depth on serializedProperty"); yield break; } // SerializedProperties are a view on a stream of serialised objects (not the C# objects!). Children // are simply the next node in the stream, but they have a depth set to current depth + 1. Iterating // children has finished when the next node's depth is set back to current depth. if (thisDepth != initialDepth + 1) { break; } // Yield a copy of the current instance if (!TryCopySerializedProperty(cursor, options, out var copiedSerializedPropertyRole)) { myLogger.Warn("Failed to copy current serialised property"); break; } var name = copiedSerializedPropertyRole.GetInstancePropertyReference("name") ?.AsStringSafe(options)?.GetString() ?? $"prop{count}"; // Tell the value presenter to hide the name field, because we're using it for the key name. Also // hide the default presentation. Of course it's a SerializedProperty, it's a child of a // SerializedProperty yield return(new CalculatedValueReferenceDecorator <TValue>( copiedSerializedPropertyRole.ValueReference, myValueServices.RoleFactory, name, false, false) .ToValue(myValueServices)); // MoveNext(false). cursor is now viewing either the next child or a sibling of the original // property, or is at the end of the stream (nextResult is false). If this evaluation fails, // we've already logged and nextResult is already false, so we don't need extra error handling TryInvokeNext(cursor, nextMethod, falseValue, options, out nextResult); count++; } }
protected override IValue GetElementValueAt(IObjectValueRole <TValue> collection, int index, IValueFetchOptions options) { var frame = myGameObjectRole.ValueReference.OriginatingFrame; var indexValue = myValueServices.ValueFactory.CreatePrimitive(frame, options, index); var childTransformValue = collection.CallInstanceMethod(myGetChildMethod, indexValue); var childTransform = new SimpleValueReference <TValue>(childTransformValue, frame, myValueServices.RoleFactory).AsObjectSafe(options); var gameObject = childTransform?.GetInstancePropertyReference("gameObject", true) ?.AsObjectSafe(options); var name = gameObject?.GetInstancePropertyReference("name", true)?.AsStringSafe(options) ?.GetString() ?? "Game Object"; return(new NamedReferenceDecorator <TValue>(gameObject.ValueReference, name, ValueOriginKind.Property, ValueFlags.None | ValueFlags.IsReadOnly, myGameObjectRole.ReifiedType.MetadataType, myValueServices.RoleFactory, true) .ToValue(myValueServices)); }
public virtual IValueRole GetPrimaryRole(IValueFetchOptions options) { // Get a role based on *this* reference, not the underlying reference. Otherwise, the returned role has the // wrong reference. return(myRoleFactory.GetPrimaryRole(this, options)); }
private IEnumerable <IValueEntity> GetChildrenImpl(IValueFetchOptions options) { var frame = myGameObjectRole.ValueReference.OriginatingFrame; var componentType = myValueServices.GetReifiedType(frame, "UnityEngine.Component, UnityEngine.CoreModule") ?? myValueServices.GetReifiedType(frame, "UnityEngine.Component, UnityEngine"); if (componentType == null) { myLogger.Warn("Unable to find UnityEngine.Component"); yield break; } var getComponentsMethod = myGameObjectRole.ReifiedType.MetadataType.GetMethods() .FirstOrDefault(ourGetComponentsSelector); if (getComponentsMethod == null) { myLogger.Warn("Unable to find UnityEngine.GameObject.GetComponents method"); yield break; } // Call Component[] GameObject.GetComponents(typeof(Component)) var typeObject = (IValueReference <TValue>)componentType.GetTypeObject(frame); var componentsArray = myGameObjectRole.CallInstanceMethod(getComponentsMethod, typeObject.GetValue(options)); var componentArray = new SimpleValueReference <TValue>(componentsArray, frame, myValueServices.RoleFactory) .GetExactPrimaryRoleSafe <TValue, IArrayValueRole <TValue> >(options); if (componentArray == null) { myLogger.Warn("Cannot get return value of GameObject.GetComponents or method returned null"); yield break; } // string UnityEditor.ObjectNames.GetInspectorTitle(UnityEngine.Object) // Returns the name of the component, formatted the same as in the Inspector. Values are also cached per // type. This obviously won't be available for standalone players, where we'll display the short type // name instead. // TODO: Support extra fallback names // Unity doesn't use the short name, but will look at the type and use GameObject.name, // MonoBehaviour.GetScriptClassName and so on. var objectNamesType = myValueServices.GetReifiedType(frame, "UnityEditor.ObjectNames, UnityEditor") ?? myValueServices.GetReifiedType(frame, "UnityEditor.ObjectNames, UnityEditor.CoreModule"); var getInspectorTitleMethod = objectNamesType?.MetadataType.GetMethods() .FirstOrDefault(ourGetInspectorTitleSelector); var childReferencesEnumerator = (IChildReferencesEnumerator <TValue>)componentArray; foreach (var componentReference in childReferencesEnumerator.GetChildReferences()) { var componentName = GetComponentName(componentReference, objectNamesType, getInspectorTitleMethod, frame, options, myValueServices, out var isNameFromValue); // Tell the value presenter to hide the name field, if we're using it for the key. Also hide the // default type presentation - we know it's a Component, it's under a group called "Components" yield return(new CalculatedValueReferenceDecorator <TValue>(componentReference, myValueServices.RoleFactory, componentName, !isNameFromValue, false).ToValue(myValueServices)); } }
public virtual TValue GetValue(IValueFetchOptions options) { return(myValueReferenceImplementation.GetValue(options)); }
protected abstract IValue GetElementValueAt(TRole collection, int index, IValueFetchOptions options);
protected override IValue GetElementValueAt(IObjectValueRole <TValue> collection, int index, IValueFetchOptions options) { var frame = mySerializedPropertyRole.ValueReference.OriginatingFrame; var indexValue = myValueServices.ValueFactory.CreatePrimitive(frame, options, index); var childSerializedPropertyValue = collection.CallInstanceMethod(myGetElementMethod, indexValue); var valueReference = new SimpleValueReference <TValue>(childSerializedPropertyValue, mySerializedPropertyRole.ReifiedType.MetadataType, $"[{index}]", ValueOriginKind.ArrayElement, ValueFlags.None | ValueFlags.IsReadOnly, frame, myValueServices.RoleFactory); // Tell the value presenter to hide the name, because it's always "data" (DefaultName is the key name) // Also hide the type presentation - they can only ever be SerializedProperty instances return(new CalculatedValueReferenceDecorator <TValue>(valueReference, myValueServices.RoleFactory, valueReference.DefaultName, false, false).ToValue(myValueServices)); }
public virtual void SetValue(TValue value, IValueFetchOptions options) { myValueReferenceImplementation.SetValue(value, options); }
public void SetValue(TValue value, IValueFetchOptions options) => throw ValueErrors.ReadOnlyReference();
private IValue GetElementValue(IValueReference <TValue> elementReference, IValueFetchOptions options) { var elementRole = elementReference.AsObjectSafe(options); if (elementRole == null) { return(null); } var isNameFromValue = true; var name = elementRole.GetInstancePropertyReference("name", true)?.AsStringSafe(options) ?.GetString(); if (name == null) { name = "Game Object"; isNameFromValue = false; } return(new NamedReferenceDecorator <TValue>(elementRole.ValueReference, name, ValueOriginKind.Property, ValueFlags.None | ValueFlags.IsReadOnly, elementRole.ReifiedType.MetadataType, myValueServices.RoleFactory, isNameFromValue) .ToValue(myValueServices)); }
public IValueRole GetPrimaryRole(IValueFetchOptions options) { return(myRoleFactory.GetPrimaryRole(this, options)); }
protected override IValue GetElementValueAt(IObjectValueRole <TValue> collection, int index, IValueFetchOptions options) { var frame = myGameObjectRole.ValueReference.OriginatingFrame; var indexValue = myValueServices.ValueFactory.CreatePrimitive(frame, options, index); var childTransformValue = collection.CallInstanceMethod(myGetChildMethod, indexValue); var childTransform = new SimpleValueReference <TValue>(childTransformValue, frame, myValueServices.RoleFactory).AsObjectSafe(options); var gameObject = childTransform?.GetInstancePropertyReference("gameObject", true) ?.AsObjectSafe(options); if (gameObject == null) { return(new ErrorValue("Game Object", "Unable to retrieve child game object")); } var name = gameObject.GetInstancePropertyReference("name", true)?.AsStringSafe(options) ?.GetString() ?? "Game Object"; // Tell the value presenter to not show the name field, we're already showing it as the key. Also don't // show the type - a GameObject's child can only be a GameObject return(new CalculatedValueReferenceDecorator <TValue>(gameObject.ValueReference, myValueServices.RoleFactory, name, false, false).ToValue(myValueServices)); }