public static bool IsPodType(this TypeReference typeRef) { TypeDefinition type = typeRef.Resolve(); if (type.IsCppBasicType() || type.IsEnum) { return(true); } var typeResolver = TypeResolver.For(typeRef); foreach (var f in type.Fields) { var fieldType = typeResolver.Resolve(f.FieldType); if (fieldType.MetadataType == MetadataType.String || fieldType.IsDynamicArray()) { return(false); } bool recursiveIsPodType = IsPodType(fieldType); if (!recursiveIsPodType) { return(false); } } return(true); }
public static bool IsManagedType(this TypeReference typeRef) { // We must check this before calling Resolve() as cecil loses this property otherwise if (typeRef.IsPointer) { return(false); } if (typeRef.IsArray || typeRef.IsGenericParameter) { return(true); } var type = typeRef.Resolve(); if (type.IsDynamicArray()) { return(true); } TypeDefinition fixedSpecialType = type.FixedSpecialType(); if (fixedSpecialType != null) { if (fixedSpecialType.MetadataType == MetadataType.String) { return(true); } return(false); } if (type.IsEnum) { return(false); } if (type.IsValueType) { // if none of the above check the type's fields var typeResolver = TypeResolver.For(typeRef); foreach (var field in type.Fields) { if (field.IsStatic) { continue; } var fieldType = typeResolver.Resolve(field.FieldType); if (fieldType.IsManagedType()) { return(true); } } return(false); } return(true); }
private static ulong HashType(TypeReference typeRef, Dictionary <TypeReference, ulong> cache) { var hash = HashTypeName(typeRef); var typeResolver = TypeResolver.For(typeRef); var typeDef = typeRef.Resolve(); // UnityEngine objects have their own serialization mechanism so exclude hashing the type's // internals and just hash its name which is stable and important to how Entities will serialize if (typeRef.IsArray || typeRef.IsGenericParameter || typeRef.IsPointer || typeDef.IsPrimitive || typeDef.IsEnum || typeDef.IsUnityEngineObject() || WorkaroundTypeNames.Contains(typeRef.FullName)) { return(hash); } foreach (var field in typeDef.Fields) { if (!field.IsStatic) // statics have no effect on data layout { var fieldTypeRef = typeResolver.ResolveFieldType(field); // Classes can have cyclical type definitions so prevent recursion if we've seen the type already if (!cache.TryGetValue(fieldTypeRef, out ulong fieldTypeHash)) { // Classes can have cyclical type definitions so to prevent a potential stackoverflow // we make all future occurence of fieldType resolve to the hash of its field type name cache.Add(fieldTypeRef, HashTypeName(fieldTypeRef)); fieldTypeHash = HashType(fieldTypeRef, cache); cache[fieldTypeRef] = fieldTypeHash; } if (field.HasLayoutInfo) { var offset = field.Offset; hash = CombineFNV1A64(hash, (ulong)offset); } hash = CombineFNV1A64(hash, fieldTypeHash); } } // TODO: Enable this. Currently IL2CPP gives totally inconsistent results to Mono. /* * if (typeDef.HasLayoutInfo) * { * var explicitSize = typeDef.ClassSize; * if (explicitSize > 0) * hash = CombineFNV1A64(hash, (ulong)explicitSize); * * // Todo: Enable this. We cannot support Pack at the moment since a type's Packing will * // change based on its field's explicit packing which will fail for Tiny mscorlib * // as it's not in sync with dotnet * //var packingSize = typeDef.PackingSize; * //if (packingSize > 0) * // hash = CombineFNV1A64(hash, (ulong)packingSize); * } */ return(hash); }
public static TypeReference GetResolvedDeclaringType(this FieldDefinition field, TypeReference type) { while (!type.Resolve().Fields.Contains(field)) { type = TypeResolver.For(type).Resolve(type.Resolve().BaseType); } return(type); }
public static TypeReference GetResolvedDeclaringType(this PropertyDefinition property, TypeReference type) { while (!type.Resolve().Properties.Contains(property)) { type = TypeResolver.For(type).Resolve(type.Resolve().BaseType); } return(type); }
FieldInfoLookUp GenerateFieldInfos(TypeReference typeRef) { var lookup = new FieldInfoLookUp() { Index = m_FieldGenInfos.Count(), Count = 0 }; if (!m_FieldInfoMap.ContainsKey(typeRef)) { var resolver = TypeResolver.For(typeRef); var typeDef = typeRef.Resolve(); // Push the component into the fieldInfoTypeList m_FieldTypes.Add(typeRef); foreach (var field in typeDef.Fields) { if (field.IsStatic) { continue; } var fieldType = resolver.ResolveFieldType(field); var sanitizedFieldName = SanitizeFieldName(field.Name); var fieldInfo = new FieldGenInfo { Offset = TypeUtils.GetFieldOffset(field.Name, typeRef, ArchBits), FieldName = sanitizedFieldName, FieldType = fieldType }; m_FieldGenInfos.Add(fieldInfo); m_FieldTypes.Add(fieldType); m_FieldNames.Add(sanitizedFieldName); lookup.Count++; } m_FieldInfoMap.Add(typeRef, lookup); // Now that we have added info for the top-level, recurse until all nested fields have fieldInfo foreach (var field in typeDef.Fields) { if (field.IsStatic) { continue; } var fieldType = resolver.ResolveFieldType(field); GenerateFieldInfos(fieldType); } } return(lookup); }
static IEnumerable <List <FieldReference> > IterateJobFields(TypeReference type, Func <FieldReference, bool> shouldYieldFilter, Func <FieldReference, bool> shouldRecurseFilter, List <FieldReference> fieldPath) { // The incoming `type` should be a full generic instance. This genericResolver // will help us resolve the generic parameters of any of its fields var genericResolver = TypeResolver.For(type); foreach (var typeField in type.Resolve().Fields) { // Early out the statics: (may add an option to change this later. But see next comment.) // Excluding statics covers: // 1) enums which infinitely recurse because the values in the enum are of the same enum type // 2) statics which infinitely recurse themselves (Such as vector3.zero.zero.zero.zero) if (typeField.IsStatic) { continue; } // The fully generic-resolved field reference. This is the reference that's needed // as a Ldfld(a) reference. var genericResolvedFieldType = genericResolver.ResolveFieldType(typeField); var shouldYield = shouldYieldFilter?.Invoke(typeField) ?? true; var shouldRecurse = shouldRecurseFilter?.Invoke(typeField) ?? true; if (!shouldYield && !shouldRecurse) { continue; } var f = genericResolver.Resolve(typeField); fieldPath.Add(f); // yield the current field path if (shouldYield) { yield return(fieldPath); } // only recurse into things that are straight values; no pointers or byref values if (shouldRecurse && typeField.FieldType.IsValueType && !typeField.FieldType.IsPrimitive) { // make sure we iterate the FieldType with all generic params resolved foreach (var fr in IterateJobFields(genericResolvedFieldType, shouldYieldFilter, shouldRecurseFilter, fieldPath)) { yield return(fr); } } fieldPath.RemoveAt(fieldPath.Count - 1); } }
internal static void GetFieldOffsetsOfRecurse(Func <FieldReference, TypeReference, bool> match, int offset, TypeReference type, List <int> list, int bits) { int in_type_offset = 0; var typeResolver = TypeResolver.For(type); foreach (var f in type.Resolve().Fields) { if (f.IsStatic) { continue; } uint alignUp(uint a, uint align) => (a + ((align - a) % align)); int valueOr1(int v) => Math.Max(v, 1); TypeUtils.AlignAndSize resize(TypeUtils.AlignAndSize s) => new TypeUtils.AlignAndSize( valueOr1(s.align), valueOr1(s.size)); var fieldReference = typeResolver.Resolve(f); var fieldType = typeResolver.Resolve(f.FieldType); var tinfo = resize(TypeUtils.AlignAndSizeOfType(fieldType, bits)); if (f.Offset != -1) { in_type_offset = f.Offset; } else { in_type_offset = (int)alignUp((uint)in_type_offset, (uint)tinfo.align); } if (fieldType.IsDynamicArray() && match(fieldReference, fieldType.DynamicArrayElementType())) { // +1 so that we have a way to indicate an Array<Entity> at position 0 // fixup code subtracts 1 list.Add(-(offset + in_type_offset + 1)); } else if (match(fieldReference, fieldType)) { list.Add(offset + in_type_offset); } else if (fieldType.IsValueType && !fieldType.IsPrimitive) { GetFieldOffsetsOfRecurse(match, offset + in_type_offset, fieldType, list, bits); } in_type_offset += tinfo.size; } }
static MethodDefinition CreateGetValueMethod(Context context, TypeReference containerType, TypeReference memberType, IMetadataTokenProvider member) { var method = new MethodDefinition ( @name: "GetValue", @attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.ReuseSlot, @returnType: memberType ) { Body = { InitLocals = true } }; var containerParameter = new ParameterDefinition("container", ParameterAttributes.None, new ByReferenceType(containerType)); method.Parameters.Add(containerParameter); var il = method.Body.GetILProcessor(); il.Emit(OpCodes.Ldarg_1); // container if (!containerType.IsValueType) { il.Emit(OpCodes.Ldind_Ref); } if (member is FieldDefinition field) { var resolvedDeclaringType = field.GetResolvedDeclaringType(containerType); var reference = TypeResolver.For(resolvedDeclaringType).Resolve(field).CreateImportedType(context.Module); il.Emit(OpCodes.Ldfld, reference); } else if (member is PropertyDefinition property) { var resolvedDeclaringType = property.GetResolvedDeclaringType(containerType); var getMethod = property.GetMethod.MakeGenericHostMethod(resolvedDeclaringType); getMethod.ReturnType = getMethod.ReturnType.CreateImportedType(context.Module); il.Emit(containerType != resolvedDeclaringType ? OpCodes.Callvirt : OpCodes.Call, context.Module.ImportReference(getMethod)); } il.Emit(OpCodes.Ret); return(method); }
public static int GetFieldOffset(string fieldName, TypeReference typeRefToLookIn, int archBits) { int in_type_offset = 0; var typeResolver = TypeResolver.For(typeRefToLookIn); var typeDefToLookIn = typeRefToLookIn.Resolve(); foreach (var f in typeDefToLookIn.Fields) { if (f.IsStatic) { continue; } uint alignUp(uint a, uint align) => (a + ((align - a) % align)); int valueOr1(int v) => Math.Max(v, 1); TypeUtils.AlignAndSize resize(TypeUtils.AlignAndSize s) => new TypeUtils.AlignAndSize( valueOr1(s.align), valueOr1(s.size)); var fieldReference = typeResolver.Resolve(f); var fieldType = typeResolver.Resolve(f.FieldType); var tinfo = resize(TypeUtils.AlignAndSizeOfType(fieldType, archBits)); if (f.Offset != -1) { in_type_offset = f.Offset; } else { in_type_offset = (int)alignUp((uint)in_type_offset, (uint)tinfo.align); } if (fieldName == f.Name) { break; } in_type_offset += tinfo.size; } return(in_type_offset); }
public static ulong HashType(TypeReference typeRef, int fieldIndex = 0) { ulong hash = kFNV1A64OffsetBasis; var typeResolver = TypeResolver.For(typeRef); var typeDef = typeRef.Resolve(); foreach (var field in typeDef.Fields) { if (!field.IsStatic) { string fieldName = GetSanitizedFullName(typeResolver.Resolve(field.FieldType)); hash = CombineFNV1A64(hash, FNV1A64(fieldName)); hash = CombineFNV1A64(hash, FNV1A64(fieldIndex)); ++fieldIndex; } } return(hash); }
public static void IterateFieldsRecurse(Action <FieldReference, TypeReference> processFunc, TypeReference type) { var typeResolver = TypeResolver.For(type); foreach (var f in type.Resolve().Fields) { var fieldReference = typeResolver.Resolve(f); var fieldType = typeResolver.Resolve(f.FieldType); processFunc(fieldReference, fieldType); // Excluding statics for recursion covers: // 1) enums which infinitely recurse because the values in the enum are of the same enum type // 2) statics which infinitely recurse themselves (Such as vector3.zero.zero.zero.zero) if (fieldType.IsValueType && !fieldType.IsPrimitive && !f.IsStatic) { IterateFieldsRecurse(processFunc, fieldType); } } }
public static void ValidateAllowedObjectType(TypeReference typeRef) { TypeDefinition type = typeRef.Resolve(); if (typeRef.IsPrimitive || type.IsEnum || typeRef.MetadataType == MetadataType.String) { return; } if (AlreadyValidated.Contains(type)) { return; } AlreadyValidated.Add(type); var typeResolver = TypeResolver.For(type); foreach (var field in type.Fields) { ValidateAllowedObjectType(typeResolver.Resolve(field.FieldType)); } }
public NativeToManagedInteropMethodBodyWriter(MethodReference managedMethod, MethodReference interopMethod, MarshalType marshalType, bool useUnicodeCharset) : base(interopMethod, managedMethod, new NativeToManagedMarshaler(TypeResolver.For(interopMethod.DeclaringType, interopMethod), marshalType, useUnicodeCharset)) { this._managedMethod = managedMethod; }
public static void PreprocessTypeFields(TypeReference valuetype, int bits) { if (bits == 32) { bits = 0; } else if (bits == 64) { bits = 1; } int size = 0; int highestFieldAlignment = 0; bool isComplex = false; // have we already preprocessed this? if (ValueTypeAlignment[bits].ContainsKey(valuetype) && !ValueTypeAlignment[bits][valuetype].IsSentinel) { return; } // For each field, calculate its layout as if it was a C++ struct //Console.WriteLine($"Type {valuetype}"); var typeResolver = TypeResolver.For(valuetype); var typeDef = valuetype.Resolve(); foreach (var fs in typeDef.Fields) { if (fs.IsStatic) { continue; } var fieldType = typeResolver.Resolve(fs.FieldType); var sz = AlignAndSizeOfType(fieldType, bits); isComplex = isComplex || fieldType.IsComplex(); // In C++, all members of a struct must have their own address. // If we have a "struct {}" as a member, treat its size as at least one byte. sz = new AlignAndSize(sz.align, Math.Max(sz.size, 1)); highestFieldAlignment = Math.Max(highestFieldAlignment, sz.align); size = AlignUp(size, sz.align); //Console.WriteLine($" Field: {fs.Name} ({fs.GetType()}) - offset: {size} alignment {sz.align} sz {sz.size}"); StructFieldAlignment[bits].Add(typeResolver.Resolve(fs) /*VERIFY*/, new AlignAndSize(sz.align, sz.size, size)); int offset = fs.Offset; if (offset >= 0) { size = offset + sz.size; } else { size += sz.size; } } // same min size for outer struct size = Math.Max(size, 1); // C++ aligns struct sizes up to the highest alignment required size = AlignUp(size, highestFieldAlignment); // If an explicit size have been provided use that instead if (typeDef.IsExplicitLayout && typeDef.ClassSize > 0) { size = typeDef.ClassSize; } // Alignment requirements are > 0 highestFieldAlignment = Math.Max(highestFieldAlignment, 1); // If an explict alignment has been provided use that instead if (typeDef.IsExplicitLayout && typeDef.PackingSize > 0) { size = typeDef.PackingSize; } ValueTypeAlignment[bits].Remove(valuetype); ValueTypeAlignment[bits].Add(valuetype, new AlignAndSize(highestFieldAlignment, size, 0, typeDef.Fields.Count == 0)); //Console.WriteLine($"ValueType: {valuetype.Name} ({valuetype.GetType()}) - alignment {highestFieldAlignment} sz {size}"); ValueTypeIsComplex[bits].Add(valuetype, isComplex); }
static MethodDefinition CreateSetValueMethod(Context context, TypeReference containerType, TypeReference memberType, IMetadataTokenProvider member, bool isReadOnly) { var method = new MethodDefinition ( @name: "SetValue", @attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.ReuseSlot, @returnType: context.ImportReference(typeof(void)) ) { Body = { InitLocals = true } }; method.Parameters.Add(new ParameterDefinition("container", ParameterAttributes.None, new ByReferenceType(containerType))); method.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, memberType)); var il = method.Body.GetILProcessor(); if (isReadOnly) { il.Emit(OpCodes.Ldstr, "Property is ReadOnly"); il.Emit(OpCodes.Newobj, context.ExceptionConstructor.Value); il.Emit(OpCodes.Throw); } else { if (member is FieldDefinition field) { var resolvedDeclaringType = field.GetResolvedDeclaringType(containerType); var reference = TypeResolver.For(resolvedDeclaringType).Resolve(field).CreateImportedType(context.Module); il.Emit(OpCodes.Ldarg_1); // container if (!containerType.IsValueType) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldarg_2); // value il.Emit(OpCodes.Stfld, reference); il.Emit(OpCodes.Ret); } else if (member is PropertyDefinition property) { var resolvedDeclaringType = property.GetResolvedDeclaringType(containerType); var setMethod = property.SetMethod.MakeGenericHostMethod(resolvedDeclaringType); setMethod.Parameters[0].ParameterType = setMethod.Parameters[0].ParameterType.CreateImportedType(context.Module); il.Emit(OpCodes.Ldarg_1); // container if (!containerType.IsValueType) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldarg_2); // value il.Emit(containerType != resolvedDeclaringType ? OpCodes.Callvirt : OpCodes.Call, context.Module.ImportReference(setMethod)); il.Emit(OpCodes.Ret); } } return(method); }
public ManagedToNativeInteropMethodBodyWriter(MethodReference interopMethod, MethodReference methodForParameterNames, MarshalType marshalType, bool useUnicodeCharset) : base(interopMethod, methodForParameterNames, new ManagedToNativeMarshaler(TypeResolver.For(interopMethod.DeclaringType, interopMethod), marshalType, useUnicodeCharset)) { }
static bool IsManagedTypeInternal(TypeReference typeRef, ref bool hasEntityRefs, ref bool hasBlobRefs, HashSet <TypeReference> seenTypes) { seenTypes.Add(typeRef); if (typeRef.IsPointer) { return(false); } if (typeRef.IsArray) { var elementType = typeRef.GetElementType(); if (elementType.IsEntityType()) { hasEntityRefs = true; } else if (elementType.IsBlobAssetReferenceType()) { hasBlobRefs = true; } return(true); } if (typeRef.IsGenericParameter) { var gp = (GenericParameter)typeRef; bool _ = false, __ = false; return(gp.Constraints.FirstOrDefault(c => c.IsManagedType(ref _, ref __)) != null); } var type = typeRef.Resolve(); if (type.IsDynamicArray()) { return(true); } TypeDefinition fixedSpecialType = type.FixedSpecialType(); if (fixedSpecialType != null) { if (fixedSpecialType.MetadataType == MetadataType.String) { return(true); } return(false); } if (type.IsEnum) { return(false); } // if none of the above check the type's fields bool isManaged = !type.IsValueType; var typeResolver = TypeResolver.For(typeRef); foreach (var field in type.Fields) { if (field.IsStatic) { continue; } var fieldType = typeResolver.Resolve(field.FieldType); if (seenTypes.Contains(fieldType)) { continue; } var fieldTypeDef = fieldType.Resolve(); if (fieldType.IsEntityType()) { hasEntityRefs = true; } else if (fieldType.IsBlobAssetReferenceType()) { hasBlobRefs = true; } if (!fieldTypeDef.IsSealed) { hasEntityRefs = true; hasBlobRefs = true; } if (IsManagedTypeInternal(fieldType, ref hasEntityRefs, ref hasBlobRefs, seenTypes)) { isManaged = true; } } return(isManaged); }