Skip to content

Parsing update for native header files with feature switch #2044

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
221 changes: 167 additions & 54 deletions src/ILLink.Tasks/CreateRuntimeRootDescriptorFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,43 @@ 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
/// </summary>
readonly struct FeatureSwitchMembers
{
public string Feature { get; }
public string FeatureValue { get; }
public string FeatureDefault { get; }
// Unique value to track the key
private readonly String _key;

public FeatureSwitchMembers (string feature, string featureValue, string featureDefault)
{
Feature = feature;
FeatureValue = featureValue;
FeatureDefault = featureDefault;
// Use a separator that is not going to be in any of the strings to ensure uniqueness
_key = feature + ',' + featureValue + ',' + featureDefault;
}

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

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

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 ();
readonly HashSet<string> defineConstants = new HashSet<string> (StringComparer.Ordinal);

public override bool Execute ()
Expand Down Expand Up @@ -139,11 +173,37 @@ void ProcessMscorlib (string typeFile)
string[] types = File.ReadAllLines (typeFile);
DefineTracker defineTracker = new DefineTracker (defineConstants, Log, typeFile);
string classId;
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 (currentFeatureSwitch.HasValue) {
Log.LogError ($"Could not figure out feature switch status in '{typeFile}' for line {def}");
} else {
char[] separators = { ',', '(', ')', ' ', '\t', '/' };
string[] featureSwitchElements = def.Split (separators, StringSplitOptions.RemoveEmptyEntries);
if (featureSwitchElements.Length != 4) {
Log.LogError ($"BEGIN_ILLINK_FEATURE_SWITCH is not formatted correctly '{typeFile}' for line {def}");
return;
}
currentFeatureSwitch = new FeatureSwitchMembers (featureSwitchElements[1], featureSwitchElements[2], featureSwitchElements[3]);
if (!featureSwitchMembers.ContainsKey (currentFeatureSwitch.Value)) {
featureSwitchMembers.Add (currentFeatureSwitch.Value, new Dictionary<string, ClassMembers> ());
}
}
}
if (def.StartsWith ("END_ILLINK_FEATURE_SWITCH")) {
if (!currentFeatureSwitch.HasValue) {
Log.LogError ($"Could not figure out feature switch status in '{typeFile}' for line {def}");
} else {
currentFeatureSwitch = null;
}
}

string[] defElements = null;
if (def.StartsWith ("DEFINE_") || def.StartsWith ("// DEFINE_")) {
char[] separators = { ',', '(', ')', ' ', '\t', '/' };
Expand All @@ -155,7 +215,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, 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 +224,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, 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, 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, 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, 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, currentFeatureSwitch);
AddMethod ("set_" + propertyName, classId, null, null, currentFeatureSwitch);
}
}
}
Expand Down Expand Up @@ -239,83 +299,130 @@ void OutputXml (string iLLinkTrimXmlFilePath, string outputFileName)
XmlDocument doc = new XmlDocument ();
doc.Load (iLLinkTrimXmlFilePath);
XmlNode linkerNode = doc["linker"];
XmlNode assemblyNode = linkerNode["assembly"];

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

ClassMembers members = classNamesToClassMembers[typeName];

// We need to keep everyting in System.Runtime.InteropServices.WindowsRuntime and
// System.Threading.Volatile.
if (!typeName.StartsWith ("System.Runtime.InteropServices.WindowsRuntime") &&
!typeName.StartsWith ("System.Threading.Volatile")) {
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 (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 (var type in featureSwitchMembers[fsMembers]) {
AddXmlTypeNode (doc, featureAssemblyNode, type.Key, type.Value);
}
linkerNode.AppendChild (featureAssemblyNode);
}
}

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);
}
XmlNode assemblyNode = linkerNode["assembly"];

foreach (var type in classNamesToClassMembers) {
AddXmlTypeNode (doc, assemblyNode, type.Key, type.Value);
}
doc.Save (outputFileName);
}

static void AddXmlTypeNode (XmlDocument doc, XmlNode assemblyNode, string typeName, ClassMembers members)
{
XmlNode typeNode = doc.CreateElement ("type");
XmlAttribute typeFullName = doc.CreateAttribute ("fullname");
typeFullName.Value = typeName;
typeNode.Attributes.Append (typeFullName);

// We need to keep everyting in System.Runtime.InteropServices.WindowsRuntime and
// System.Threading.Volatile.
if (!typeName.StartsWith ("System.Runtime.InteropServices.WindowsRuntime") &&
!typeName.StartsWith ("System.Threading.Volatile")) {
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);
}
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);
}
}
assemblyNode.AppendChild (typeNode);
}
doc.Save (outputFileName);
assemblyNode.AppendChild (typeNode);
}

void AddClass (string classNamespace, string className, string classId, bool keepAllFields = false)

void AddClass (string classNamespace, string className, string classId, bool keepAllFields = 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;

ClassMembers members;
if (!featureSwitch.HasValue) {
if (!classNamesToClassMembers.TryGetValue (fullClassName, out members)) {
members = new ClassMembers ();
classNamesToClassMembers[fullClassName] = members;
}
} else {
Dictionary<string, ClassMembers> currentFeatureSwitchMembers = featureSwitchMembers[featureSwitch.Value];
if (!currentFeatureSwitchMembers.TryGetValue (fullClassName, out members)) {
members = new ClassMembers ();
currentFeatureSwitchMembers[fullClassName] = members;
}
}
members.keepAllFields |= keepAllFields;
}
}

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

ClassMembers members = classNamesToClassMembers[className];
if (!featureSwitch.HasValue) {
members = classNamesToClassMembers[className];
} else {
members = featureSwitchMembers[featureSwitch.Value][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, FeatureSwitchMembers? featureSwitch = null)
{
string fullClassName;
if (classId != null) {
Expand All @@ -324,7 +431,13 @@ void AddMethod (string methodName, string classId, string classNamespace = null,
fullClassName = GetFullClassName (classNamespace, className);
}

ClassMembers members = classNamesToClassMembers[fullClassName];
ClassMembers members;

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

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