Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 151 additions & 16 deletions src/ILLink.Tasks/CreateRuntimeRootDescriptorFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,33 @@ class ClassMembers
public HashSet<string> fields;
}

/// <summary>
/// Helper utility to track feature switch macros in header file
/// This type is used in a dictionary as a key and only uses
/// the feature name since value and default are not expected to change
/// </summary>
class FeatureSwitchMembers
{
public string feature;
public string featureValue;
public string featureDefault;

public override int GetHashCode ()
{
return feature.GetHashCode ();
}

public override bool Equals (object obj)
{
FeatureSwitchMembers other = obj as FeatureSwitchMembers;
return other.feature.Equals (feature);
}
}

readonly Dictionary<string, string> namespaceDictionary = new Dictionary<string, string> ();
readonly Dictionary<string, string> classIdsToClassNames = new Dictionary<string, string> ();
readonly Dictionary<string, ClassMembers> classNamesToClassMembers = new Dictionary<string, ClassMembers> ();
readonly Dictionary<FeatureSwitchMembers, Dictionary<string, ClassMembers>> featureSwitchMembers = new Dictionary<FeatureSwitchMembers, Dictionary<string, ClassMembers>> ();
readonly HashSet<string> defineConstants = new HashSet<string> (StringComparer.Ordinal);

public override bool Execute ()
Expand Down Expand Up @@ -139,11 +163,38 @@ void ProcessMscorlib (string typeFile)
string[] types = File.ReadAllLines (typeFile);
DefineTracker defineTracker = new DefineTracker (defineConstants, Log, typeFile);
string classId;
bool insideFeatureSwitch = false;
FeatureSwitchMembers currentFeatureSwitch = null;

