Ejemplo n.º 1
0
        private FieldInfoData CreateDescriptionsDataWithFieldDescriptionsHierarchy()
        {
            var expectedRoot = new ContainerNode("Node", "Node", ContainerNodeRole.None);

            var fieldDescription = new MockedFieldInfo()
            {
                Name = "PropertyInDimension"
            };
            var fieldDescription2 = new MockedFieldInfo()
            {
                Name = "StandAloneProperty"
            };

            var dimensionInfo           = new ContainerNode("Dimension1", "Dimension1", ContainerNodeRole.Dimension);
            var propertyInDimensionInfo = new FieldInfoNode(fieldDescription);

            dimensionInfo.Children.Add(propertyInDimensionInfo);

            var standAlonePropertyInfo = new FieldInfoNode(fieldDescription2);

            expectedRoot.Children.Add(dimensionInfo);
            expectedRoot.Children.Add(standAlonePropertyInfo);

            var descriptionsData = new FieldInfoData(expectedRoot);

            return(descriptionsData);
        }
Ejemplo n.º 2
0
        public void Constructor_AssignsRootFieldInfo()
        {
            var expectedRoot     = new ContainerNode("Node", ContainerNodeRole.None);
            var descriptionsData = new FieldInfoData(expectedRoot);

            var actualRoot = descriptionsData.RootFieldInfo;

            Assert.AreSame(expectedRoot, actualRoot);
        }
        public void Constructor_AssignsDescriptionsDataProperty()
        {
            var expectedDescriptionsData = new FieldInfoData(new ContainerNode("", ContainerNodeRole.Dimension));
            var args = new GetDescriptionsDataCompletedEventArgs(null, null, expectedDescriptionsData);

            var actualDescriptionsData = args.DescriptionsData;

            Assert.AreSame(expectedDescriptionsData, actualDescriptionsData);
        }
Ejemplo n.º 4
0
        public void GetDescriptionsDataAsyncCompletedEventArgs_ContainsTheResultReturnedByGenerateDescriptionsData()
        {
            var expectedDescriptionsData = new FieldInfoData(new ContainerNode("TestName", ContainerNodeRole.Other));

            this.provider.ActionToExecuteAsync = () => expectedDescriptionsData;
            this.provider.GetDescriptionsDataAsync();

            var actualDescriptionsData = this.latestCompletedEventArgs.DescriptionsData;

            Assert.AreSame(expectedDescriptionsData, actualDescriptionsData);
        }
