static void ProcessAssembly(AssemblyDefinition bindingsAssembly, string assemblyPath, string outputPath, BaseAssemblyResolver resolver, string [] knownPaths, NativeTypeManifest manifest, bool verify) { string metadataFileName = Path.ChangeExtension(outputPath, "json"); var readerParams = new ReaderParameters { AssemblyResolver = resolver, ReadSymbols = true, SymbolReaderProvider = new PortablePdbReaderProvider(), ReadingMode = ReadingMode.Deferred }; var assembly = AssemblyDefinition.ReadAssembly(assemblyPath, readerParams); var metadata = new AssemblyMetadata { AssemblyName = assembly.Name.Name, AssemblyPath = assemblyPath, References = (from module in assembly.Modules from reference in module.AssemblyReferences select AssemblyReferenceMetadata.ResolveReference(reference, resolver, assembly.Name.Name, knownPaths)).ToArray() }; /////////////////////////////////////////////////////////////////// //Enums var unrealEnums = (from module in assembly.Modules from type in module.Types where type.IsEnum && IsUnrealEnum(type) select type); unrealEnums = manifest.CheckForCollisions(unrealEnums, "Enum"); //Generate metadata bool errorGeneratingEnumMetadata = false; var enumMetadata = unrealEnums.ToDictionaryErrorEmit(type => type, type => new EnumMetadata(type), out errorGeneratingEnumMetadata); metadata.Enums = enumMetadata.Values.ToArray(); /////////////////////////////////////////////////////////////////// //Structs // Capture the current struct list in an array, as IL rewriting for UStructs may add array marshaler types. var unrealStructs = (from module in assembly.Modules from type in module.Types where type.IsValueType && IsUnrealStruct(type) select type); unrealStructs = manifest.CheckForCollisions(unrealStructs, "Struct"); // We need to create struct metadata in the correct order to ensure that blittable structs have // their UStruct attributes updated before other referencing structs use them to create UnrealTypes. var structStack = new Stack <TypeDefinition> (); var pushedStructs = new HashSet <TypeDefinition> (); var structHandlingOrder = new List <TypeDefinition> (); var structMetadata = new Dictionary <TypeDefinition, StructMetadata> (); bool errorsGeneratingStructMetadata = false; foreach (var unrealStruct in unrealStructs) { if (!pushedStructs.Contains(unrealStruct)) { structStack.Push(unrealStruct); pushedStructs.Add(unrealStruct); PushReferencedStructsFromAssembly(assembly, unrealStruct, structStack, pushedStructs); while (structStack.Count > 0) { var currentStruct = structStack.Pop(); try { if (structMetadata.ContainsKey(currentStruct)) { throw new InternalRewriteException(currentStruct, "Attempted to create struct metadata twice"); } var currentMetadata = new StructMetadata(currentStruct); structHandlingOrder.Add(currentStruct); structMetadata.Add(currentStruct, currentMetadata); } catch (MonoAssemblyProcessError error) { errorsGeneratingStructMetadata = true; ErrorEmitter.Error(error); } } } } metadata.Structs = structMetadata.Values.ToArray(); // Rewrite as a separate pass. For substructs, we need access to metadata.Structs to propagate hashes. foreach (var currentStruct in structHandlingOrder) { StructMetadata currentMetadata = structMetadata [currentStruct]; RewriteUnrealStruct(metadata, assembly, currentStruct, currentMetadata, bindingsAssembly); } /////////////////////////////////////////////////////////////////// //Classes bool unrealClassError; // get all the unreal classes (we don't care if they are exported) var unrealClasses = assembly.Modules.SelectMany(x => x.Types).SelectWhereErrorEmit(type => type.IsClass && type.BaseType != null && IsUnrealClass(type), type => type, out unrealClassError); unrealClasses = manifest.CheckForCollisions(unrealClasses, "Class"); bool errorGeneratingMetadata; var unrealClassDictionary = unrealClasses.ToDictionaryErrorEmit(type => type, type => ClassMetadata.Create(type, GetUnrealClass(type)), out errorGeneratingMetadata); if (errorGeneratingMetadata || errorsGeneratingStructMetadata || errorGeneratingEnumMetadata) { throw new MetaDataGenerationException(); } metadata.Classes = unrealClassDictionary.Values.ToArray(); if (unrealClassDictionary.Count > 0) { var rewriteStack = new Stack <KeyValuePair <TypeDefinition, ClassMetadata> > (); var rewrittenClasses = new HashSet <TypeDefinition> (); // make sure we write base classes before derived ones foreach (var pair in unrealClassDictionary) { var currentPair = pair; bool needRewrite = true; while (needRewrite) { if (!rewrittenClasses.Contains(currentPair.Key)) { rewriteStack.Push(currentPair); var baseType = currentPair.Key.BaseType.Resolve(); if (unrealClassDictionary.ContainsKey(baseType)) { ClassMetadata baseClassMetadata = unrealClassDictionary [baseType]; currentPair = new KeyValuePair <TypeDefinition, ClassMetadata> (baseType, baseClassMetadata); } else { needRewrite = false; } } else { needRewrite = false; } } while (rewriteStack.Count > 0) { currentPair = rewriteStack.Pop(); if (rewrittenClasses.Contains(currentPair.Key)) { throw new InternalRewriteException(currentPair.Key, "Attempted to rewrite class twice"); } var baseType = currentPair.Key.BaseType; if (baseType != null && unrealClassDictionary.ContainsKey(baseType.Resolve())) { ClassMetadata superClassMetadata = unrealClassDictionary [baseType.Resolve()]; currentPair.Value.SetSuperClassHash(superClassMetadata); } RewriteUnrealClass(metadata, assembly, currentPair.Key, currentPair.Value, bindingsAssembly); rewrittenClasses.Add(currentPair.Key); } } var writer = new WriterParameters { WriteSymbols = true, SymbolWriterProvider = new PortablePdbWriterProvider() }; assembly.Write(outputPath, writer); VerifyAssembly(outputPath, knownPaths, verify); //for some reason msbuild uses creation time or incrementral builds, not mtime //but cecil preserves the creation time from the original so we have to reset it File.SetCreationTimeUtc(outputPath, DateTime.UtcNow); string metadataContents = fastJSON.JSON.ToNiceJSON(metadata, new fastJSON.JSONParameters { UseExtensions = false }); File.WriteAllText(metadataFileName, metadataContents); } else { File.Delete(metadataFileName); } }