Skip to content

Commit

Permalink
Cache reflection calls when running utility lints and commands.
Browse files Browse the repository at this point in the history
Reduces runtime of --check-yaml command to 70% of original.
  • Loading branch information
RoosterDragon authored and Mailaender committed Apr 4, 2023
1 parent 8c83d60 commit 9476fc9
Show file tree
Hide file tree
Showing 24 changed files with 116 additions and 87 deletions.
17 changes: 6 additions & 11 deletions OpenRA.Game/Exts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,16 @@ public static IEnumerable<string> GetNamespaces(this Assembly a)
return a.GetTypes().Select(t => t.Namespace).Distinct().Where(n => n != null);
}

public static bool HasAttribute<T>(this MemberInfo mi)
public static bool HasAttribute<TAttribute>(this MemberInfo mi)
where TAttribute : Attribute
{
return Attribute.IsDefined(mi, typeof(T));
return Attribute.IsDefined(mi, typeof(TAttribute));
}

public static T[] GetCustomAttributes<T>(this MemberInfo mi, bool inherit)
where T : class
public static TAttribute[] GetCustomAttributes<TAttribute>(this MemberInfo mi, bool inherit)
where TAttribute : Attribute
{
return (T[])mi.GetCustomAttributes(typeof(T), inherit);
}

public static T[] GetCustomAttributes<T>(this ParameterInfo mi)
where T : class
{
return (T[])mi.GetCustomAttributes(typeof(T), true);
return (TAttribute[])mi.GetCustomAttributes(typeof(TAttribute), inherit);
}

public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
Expand Down
31 changes: 31 additions & 0 deletions OpenRA.Game/IUtilityCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,43 @@
*/
#endregion

using System;
using System.Reflection;
using OpenRA.Primitives;
using OpenRA.Traits;