Ejemplo n.º 5
0
        public void Constructor_WhenNullRootIsPassed_ThrowsArgumentNullException()
        {
            try
            {
                var descriptionsData = new FieldInfoData(null);

                Assert.Fail("ArgumentNullException expected.");
            }
            catch (ArgumentNullException)
            {
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Gets the value of this <see cref="FieldInfo"/>. Uses caching with <see cref="CreateGetter(FieldInfo)"/>
        /// </summary>
        public static T GetValue <T>(this FieldInfo field, object obj)
        {
            if (!typeof(T).IsAssignableFrom(field.FieldType) && !typeof(T).IsSubclassOf(field.FieldType))
            {
                throw new InvalidCastException("Cannot cast from " + field.FieldType + " to " + typeof(T));
            }
            FieldInfoData fieldInfoData = new FieldInfoData(field.DeclaringType, field.Name);

            if (!cachedGetters.ContainsKey(fieldInfoData))
            {
                cachedGetters.Add(fieldInfoData, CreateGetter(field));
            }
            return(cachedGetters[fieldInfoData].DynamicInvoke(obj) is T value ? value : default);
Ejemplo n.º 7
0
        bool InjectFieldInfo()
        {
            var getFieldInfoFn            = AssemblyDefinition.MainModule.ImportReference(typeof(TypeManager).GetMethod("GetFieldInfo"));
            var getFieldInfoFnGenericName = getFieldInfoFn.Resolve().FullName;

            var fieldNameInstructionMap = new Dictionary <FieldInfoData, List <(ILProcessor, Instruction)> >(new FieldInfoDataComparer());

            // scan the assembly for calls to FieldInfo's constructor/implicit constructor and
            // replace the call with our own
            foreach (var type in AssemblyDefinition.MainModule.GetAllTypes())
            {
                bool shouldScan = type.CustomAttributes.Any(ca => ca.AttributeType.Name == nameof(GenerateFieldInfoAttribute) && ca.AttributeType.Namespace == "Unity.Entities");
                foreach (var method in type.GetMethods())
                {
                    if (method.Body == null)
                    {
                        continue;
                    }

                    if (!shouldScan)
                    {
                        shouldScan = method.CustomAttributes.Any(ca => ca.AttributeType.Name == nameof(GenerateFieldInfoAttribute) && ca.AttributeType.Namespace == "Unity.Entities");
                    }

                    if (!shouldScan)
                    {
                        continue;
                    }

                    foreach (var instruction in method.Body.Instructions)
                    {
                        if (instruction.OpCode == OpCodes.Call)
                        {
                            var methodFn = instruction.Operand as MethodReference;
                            if (methodFn.Resolve()?.FullName == getFieldInfoFnGenericName)
                            {
                                // We know this call only takes a ldstr op as a parameter, if it isn't then error
                                var ldStringOp = instruction.Previous;
                                if (ldStringOp.OpCode != OpCodes.Ldstr)
                                {
                                    throw new ArgumentException($"{method.FullName} is constructing a {typeof(TypeManager.FieldInfo).FullName} but has not provided a string literal as the constructor argument.");
                                }

                                var fieldInfoData = new FieldInfoData();
                                fieldInfoData.BaseType  = (methodFn as GenericInstanceMethod).GenericArguments[0];
                                fieldInfoData.FieldPath = ldStringOp.Operand as string;
                                if (!fieldNameInstructionMap.ContainsKey(fieldInfoData))
                                {
                                    fieldNameInstructionMap.Add(fieldInfoData, new List <(ILProcessor, Instruction)>());
                                }

                                // Fetch the ilprocessor, and cache the ldstr instruction and ilprocessor -- we will
                                // use them later to replace the ldstr and call ops with a load to a generated FieldInfo
                                var processor = method.Body.GetILProcessor();
                                fieldNameInstructionMap[fieldInfoData].Add((processor, ldStringOp));
                            }
                        }
                    }
                }
            }

            if (fieldNameInstructionMap.Count == 0)
            {
                return(false);
            }

            var fieldInfoRef = AssemblyDefinition.MainModule.ImportReference(typeof(TypeManager.FieldInfo));
            var fieldInfoBaseTypeFieldRef    = AssemblyDefinition.MainModule.ImportReference(typeof(TypeManager.FieldInfo).GetField(nameof(TypeManager.FieldInfo.BaseType)));
            var fieldInfoFieldTypeFieldRef   = AssemblyDefinition.MainModule.ImportReference(typeof(TypeManager.FieldInfo).GetField(nameof(TypeManager.FieldInfo.FieldType)));
            var fieldInfoFieldOffsetFieldRef = AssemblyDefinition.MainModule.ImportReference(typeof(TypeManager.FieldInfo).GetField(nameof(TypeManager.FieldInfo.FieldOffset)));

            // Adds "static public class CodeGeneratedFieldInfoRegistry" type
            var fieldInfoRegistry = new TypeDefinition("Unity.Entities.CodeGeneratedRegistry", "FieldInfoRegistry", TypeAttributes.Class | TypeAttributes.Public, AssemblyDefinition.MainModule.ImportReference(typeof(object)));

            AssemblyDefinition.MainModule.Types.Add(fieldInfoRegistry);

            // Declares: "static CodeGeneratedFieldInfoRegistry() { }" (i.e. the static ctor / .cctor)
            var fieldInfoRegistryCctorDef = new MethodDefinition(".cctor", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, AssemblyDefinition.MainModule.ImportReference(typeof(void)));

            fieldInfoRegistry.Methods.Add(fieldInfoRegistryCctorDef);
            fieldInfoRegistry.IsBeforeFieldInit       = true;
            fieldInfoRegistryCctorDef.Body.InitLocals = true;
            var scratchFieldInfo = new VariableDefinition(fieldInfoRef);

            fieldInfoRegistryCctorDef.Body.Variables.Add(scratchFieldInfo);

            // We now know what fields we need to provide FieldInfo for, and where we need to inject
            // so now lets produce the FieldInfo for the given strings, generate a static FieldInfo for each unique
            // FieldInfo and then replace the ldstr+call instructions with a nop+ldsfld to our new static Field info
            int i  = 0;
            var il = fieldInfoRegistryCctorDef.Body.GetILProcessor();

            foreach (var fieldInfoData in fieldNameInstructionMap.Keys)
            {
                // Determine field offset, field type etc...
                fieldInfoData.FieldOffset = TypeUtils.GetFieldOffsetByFieldPath(fieldInfoData.FieldPath, fieldInfoData.BaseType, kArchBits, out fieldInfoData.FieldType);

                if (fieldInfoData.FieldType == null)
                {
                    throw new ArgumentException($"Could not find the field '{fieldInfoData.FieldPath}' within the type '{fieldInfoData.BaseType.FullName}'. Please double check your TypeManager.GetFieldInfo() field path argument");
                }

                // Create a new FieldInfo field in our registry
                var fieldInfoField = new FieldDefinition("FieldInfo" + i++, Mono.Cecil.FieldAttributes.Static | Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.InitOnly, fieldInfoRef);
                fieldInfoRegistry.Fields.Add(fieldInfoField);

                // Initialize the new field in our cctor

                // BaseType = typeof(fieldInfoData.BaseType)
                il.Emit(OpCodes.Ldloca, scratchFieldInfo);
                il.Emit(OpCodes.Ldtoken, AssemblyDefinition.MainModule.ImportReference(fieldInfoData.BaseType));
                il.Emit(OpCodes.Call, m_GetTypeFromHandleFnRef);
                il.Emit(OpCodes.Stfld, fieldInfoBaseTypeFieldRef);

                // FieldType = typeof(fieldInfoData.FieldType)
                il.Emit(OpCodes.Ldloca, scratchFieldInfo);
                il.Emit(OpCodes.Ldtoken, AssemblyDefinition.MainModule.ImportReference(fieldInfoData.FieldType));
                il.Emit(OpCodes.Call, m_GetTypeFromHandleFnRef);
                il.Emit(OpCodes.Stfld, fieldInfoFieldTypeFieldRef);

                // FieldOffset = fieldInfoData.FieldOffset
                il.Emit(OpCodes.Ldloca, scratchFieldInfo);
                il.Emit(OpCodes.Ldc_I4, fieldInfoData.FieldOffset);
                il.Emit(OpCodes.Stfld, fieldInfoFieldOffsetFieldRef);

                // Store top of stack to static field
                il.Emit(OpCodes.Ldloc, scratchFieldInfo);
                il.Emit(OpCodes.Stsfld, fieldInfoField);

                // Now replace ldstr with a nop and call to a load from our new field
                foreach (var(methodProcessor, ldstrOp) in fieldNameInstructionMap[fieldInfoData])
                {
                    var module = methodProcessor.Body.Method.Module;
                    var callOp = ldstrOp.Next;

                    methodProcessor.Replace(callOp, Instruction.Create(OpCodes.Nop));
                    methodProcessor.Replace(ldstrOp, Instruction.Create(OpCodes.Ldsfld, module.ImportReference(fieldInfoField)));
                }
            }

            il.Emit(OpCodes.Ret);

            return(true);
        }