foreach (string def in types) {
if (defineTracker.ProcessLine (def) || !defineTracker.IsActiveSection)
continue;

//We will handle BEGIN_ILLINK_FEATURE_SWITCH and END_ILLINK_FEATURE_SWITCH
if (def.StartsWith ("BEGIN_ILLINK_FEATURE_SWITCH")) {
if (insideFeatureSwitch) {
Log.LogError ($"Could not figure out feature switch status in '{typeFile}' for line {def}");
} else {
insideFeatureSwitch = true;
char[] separators = { ',', '(', ')', ' ', '\t', '/' };
string[] featureSwitchElements = def.Split (separators, StringSplitOptions.RemoveEmptyEntries);
currentFeatureSwitch = new FeatureSwitchMembers ();
currentFeatureSwitch.feature = featureSwitchElements[1];
currentFeatureSwitch.featureValue = featureSwitchElements[2];
currentFeatureSwitch.featureDefault = featureSwitchElements[3];
if (!featureSwitchMembers.ContainsKey (currentFeatureSwitch)) {
featureSwitchMembers.Add (currentFeatureSwitch, new Dictionary<string, ClassMembers> ());
}
}
}
if (def.StartsWith ("END_ILLINK_FEATURE_SWITCH")) {
if (!insideFeatureSwitch) {
Log.LogError ($"Could not figure out feature switch status in '{typeFile}' for line {def}");
} else {
insideFeatureSwitch = false;
}
}

string[] defElements = null;
if (def.StartsWith ("DEFINE_") || def.StartsWith ("// DEFINE_")) {
char[] separators = { ',', '(', ')', ' ', '\t', '/' };
Expand All @@ -155,7 +206,7 @@ void ProcessMscorlib (string typeFile)
classId = defElements[1]; // APP_DOMAIN
string classNamespace = defElements[2]; // System
string className = defElements[3]; // AppDomain
AddClass (classNamespace, className, classId);
AddClass (classNamespace, className, classId, false, insideFeatureSwitch, currentFeatureSwitch);
} else if (def.StartsWith ("DEFINE_CLASS_U(")) {
// E.g., DEFINE_CLASS_U(System, AppDomain, AppDomainBaseObject)
string classNamespace = defElements[1]; // System
Expand All @@ -164,29 +215,29 @@ void ProcessMscorlib (string typeFile)
// For these classes the sizes of managed and unmanaged classes and field offsets
// are compared so we need to preserve all fields.
const bool keepAllFields = true;
AddClass (classNamespace, className, classId, keepAllFields);
AddClass (classNamespace, className, classId, keepAllFields, insideFeatureSwitch, currentFeatureSwitch);
} else if (def.StartsWith ("DEFINE_FIELD(")) {
// E.g., DEFINE_FIELD(ACCESS_VIOLATION_EXCEPTION, IP, _ip)
classId = defElements[1]; // ACCESS_VIOLATION_EXCEPTION
string fieldName = defElements[3]; // _ip
AddField (fieldName, classId);
AddField (fieldName, classId, insideFeatureSwitch, currentFeatureSwitch);
} else if (def.StartsWith ("DEFINE_METHOD(")) {
// E.g., DEFINE_METHOD(APP_DOMAIN, ON_ASSEMBLY_LOAD, OnAssemblyLoadEvent, IM_Assembly_RetVoid)
string methodName = defElements[3]; // OnAssemblyLoadEvent
classId = defElements[1]; // APP_DOMAIN
AddMethod (methodName, classId);
AddMethod (methodName, classId, null, null, insideFeatureSwitch, currentFeatureSwitch);
} else if (def.StartsWith ("DEFINE_PROPERTY(") || def.StartsWith ("DEFINE_STATIC_PROPERTY(")) {
// E.g., DEFINE_PROPERTY(ARRAY, LENGTH, Length, Int)
// or DEFINE_STATIC_PROPERTY(THREAD, CURRENT_THREAD, CurrentThread, Thread)
string propertyName = defElements[3]; // Length or CurrentThread
classId = defElements[1]; // ARRAY or THREAD
AddMethod ("get_" + propertyName, classId);
AddMethod ("get_" + propertyName, classId, null, null, insideFeatureSwitch, currentFeatureSwitch);
} else if (def.StartsWith ("DEFINE_SET_PROPERTY(")) {
// E.g., DEFINE_SET_PROPERTY(THREAD, UI_CULTURE, CurrentUICulture, CultureInfo)
string propertyName = defElements[3]; // CurrentUICulture
classId = defElements[1]; // THREAD
AddMethod ("get_" + propertyName, classId);
AddMethod ("set_" + propertyName, classId);
AddMethod ("get_" + propertyName, classId, null, null, insideFeatureSwitch, currentFeatureSwitch);
AddMethod ("set_" + propertyName, classId, null, null, insideFeatureSwitch, currentFeatureSwitch);
}
}
}
Expand Down Expand Up @@ -239,6 +290,70 @@ void OutputXml (string iLLinkTrimXmlFilePath, string outputFileName)
XmlDocument doc = new XmlDocument ();
doc.Load (iLLinkTrimXmlFilePath);
XmlNode linkerNode = doc["linker"];

if (featureSwitchMembers.Count > 0) {
foreach (var fsMembers in featureSwitchMembers.Keys) {
// <assembly fullname="System.Private.CoreLib" feature="System.Diagnostics.Tracing.EventSource.IsSupported" featurevalue="true" featuredefault="true">
XmlNode featureAssemblyNode = doc.CreateElement ("assembly");
XmlAttribute featureAssemblyFullName = doc.CreateAttribute ("fullname");
featureAssemblyFullName.Value = "System.Private.CoreLib";
featureAssemblyNode.Attributes.Append (featureAssemblyFullName);

XmlAttribute featureName = doc.CreateAttribute ("feature");
featureName.Value = fsMembers.feature;
featureAssemblyNode.Attributes.Append (featureName);

XmlAttribute featureValue = doc.CreateAttribute ("featurevalue");
featureValue.Value = fsMembers.featureValue;
featureAssemblyNode.Attributes.Append (featureValue);

XmlAttribute featureDefault = doc.CreateAttribute ("featuredefault");
featureDefault.Value = fsMembers.featureDefault;
featureAssemblyNode.Attributes.Append (featureDefault);

foreach (string typeName in featureSwitchMembers[fsMembers].Keys) {
XmlNode typeNode = doc.CreateElement ("type");
XmlAttribute typeFullName = doc.CreateAttribute ("fullname");
typeFullName.Value = typeName;
typeNode.Attributes.Append (typeFullName);

ClassMembers members = featureSwitchMembers[fsMembers][typeName];

if (members.keepAllFields) {
XmlAttribute preserve = doc.CreateAttribute ("preserve");
preserve.Value = "fields";
typeNode.Attributes.Append (preserve);
} else if ((members.fields == null) && (members.methods == null)) {
XmlAttribute preserve = doc.CreateAttribute ("preserve");
preserve.Value = "nothing";
typeNode.Attributes.Append (preserve);
}

if (!members.keepAllFields && (members.fields != null)) {
foreach (string field in members.fields) {
XmlNode fieldNode = doc.CreateElement ("field");
XmlAttribute fieldName = doc.CreateAttribute ("name");
fieldName.Value = field;
fieldNode.Attributes.Append (fieldName);
typeNode.AppendChild (fieldNode);
}
}

if (members.methods != null) {
foreach (string method in members.methods) {
XmlNode methodNode = doc.CreateElement ("method");
XmlAttribute methodName = doc.CreateAttribute ("name");
methodName.Value = method;
methodNode.Attributes.Append (methodName);
typeNode.AppendChild (methodNode);
}
}
featureAssemblyNode.AppendChild (typeNode);
}
linkerNode.AppendChild (featureAssemblyNode);
}
}