namespace OpenRA
{
public class Utility
{
static readonly ConcurrentCache<Type, FieldInfo[]> TypeFields =
new ConcurrentCache<Type, FieldInfo[]>(type => type.GetFields());

static readonly ConcurrentCache<(MemberInfo Member, Type AttributeType), bool> MemberHasAttribute =
new ConcurrentCache<(MemberInfo Member, Type AttributeType), bool>(
x => Attribute.IsDefined(x.Member, x.AttributeType));

static readonly ConcurrentCache<(MemberInfo Member, Type AttributeType, bool Inherit), object[]> MemberCustomAttributes =
new ConcurrentCache<(MemberInfo Member, Type AttributeType, bool Inherit), object[]>(
x => x.Member.GetCustomAttributes(x.AttributeType, x.Inherit));

public static FieldInfo[] GetFields(Type type)
{
return TypeFields[type];
}

public static bool HasAttribute<TAttribute>(MemberInfo member)
where TAttribute : Attribute
{
return MemberHasAttribute[(member, typeof(TAttribute))];
}

public static TAttribute[] GetCustomAttributes<TAttribute>(MemberInfo member, bool inherit)
where TAttribute : Attribute
{
return (TAttribute[])MemberCustomAttributes[(member, typeof(TAttribute), inherit)];
}

public readonly ModData ModData;
public readonly InstalledMods Mods;

Expand Down
10 changes: 5 additions & 5 deletions OpenRA.Mods.Common/Lint/CheckActorReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,16 @@ void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning
void CheckTrait(Action<string> emitError, ActorInfo actorInfo, TraitInfo traitInfo, Ruleset rules)
{
var actualType = traitInfo.GetType();
foreach (var field in actualType.GetFields())
foreach (var field in Utility.GetFields(actualType))
{
if (field.HasAttribute<ActorReferenceAttribute>())
if (Utility.HasAttribute<ActorReferenceAttribute>(field))
CheckActorReference(emitError, actorInfo, traitInfo, field, rules.Actors,
field.GetCustomAttributes<ActorReferenceAttribute>(true)[0]);
Utility.GetCustomAttributes<ActorReferenceAttribute>(field, true)[0]);

if (field.HasAttribute<WeaponReferenceAttribute>())
if (Utility.HasAttribute<WeaponReferenceAttribute>(field))
CheckWeaponReference(emitError, actorInfo, traitInfo, field, rules.Weapons);

if (field.HasAttribute<VoiceSetReferenceAttribute>())
if (Utility.HasAttribute<VoiceSetReferenceAttribute>(field))
CheckVoiceReference(emitError, actorInfo, traitInfo, field, rules.Voices);
}
}
Expand Down
6 changes: 3 additions & 3 deletions OpenRA.Mods.Common/Lint/CheckChromeHotkeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void Run(Action<string> emitError, Action<string> emitWarning, ModData mo

// Build the list of widget keys to validate
var checkWidgetFields = modData.ObjectCreator.GetTypesImplementing<Widget>()
.SelectMany(w => w.GetFields()
.SelectMany(w => Utility.GetFields(w)
.Where(f => f.FieldType == typeof(HotkeyReference))
.Select(f => (w.Name.Substring(0, w.Name.Length - 6), f.Name)))
.ToArray();
Expand All @@ -47,7 +47,7 @@ public void Run(Action<string> emitError, Action<string> emitWarning, ModData mo

foreach (var w in modData.ObjectCreator.GetTypesImplementing<Widget>())
{
foreach (var m in w.GetMethods().Where(m => m.HasAttribute<CustomLintableHotkeyNames>()))
foreach (var m in w.GetMethods().Where(m => Utility.HasAttribute<CustomLintableHotkeyNames>(m)))
{
var p = m.GetParameters();
if (p.Length == 3 && p[0].ParameterType == typeof(MiniYamlNode) && p[1].ParameterType == typeof(Action<string>)
Expand Down Expand Up @@ -104,7 +104,7 @@ void CheckInner(ModData modData, string[] namedKeys, (string Widget, string Fiel
if (type == null)
continue;

checkArgKeys.AddRange(type.GetCustomAttributes<ChromeLogicArgsHotkeys>(true).SelectMany(x => x.LogicArgKeys));
checkArgKeys.AddRange(Utility.GetCustomAttributes<ChromeLogicArgsHotkeys>(type, true).SelectMany(x => x.LogicArgKeys));
}

foreach (var n in node.Value.Nodes)
Expand Down
19 changes: 11 additions & 8 deletions OpenRA.Mods.Common/Lint/CheckConditions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,23 @@ void Run(Action<string> emitError, Action<string> emitWarning, Ruleset rules)

foreach (var trait in actorInfo.Value.TraitInfos<TraitInfo>())
{
var fieldConsumed = trait.GetType().GetFields()
.Where(x => x.HasAttribute<ConsumedConditionReferenceAttribute>())
var fields = Utility.GetFields(trait.GetType());
var properties = trait.GetType().GetProperties();

var fieldConsumed = fields
.Where(x => Utility.HasAttribute<ConsumedConditionReferenceAttribute>(x))
.SelectMany(f => LintExts.GetFieldValues(trait, f));

var propertyConsumed = trait.GetType().GetProperties()
.Where(x => x.HasAttribute<ConsumedConditionReferenceAttribute>())
var propertyConsumed = properties
.Where(x => Utility.HasAttribute<ConsumedConditionReferenceAttribute>(x))
.SelectMany(p => LintExts.GetPropertyValues(trait, p));

var fieldGranted = trait.GetType().GetFields()
.Where(x => x.HasAttribute<GrantedConditionReferenceAttribute>())
var fieldGranted = fields
.Where(x => Utility.HasAttribute<GrantedConditionReferenceAttribute>(x))
.SelectMany(f => LintExts.GetFieldValues(trait, f));

var propertyGranted = trait.GetType().GetProperties()
.Where(x => x.HasAttribute<GrantedConditionReferenceAttribute>())
var propertyGranted = properties
.Where(x => Utility.HasAttribute<GrantedConditionReferenceAttribute>(x))
.SelectMany(f => LintExts.GetPropertyValues(trait, f));

foreach (var c in fieldConsumed.Concat(propertyConsumed))
Expand Down
4 changes: 2 additions & 2 deletions OpenRA.Mods.Common/Lint/CheckCursors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ void Run(Action<string> emitError, ModData modData, Ruleset rules)
{
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields();
var fields = Utility.GetFields(traitInfo.GetType());
foreach (var field in fields)
{
var cursorReference = field.GetCustomAttributes<CursorReferenceAttribute>(true).FirstOrDefault();
var cursorReference = Utility.GetCustomAttributes<CursorReferenceAttribute>(field, true).FirstOrDefault();
if (cursorReference == null)
continue;

Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Lint/CheckLocomotorReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void Run(Action<string> emitError, Ruleset rules)
{
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields().Where(f => f.HasAttribute<LocomotorReferenceAttribute>());
var fields = Utility.GetFields(traitInfo.GetType()).Where(f => Utility.HasAttribute<LocomotorReferenceAttribute>(f));
foreach (var field in fields)
{
var locomotors = LintExts.GetFieldValues(traitInfo, field);
Expand Down
6 changes: 3 additions & 3 deletions OpenRA.Mods.Common/Lint/CheckNotifications.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ void Run(Action<string> emitError, Ruleset rules)
{
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields();
foreach (var field in fields.Where(x => x.HasAttribute<NotificationReferenceAttribute>()))
var fields = Utility.GetFields(traitInfo.GetType());
foreach (var field in fields.Where(x => Utility.HasAttribute<NotificationReferenceAttribute>(x)))
{
string type = null;
var notificationReference = field.GetCustomAttributes<NotificationReferenceAttribute>(true).First();
var notificationReference = Utility.GetCustomAttributes<NotificationReferenceAttribute>(field, true).First();
if (!string.IsNullOrEmpty(notificationReference.NotificationTypeFieldName))
{
var fieldInfo = fields.First(f => f.Name == notificationReference.NotificationTypeFieldName);
Expand Down
12 changes: 6 additions & 6 deletions OpenRA.Mods.Common/Lint/CheckPalettes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ void Run(Action<string> emitError, Ruleset rules)
{
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields();
var fields = Utility.GetFields(traitInfo.GetType());
foreach (var field in fields)
{
var paletteReference = field.GetCustomAttributes<PaletteReferenceAttribute>(true).FirstOrDefault();
var paletteReference = Utility.GetCustomAttributes<PaletteReferenceAttribute>(field, true).FirstOrDefault();
if (paletteReference == null)
continue;

Expand Down Expand Up @@ -82,10 +82,10 @@ void Run(Action<string> emitError, Ruleset rules)
if (projectileInfo == null)
continue;

var fields = projectileInfo.GetType().GetFields();
var fields = Utility.GetFields(projectileInfo.GetType());
foreach (var field in fields)
{
var paletteReference = field.GetCustomAttributes<PaletteReferenceAttribute>(true).FirstOrDefault();
var paletteReference = Utility.GetCustomAttributes<PaletteReferenceAttribute>(field, true).FirstOrDefault();
if (paletteReference == null)
continue;

Expand Down Expand Up @@ -126,10 +126,10 @@ void GetPalettes(Ruleset rules, List<string> palettes, List<string> playerPalett
var tilesetPalettes = new List<(string Tileset, string PaletteName)>();
foreach (var traitInfo in worldActorInfo.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields();
var fields = Utility.GetFields(traitInfo.GetType());
foreach (var field in fields)
{
var paletteDefinition = field.GetCustomAttributes<PaletteDefinitionAttribute>(true).FirstOrDefault();
var paletteDefinition = Utility.GetCustomAttributes<PaletteDefinitionAttribute>(field, true).FirstOrDefault();
if (paletteDefinition == null)
continue;

Expand Down
8 changes: 4 additions & 4 deletions OpenRA.Mods.Common/Lint/CheckSequences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ void ILintSequencesPass.Run(Action<string> emitError, Action<string> emitWarning
var traitName = traitInfo.GetType().Name;
traitName = traitName.Remove(traitName.Length - 4);

var fields = traitInfo.GetType().GetFields();
var fields = Utility.GetFields(traitInfo.GetType());
foreach (var field in fields)
{
var sequenceReference = field.GetCustomAttributes<SequenceReferenceAttribute>(true).FirstOrDefault();
var sequenceReference = Utility.GetCustomAttributes<SequenceReferenceAttribute>(field, true).FirstOrDefault();
if (sequenceReference == null)
continue;

Expand Down Expand Up @@ -103,10 +103,10 @@ void ILintSequencesPass.Run(Action<string> emitError, Action<string> emitWarning
if (projectileInfo == null)
continue;

var fields = projectileInfo.GetType().GetFields();
var fields = Utility.GetFields(projectileInfo.GetType());
foreach (var field in fields)
{
var sequenceReference = field.GetCustomAttributes<SequenceReferenceAttribute>(true).FirstOrDefault();
var sequenceReference = Utility.GetCustomAttributes<SequenceReferenceAttribute>(field, true).FirstOrDefault();
if (sequenceReference == null)
continue;

Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Lint/CheckSyncAnnotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ static bool AnyTypeMemberIsSynced(Type type)
const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
while (type != null)
{
if (((MemberInfo[])type.GetFields(Flags)).Concat(type.GetProperties(Flags)).Any(x => x.HasAttribute<SyncAttribute>()))
if (((MemberInfo[])type.GetFields(Flags)).Concat(type.GetProperties(Flags)).Any(x => Utility.HasAttribute<SyncAttribute>(x)))
return true;
type = type.BaseType;
}
Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Lint/CheckTraitLocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ void ILintRulesPass.Run(Action<string> emitError, Action<string> emitWarning, Mo
{
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
{
var traitLocation = traitInfo.GetType().GetCustomAttributes<TraitLocationAttribute>(true).FirstOrDefault();
var traitLocation = Utility.GetCustomAttributes<TraitLocationAttribute>(traitInfo.GetType(), true).FirstOrDefault();
if (traitLocation == null)
continue;

Expand Down
8 changes: 4 additions & 4 deletions OpenRA.Mods.Common/Lint/CheckTranslationReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData
{
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields();
var fields = Utility.GetFields(traitInfo.GetType());
foreach (var field in fields)
{
var translationReference = field.GetCustomAttributes<TranslationReferenceAttribute>(true).FirstOrDefault();
var translationReference = Utility.GetCustomAttributes<TranslationReferenceAttribute>(field, true).FirstOrDefault();
if (translationReference == null)
continue;

Expand Down Expand Up @@ -72,7 +72,7 @@ void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData

foreach (var modType in modData.ObjectCreator.GetTypes())
{
foreach (var fieldInfo in modType.GetFields(Binding).Where(m => m.HasAttribute<TranslationReferenceAttribute>()))
foreach (var fieldInfo in modType.GetFields(Binding).Where(m => Utility.HasAttribute<TranslationReferenceAttribute>(m)))
{
if (fieldInfo.FieldType != typeof(string))
emitError($"Translation attribute on non string field {fieldInfo.Name}.");
Expand All @@ -87,7 +87,7 @@ void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData
if (!translation.HasMessage(key))
emitError($"{key} not present in {language} translation.");

var translationReference = fieldInfo.GetCustomAttributes<TranslationReferenceAttribute>(true)[0];
var translationReference = Utility.GetCustomAttributes<TranslationReferenceAttribute>(fieldInfo, true)[0];
if (translationReference.RequiredVariableNames != null && translationReference.RequiredVariableNames.Length > 0)
referencedVariablesPerKey.GetOrAdd(key, translationReference.RequiredVariableNames);

Expand Down
4 changes: 2 additions & 2 deletions OpenRA.Mods.Common/Lint/CheckVoiceReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void Run(Action<string> emitError, Ruleset rules)
{
foreach (var traitInfo in actorInfo.Value.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields().Where(f => f.HasAttribute<VoiceSetReferenceAttribute>());
var fields = Utility.GetFields(traitInfo.GetType()).Where(f => Utility.HasAttribute<VoiceSetReferenceAttribute>(f));
foreach (var field in fields)
{
var voiceSets = LintExts.GetFieldValues(traitInfo, field);
Expand All @@ -57,7 +57,7 @@ void CheckVoices(ActorInfo actorInfo, Action<string> emitError, Ruleset rules, s

foreach (var traitInfo in actorInfo.TraitInfos<TraitInfo>())
{
var fields = traitInfo.GetType().GetFields().Where(f => f.HasAttribute<VoiceReferenceAttribute>());
var fields = Utility.GetFields(traitInfo.GetType()).Where(f => Utility.HasAttribute<VoiceReferenceAttribute>(f));
foreach (var field in fields)
{
var voices = LintExts.GetFieldValues(traitInfo, field);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void IUtilityCommand.Run(Utility utility, string[] args)
var interfaces = implementingType.GetInterfaces();
foreach (var interfaceType in interfaces)
{
if (!interfaceType.HasAttribute<RequireExplicitImplementationAttribute>())
if (!Utility.HasAttribute<RequireExplicitImplementationAttribute>(interfaceType))
continue;

var interfaceMembers = interfaceType.GetMembers();
Expand Down
6 changes: 3 additions & 3 deletions OpenRA.Mods.Common/UtilityCommands/CreateManPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ void IUtilityCommand.Run(Utility utility, string[] args)
sections.Add("Launch", new LaunchArguments(new Arguments(Array.Empty<string>())));
foreach (var section in sections.OrderBy(s => s.Key))
{
var fields = section.Value.GetType().GetFields();
var fields = Utility.GetFields(section.Value.GetType());
foreach (var field in fields)
{
if (!field.HasAttribute<DescAttribute>())
if (!Utility.HasAttribute<DescAttribute>(field))
continue;

Console.WriteLine(".TP");
Expand All @@ -56,7 +56,7 @@ void IUtilityCommand.Run(Utility utility, string[] args)
else
Console.WriteLine();

var lines = field.GetCustomAttributes<DescAttribute>(false).SelectMany(d => d.Lines);
var lines = Utility.GetCustomAttributes<DescAttribute>(field, false).SelectMany(d => d.Lines);
foreach (var line in lines)
Console.WriteLine(line);
}
Expand Down
12 changes: 6 additions & 6 deletions OpenRA.Mods.Common/UtilityCommands/ExtractEmmyLuaAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ static void WriteGlobals(IEnumerable<Type> globalTables)
{
foreach (var t in globalTables)
{
var name = t.GetCustomAttributes<ScriptGlobalAttribute>(true).First().Name;
var name = Utility.GetCustomAttributes<ScriptGlobalAttribute>(t, true).First().Name;
Console.WriteLine("---Global variable provided by the game scripting engine.");

foreach (var obsolete in t.GetCustomAttributes(false).OfType<ObsoleteAttribute>())
Expand All @@ -191,9 +191,9 @@ static void WriteGlobals(IEnumerable<Type> globalTables)

var body = "";

if (member.HasAttribute<DescAttribute>())
if (Utility.HasAttribute<DescAttribute>(member))
{
var lines = member.GetCustomAttributes<DescAttribute>(true).First().Lines;
var lines = Utility.GetCustomAttributes<DescAttribute>(member, true).First().Lines;
foreach (var line in lines)
Console.WriteLine($" --- {line}");
}
Expand Down Expand Up @@ -258,11 +258,11 @@ static void WriteScriptProperties(Type type, IEnumerable<Type> implementingTypes
{
Console.WriteLine();

var isActivity = memberInfo.HasAttribute<ScriptActorPropertyActivityAttribute>();
var isActivity = Utility.HasAttribute<ScriptActorPropertyActivityAttribute>(memberInfo);

if (memberInfo.HasAttribute<DescAttribute>())
if (Utility.HasAttribute<DescAttribute>(memberInfo))
{
var lines = memberInfo.GetCustomAttributes<DescAttribute>(true).First().Lines;
var lines = Utility.GetCustomAttributes<DescAttribute>(memberInfo, true).First().Lines;
foreach (var line in lines)
Console.WriteLine($" --- {line}");
}
Expand Down
Loading

0 comments on commit 9476fc9

Please sign in to comment.