Implement runtime rule dependency checking

This commit is contained in:
Sam Harwell 2013-02-25 15:09:31 -06:00
parent 1bb1f23579
commit 590ae4fe66
2 changed files with 117 additions and 217 deletions

View File

@ -141,7 +141,7 @@
<Compile Include="Misc\NotNullAttribute.cs" />
<None Include="Misc\OrderedHashSet`1.cs" />
<Compile Include="Misc\ParseCanceledException.cs" />
<None Include="Misc\RuleDependencyChecker.cs" />
<Compile Include="Misc\RuleDependencyChecker.cs" />
<None Include="Misc\RuleDependencyProcessor.cs" />
<None Include="Misc\TestRig.cs" />
<Compile Include="Misc\Utils.cs" />

View File

@ -36,21 +36,20 @@ using Antlr4.Runtime;
using Antlr4.Runtime.Atn;
using Antlr4.Runtime.Misc;
using Sharpen;
using Sharpen.Annotation;
using Sharpen.Logging;
using Sharpen.Reflect;
namespace Antlr4.Runtime.Misc
{
/// <author>Sam Harwell</author>
public class RuleDependencyChecker
{
#if false
private static readonly Logger Logger = Logger.GetLogger(typeof(Antlr4.Runtime.Misc.RuleDependencyChecker
).FullName);
#endif
private static readonly ISet<Type> checkedTypes = new HashSet<Type>();
public static void CheckDependencies<_T0>(Type<_T0> dependentClass)
public static void CheckDependencies(Type dependentClass)
{
if (IsChecked(dependentClass))
{
@ -63,56 +62,56 @@ namespace Antlr4.Runtime.Misc
{
continue;
}
IList<Tuple<RuleDependency, IAnnotatedElement>> dependencies = GetDependencies(clazz
IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>> dependencies = GetDependencies(clazz
);
if (dependencies.Count == 0)
{
continue;
}
IDictionary<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>> recognizerDependencies
= new Dictionary<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>>();
foreach (Tuple<RuleDependency, IAnnotatedElement> dependency in dependencies)
IDictionary<Type, IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>>> recognizerDependencies
= new Dictionary<Type, IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>>>();
foreach (Tuple<RuleDependencyAttribute, ICustomAttributeProvider> dependency in dependencies)
{
Type recognizerType = dependency.Item1.Recognizer();
IList<Tuple<RuleDependency, IAnnotatedElement>> list = recognizerDependencies.Get
Type recognizerType = dependency.Item1.Recognizer;
IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>> list = recognizerDependencies.Get
(recognizerType);
if (list == null)
{
list = new List<Tuple<RuleDependency, IAnnotatedElement>>();
list = new List<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>>();
recognizerDependencies.Put(recognizerType, list);
}
list.AddItem(dependency);
list.Add(dependency);
}
foreach (KeyValuePair<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>> entry
in recognizerDependencies.EntrySet())
foreach (KeyValuePair<Type, IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>>> entry
in recognizerDependencies)
{
//processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format("ANTLR 4: Validating %d dependencies on rules in %s.", entry.getValue().size(), entry.getKey().toString()));
CheckDependencies(entry.Value, entry.Key);
}
CheckDependencies(dependencies, dependencies[0].Item1.Recognizer());
CheckDependencies(dependencies, dependencies[0].Item1.Recognizer);
}
}
private static IList<Type> GetTypesToCheck<_T0>(Type<_T0> clazz)
private static IList<Type> GetTypesToCheck(Type clazz)
{
ISet<Type> result = new HashSet<Type>();
GetTypesToCheck(clazz, result);
return new List<Type>(result);
}
private static void GetTypesToCheck<_T0>(Type<_T0> clazz, ISet<Type> result)
private static void GetTypesToCheck(Type clazz, ISet<Type> result)
{
if (!result.AddItem(clazz))
if (!result.Add(clazz))
{
return;
}
foreach (Type declared in clazz.GetDeclaredClasses())
foreach (Type declared in clazz.GetNestedTypes())
{
GetTypesToCheck(declared, result);
}
}
private static bool IsChecked<_T0>(Type<_T0> clazz)
private static bool IsChecked(Type clazz)
{
lock (checkedTypes)
{
@ -120,48 +119,46 @@ namespace Antlr4.Runtime.Misc
}
}
private static void MarkChecked<_T0>(Type<_T0> clazz)
private static void MarkChecked(Type clazz)
{
lock (checkedTypes)
{
checkedTypes.AddItem(clazz);
checkedTypes.Add(clazz);
}
}
private static void CheckDependencies<_T0>(IList<Tuple<RuleDependency, IAnnotatedElement
>> dependencies, Type<_T0> recognizerType) where _T0:Recognizer<object, object
>
private static void CheckDependencies(IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider
>> dependencies, Type recognizerType)
{
string[] ruleNames = GetRuleNames(recognizerType);
int[] ruleVersions = GetRuleVersions(recognizerType, ruleNames);
RuleDependencyChecker.RuleRelations relations = ExtractRuleRelations(recognizerType
);
StringBuilder errors = new StringBuilder();
foreach (Tuple<RuleDependency, IAnnotatedElement> dependency in dependencies)
foreach (Tuple<RuleDependencyAttribute, ICustomAttributeProvider> dependency in dependencies)
{
if (!dependency.Item1.Recognizer().IsAssignableFrom(recognizerType))
if (!dependency.Item1.Recognizer.IsAssignableFrom(recognizerType))
{
continue;
}
// this is the rule in the dependency set with the highest version number
int effectiveRule = dependency.Item1.Rule();
int effectiveRule = dependency.Item1.Rule;
if (effectiveRule < 0 || effectiveRule >= ruleVersions.Length)
{
string message = string.Format("Rule dependency on unknown rule %d@%d in %s%n", dependency
.Item1.Rule(), dependency.Item1.Version(), dependency.Item1.Recognizer().ToString
.Item1.Rule, dependency.Item1.Version, dependency.Item1.Recognizer.ToString
());
errors.Append(message);
continue;
}
EnumSet<Dependents> dependents = EnumSet.Of(Dependents.Self, dependency.Item1.Dependents
());
Dependents dependents = Dependents.Self | dependency.Item1.Dependents;
ReportUnimplementedDependents(errors, dependency, dependents);
BitSet @checked = new BitSet();
int highestRequiredDependency = CheckDependencyVersion(errors, dependency, ruleNames
, ruleVersions, effectiveRule, null);
if (dependents.Contains(Dependents.Parents))
if (dependents.HasFlag(Dependents.Parents))
{
BitSet parents = relations.parents[dependency.Item1.Rule()];
BitSet parents = relations.parents[dependency.Item1.Rule];
for (int parent = parents.NextSetBit(0); parent >= 0; parent = parents.NextSetBit
(parent + 1))
{
@ -175,9 +172,9 @@ namespace Antlr4.Runtime.Misc
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
if (dependents.Contains(Dependents.Children))
if (dependents.HasFlag(Dependents.Children))
{
BitSet children = relations.children[dependency.Item1.Rule()];
BitSet children = relations.children[dependency.Item1.Rule];
for (int child = children.NextSetBit(0); child >= 0; child = children.NextSetBit(
child + 1))
{
@ -191,9 +188,9 @@ namespace Antlr4.Runtime.Misc
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
if (dependents.Contains(Dependents.Ancestors))
if (dependents.HasFlag(Dependents.Ancestors))
{
BitSet ancestors = relations.GetAncestors(dependency.Item1.Rule());
BitSet ancestors = relations.GetAncestors(dependency.Item1.Rule);
for (int ancestor = ancestors.NextSetBit(0); ancestor >= 0; ancestor = ancestors.
NextSetBit(ancestor + 1))
{
@ -207,9 +204,9 @@ namespace Antlr4.Runtime.Misc
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
if (dependents.Contains(Dependents.Descendants))
if (dependents.HasFlag(Dependents.Descendants))
{
BitSet descendants = relations.GetDescendants(dependency.Item1.Rule());
BitSet descendants = relations.GetDescendants(dependency.Item1.Rule);
for (int descendant = descendants.NextSetBit(0); descendant >= 0; descendant = descendants
.NextSetBit(descendant + 1))
{
@ -224,12 +221,12 @@ namespace Antlr4.Runtime.Misc
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
int declaredVersion = dependency.Item1.Version();
int declaredVersion = dependency.Item1.Version;
if (declaredVersion > highestRequiredDependency)
{
string message = string.Format("Rule dependency version mismatch: %s has maximum dependency version %d (expected %d) in %s%n"
, ruleNames[dependency.Item1.Rule()], highestRequiredDependency, declaredVersion
, dependency.Item1.Recognizer().ToString());
, ruleNames[dependency.Item1.Rule], highestRequiredDependency, declaredVersion
, dependency.Item1.Recognizer.ToString());
errors.Append(message);
}
}
@ -240,28 +237,28 @@ namespace Antlr4.Runtime.Misc
MarkChecked(recognizerType);
}
private static readonly ISet<Dependents> ImplementedDependents = EnumSet.Of(Dependents
.Self, Dependents.Parents, Dependents.Children, Dependents.Ancestors, Dependents
.Descendants);
private static readonly Dependents ImplementedDependents = Dependents
.Self | Dependents.Parents | Dependents.Children | Dependents.Ancestors | Dependents
.Descendants;
private static void ReportUnimplementedDependents(StringBuilder errors, Tuple<RuleDependency
, IAnnotatedElement> dependency, EnumSet<Dependents> dependents)
private static void ReportUnimplementedDependents(StringBuilder errors, Tuple<RuleDependencyAttribute
, ICustomAttributeProvider> dependency, Dependents dependents)
{
EnumSet<Dependents> unimplemented = dependents.Clone();
unimplemented.RemoveAll(ImplementedDependents);
if (!unimplemented.IsEmpty())
Dependents unimplemented = dependents;
unimplemented &= ~ImplementedDependents;
if (unimplemented != Dependents.None)
{
string message = string.Format("Cannot validate the following dependents of rule %d: %s%n"
, dependency.Item1.Rule(), unimplemented);
, dependency.Item1.Rule, unimplemented);
errors.Append(message);
}
}
private static int CheckDependencyVersion(StringBuilder errors, Tuple<RuleDependency
, IAnnotatedElement> dependency, string[] ruleNames, int[] ruleVersions, int
private static int CheckDependencyVersion(StringBuilder errors, Tuple<RuleDependencyAttribute
, ICustomAttributeProvider> dependency, string[] ruleNames, int[] ruleVersions, int
relatedRule, string relation)
{
string ruleName = ruleNames[dependency.Item1.Rule()];
string ruleName = ruleNames[dependency.Item1.Rule];
string path;
if (relation == null)
{
@ -273,76 +270,84 @@ namespace Antlr4.Runtime.Misc
path = string.Format("rule %s (%s of %s)", mismatchedRuleName, relation, ruleName
);
}
int declaredVersion = dependency.Item1.Version();
int declaredVersion = dependency.Item1.Version;
int actualVersion = ruleVersions[relatedRule];
if (actualVersion > declaredVersion)
{
string message = string.Format("Rule dependency version mismatch: %s has version %d (expected <= %d) in %s%n"
, path, actualVersion, declaredVersion, dependency.Item1.Recognizer().ToString
, path, actualVersion, declaredVersion, dependency.Item1.Recognizer.ToString
());
errors.Append(message);
}
return actualVersion;
}
private static int[] GetRuleVersions<_T0>(Type<_T0> recognizerClass, string[] ruleNames
) where _T0:Recognizer<object, object>
private static int[] GetRuleVersions(Type recognizerClass, string[] ruleNames
)
{
int[] versions = new int[ruleNames.Length];
FieldInfo[] fields = recognizerClass.GetFields();
foreach (FieldInfo field in fields)
{
bool isStatic = (field.GetModifiers() & Modifier.Static) != 0;
bool isStatic = field.IsStatic;
bool isInteger = field.FieldType == typeof(int);
if (isStatic && isInteger && field.Name.StartsWith("RULE_"))
{
try
{
string name = Sharpen.Runtime.Substring(field.Name, "RULE_".Length);
string name = field.Name.Substring("RULE_".Length);
if (name.Length == 0 || !System.Char.IsLower(name[0]))
{
continue;
}
int index = field.GetInt(null);
int index = (int)field.GetValue(null);
if (index < 0 || index >= versions.Length)
{
object[] @params = new object[] { index, field.Name, recognizerClass.Name };
#if false
Logger.Log(Level.Warning, "Rule index {0} for rule ''{1}'' out of bounds for recognizer {2}."
, @params);
#endif
continue;
}
MethodInfo ruleMethod = GetRuleMethod(recognizerClass, name);
if (ruleMethod == null)
{
object[] @params = new object[] { name, recognizerClass.Name };
#if false
Logger.Log(Level.Warning, "Could not find rule method for rule ''{0}'' in recognizer {1}."
, @params);
#endif
continue;
}
RuleVersion ruleVersion = ruleMethod.GetAnnotation<RuleVersion>();
int version = ruleVersion != null ? ruleVersion.Value() : 0;
RuleVersionAttribute ruleVersion = ruleMethod.GetCustomAttribute<RuleVersionAttribute>();
int version = ruleVersion != null ? ruleVersion.Version : 0;
versions[index] = version;
}
catch (ArgumentException ex)
{
#if false
Logger.Log(Level.Warning, null, ex);
#endif
}
catch (MemberAccessException ex)
{
#if false
Logger.Log(Level.Warning, null, ex);
#endif
}
}
}
return versions;
}
private static MethodInfo GetRuleMethod<_T0>(Type<_T0> recognizerClass, string name
) where _T0:Recognizer<object, object>
private static MethodInfo GetRuleMethod(Type recognizerClass, string name
)
{
MethodInfo[] declaredMethods = recognizerClass.GetMethods();
foreach (MethodInfo method in declaredMethods)
{
if (method.Name.Equals(name) && method.IsAnnotationPresent(typeof(RuleVersion)))
if (method.Name.Equals(name) && method.GetCustomAttribute<RuleVersionAttribute>() != null)
{
return method;
}
@ -350,137 +355,54 @@ namespace Antlr4.Runtime.Misc
return null;
}
private static string[] GetRuleNames<_T0>(Type<_T0> recognizerClass) where _T0:Recognizer
<object, object>
private static string[] GetRuleNames(Type recognizerClass)
{
try
{
FieldInfo ruleNames = recognizerClass.GetField("ruleNames");
return (string[])ruleNames.GetValue(null);
}
catch (NoSuchFieldException ex)
{
Logger.Log(Level.Warning, null, ex);
}
catch (SecurityException ex)
{
Logger.Log(Level.Warning, null, ex);
}
catch (ArgumentException ex)
{
Logger.Log(Level.Warning, null, ex);
}
catch (MemberAccessException ex)
{
Logger.Log(Level.Warning, null, ex);
}
return new string[0];
FieldInfo ruleNames = recognizerClass.GetField("ruleNames");
return (string[])ruleNames.GetValue(null);
}
public static IList<Tuple<RuleDependency, IAnnotatedElement>> GetDependencies<_T0
>(Type<_T0> clazz)
public static IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>> GetDependencies(Type clazz)
{
IList<Tuple<RuleDependency, IAnnotatedElement>> result = new List<Tuple<RuleDependency
, IAnnotatedElement>>();
IList<ElementType> supportedTarget = Arrays.AsList(typeof(RuleDependency).GetAnnotation
<Target>().Value());
foreach (ElementType target in supportedTarget)
IList<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>> result = new List<Tuple<RuleDependencyAttribute
, ICustomAttributeProvider>>();
GetElementDependencies(clazz, result);
foreach (ConstructorInfo ctor in clazz.GetConstructors(BindingFlags.DeclaredOnly))
{
switch (target)
{
case ElementType.Type:
{
if (!clazz.IsAnnotation())
{
GetElementDependencies(clazz, result);
}
break;
}
case ElementType.AnnotationType:
{
if (!clazz.IsAnnotation())
{
GetElementDependencies(clazz, result);
}
break;
}
case ElementType.Constructor:
{
foreach (Constructor<object> ctor in clazz.GetDeclaredConstructors())
{
GetElementDependencies(ctor, result);
}
break;
}
case ElementType.Field:
{
foreach (FieldInfo field in Sharpen.Runtime.GetDeclaredFields(clazz))
{
GetElementDependencies(field, result);
}
break;
}
case ElementType.LocalVariable:
{
System.Console.Error.WriteLine("Runtime rule dependency checking is not supported for local variables."
);
break;
}
case ElementType.Method:
{
foreach (MethodInfo method in Sharpen.Runtime.GetDeclaredMethods(clazz))
{
GetElementDependencies(method, result);
}
break;
}
case ElementType.Package:
{
// package is not a subset of class, so nothing to do here
break;
}
case ElementType.Parameter:
{
System.Console.Error.WriteLine("Runtime rule dependency checking is not supported for parameters."
);
break;
}
}
GetElementDependencies(ctor, result);
foreach (ParameterInfo parameter in ctor.GetParameters())
GetElementDependencies(parameter, result);
}
foreach (FieldInfo field in clazz.GetFields(BindingFlags.DeclaredOnly))
{
GetElementDependencies(field, result);
}
foreach (MethodInfo method in clazz.GetMethods(BindingFlags.DeclaredOnly))
{
GetElementDependencies(method, result);
if (method.ReturnParameter != null)
GetElementDependencies(method.ReturnParameter, result);
foreach (ParameterInfo parameter in method.GetParameters())
GetElementDependencies(parameter, result);
}
return result;
}
private static void GetElementDependencies(IAnnotatedElement annotatedElement, IList
<Tuple<RuleDependency, IAnnotatedElement>> result)
private static void GetElementDependencies(ICustomAttributeProvider annotatedElement, IList
<Tuple<RuleDependencyAttribute, ICustomAttributeProvider>> result)
{
RuleDependency dependency = annotatedElement.GetAnnotation<RuleDependency>();
if (dependency != null)
foreach (RuleDependencyAttribute dependency in annotatedElement.GetCustomAttributes(typeof(RuleDependencyAttribute), true))
{
result.AddItem(Tuple.Create(dependency, annotatedElement));
}
RuleDependencies dependencies = annotatedElement.GetAnnotation<RuleDependencies>(
);
if (dependencies != null)
{
foreach (RuleDependency d in dependencies.Value())
{
if (d != null)
{
result.AddItem(Tuple.Create(d, annotatedElement));
}
}
result.Add(Tuple.Create(dependency, annotatedElement));
}
}
private static RuleDependencyChecker.RuleRelations ExtractRuleRelations<_T0>(Type
<_T0> recognizer) where _T0:Recognizer<object, object>
private static RuleDependencyChecker.RuleRelations ExtractRuleRelations(Type
recognizer)
{
string serializedATN = GetSerializedATN(recognizer);
if (serializedATN == null)
@ -509,45 +431,23 @@ namespace Antlr4.Runtime.Misc
return relations;
}
private static string GetSerializedATN<_T0>(Type<_T0> recognizerClass)
private static string GetSerializedATN(Type recognizerClass)
{
try
{
FieldInfo serializedAtnField = Sharpen.Runtime.GetDeclaredField(recognizerClass,
"_serializedATN");
if (Modifier.IsStatic(serializedAtnField.GetModifiers()))
{
return (string)serializedAtnField.GetValue(null);
}
return null;
}
catch (NoSuchFieldException)
{
if (recognizerClass.BaseType != null)
{
return GetSerializedATN(recognizerClass.BaseType);
}
return null;
}
catch (SecurityException)
{
return null;
}
catch (ArgumentException)
{
return null;
}
catch (MemberAccessException)
{
return null;
}
FieldInfo serializedAtnField = recognizerClass.GetField("_serializedATN", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (serializedAtnField != null)
return (string)serializedAtnField.GetValue(null);
if (recognizerClass.BaseType != null)
return GetSerializedATN(recognizerClass.BaseType);
return null;
}
private sealed class RuleRelations
{
private readonly BitSet[] parents;
public readonly BitSet[] parents;
private readonly BitSet[] children;
public readonly BitSet[] children;
public RuleRelations(int ruleCount)
{