XmlNode assemblyNode = linkerNode["assembly"];

foreach (string typeName in classNamesToClassMembers.Keys) {
Expand Down Expand Up @@ -288,34 +403,48 @@ void OutputXml (string iLLinkTrimXmlFilePath, string outputFileName)
doc.Save (outputFileName);
}

void AddClass (string classNamespace, string className, string classId, bool keepAllFields = false)
void AddClass (string classNamespace, string className, string classId, bool keepAllFields = false, bool featureSwitchOn = false, FeatureSwitchMembers featureSwitch = null)
{
string fullClassName = GetFullClassName (classNamespace, className);
if (fullClassName != null) {
if ((classId != null) && (classId != "NoClass")) {
classIdsToClassNames[classId] = fullClassName;
}
if (!classNamesToClassMembers.TryGetValue (fullClassName, out ClassMembers members)) {
members = new ClassMembers ();
classNamesToClassMembers[fullClassName] = members;
if (!featureSwitchOn) {
if (!classNamesToClassMembers.TryGetValue (fullClassName, out ClassMembers members)) {
members = new ClassMembers ();
classNamesToClassMembers[fullClassName] = members;
}
members.keepAllFields |= keepAllFields;
} else {
Dictionary<string, ClassMembers> currentFeatureSwitchMembers = featureSwitchMembers[featureSwitch];
if (!currentFeatureSwitchMembers.TryGetValue (fullClassName, out ClassMembers members)) {
members = new ClassMembers ();
currentFeatureSwitchMembers[fullClassName] = members;
}
members.keepAllFields |= keepAllFields;
}
members.keepAllFields |= keepAllFields;
}
}

void AddField (string fieldName, string classId)
void AddField (string fieldName, string classId, bool featureSwitchOn = false, FeatureSwitchMembers featureSwitch = null)
{
string className = classIdsToClassNames[classId];
ClassMembers members;

ClassMembers members = classNamesToClassMembers[className];
if (!featureSwitchOn) {
members = classNamesToClassMembers[className];
} else {
members = featureSwitchMembers[featureSwitch][className];
}

if (members.fields == null) {
members.fields = new HashSet<string> ();
}
members.fields.Add (fieldName);
}

void AddMethod (string methodName, string classId, string classNamespace = null, string className = null)
void AddMethod (string methodName, string classId, string classNamespace = null, string className = null, bool featureSwitchOn = false, FeatureSwitchMembers featureSwitch = null)
{
string fullClassName;
if (classId != null) {
Expand All @@ -324,7 +453,13 @@ void AddMethod (string methodName, string classId, string classNamespace = null,
fullClassName = GetFullClassName (classNamespace, className);
}

ClassMembers members = classNamesToClassMembers[fullClassName];
ClassMembers members;

if (!featureSwitchOn) {
members = classNamesToClassMembers[fullClassName];
} else {
members = featureSwitchMembers[featureSwitch][fullClassName];
}

if (members.methods == null) {
members.methods = new HashSet<string> ();
Expand Down