public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This is a summary node that doesn't introduce dependencies. if (relocsOnly) { return(new ObjectData(Array.Empty <byte>(), Array.Empty <Relocation>(), 1, new ISymbolDefinitionNode[] { this })); } var modulesWithCctor = new List <ModuleDesc>(); foreach (var methodNode in factory.MetadataManager.GetCompiledMethodBodies()) { MethodDesc method = methodNode.Method; if (method.OwningType is MetadataType mdType && mdType.IsModuleType && method.IsStaticConstructor) { modulesWithCctor.Add(mdType.Module); } } // We have a list of modules with a class constructor. // Do a topological sort based on the assembly references of each module. // This is an approximation that tries to deal with module initializers that might have // dependencies on each other. The spec doesn't guarantee any ordering so this is best effort. List <ModuleDesc> sortedModules = new List <ModuleDesc>(); var graphFactory = new ModuleGraphFactory(); // This is a list because we want to keep a stable sort order. var allModules = new List <ModuleGraphNode>(); // Seed the graph with the list of modules we're interested in foreach (ModuleDesc module in modulesWithCctor) { allModules.Add(graphFactory.GetNode(module)); } // Expand the graph to include all the nodes in between the interesting ones var modulesToExpand = new Queue <ModuleGraphNode>(allModules); while (modulesToExpand.Count > 0) { ModuleGraphNode node = modulesToExpand.Dequeue(); foreach (var reference in node.Edges) { if (!allModules.Contains(reference)) { allModules.Add(reference); modulesToExpand.Enqueue(reference); } } } // Now sort the nodes // // We start with the modules that don't reference any other modules. // Then we add modules that reference the modules we already sorted. // Etc. until we figured out the order of all modules with a cctor. // // This might appear counter intuitive (the cctor of the entrypoint module // will likely run last), but if the entrypoint module cctor calls into another // module that has a cctor, CoreCLR will run that cctor first. // // If a module doesn't call into other modules with a cctor, it doesn't matter // when we sort it. If it does call into one, it should run before. var markedModules = new HashSet <ModuleGraphNode>(); while (sortedModules.Count != modulesWithCctor.Count) { bool madeProgress = false; // Add nodes that have all their dependencies already satisfied. foreach (var module in allModules) { if (!markedModules.Contains(module) && module.Satisfies(markedModules)) { madeProgress = true; markedModules.Add(module); if (modulesWithCctor.Contains(module.Module)) { sortedModules.Add(module.Module); } } } // If we haven't made progress, there's a cycle. Pick the first unmarked node as victim. if (!madeProgress) { foreach (var module in allModules) { if (!markedModules.Contains(module)) { markedModules.Add(module); if (modulesWithCctor.Contains(module.Module)) { sortedModules.Add(module.Module); } break; } } } } // The data structure is a flat list of module constructors to call. // This is insufficient for the purposes of ordering in the multi-object-module mode. // If this mode ever becomes more interesting, we'll need to do the sorting at // the time of startup. (Linker likely can't do it, unfortunately.) ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); builder.AddSymbol(this); builder.AddSymbol(_endSymbol); foreach (var module in sortedModules) { builder.EmitPointerReloc(factory.MethodEntrypoint(module.GetGlobalModuleType().GetStaticConstructor())); } var result = builder.ToObjectData(); _endSymbol.SetSymbolOffset(result.Data.Length); return(result); }
public ModuleGraphNode(ModuleGraphFactory factory, ModuleDesc module, ModuleDesc[] edges) => (_factory, Module, _edges) = (factory, module, edges);