Updated RuleDependencyChecker to match the new behavior of RuleDependencyProcessor

This commit is contained in:
Sam Harwell 2013-02-25 14:37:45 -06:00
parent 5e49211455
commit 6b7eb5dad4
2 changed files with 323 additions and 22 deletions

@ -1 +1 @@
Subproject commit dde8d7b3178c9654db7eccbbf6c629b4ed2e7fd4
Subproject commit 8dedc2f0957b5f242059d750b3148364c49ffc4a

View File

@ -33,6 +33,8 @@ using System.Reflection;
using System.Security;
using System.Text;
using Antlr4.Runtime;
using Antlr4.Runtime.Atn;
using Antlr4.Runtime.Misc;
using Sharpen;
using Sharpen.Annotation;
using Sharpen.Logging;
@ -54,9 +56,7 @@ namespace Antlr4.Runtime.Misc
{
return;
}
IList<Type> typesToCheck = new List<Type>();
typesToCheck.AddItem(dependentClass);
Sharpen.Collections.AddAll(typesToCheck, dependentClass.GetDeclaredClasses());
IList<Type> typesToCheck = GetTypesToCheck(dependentClass);
foreach (Type clazz in typesToCheck)
{
if (IsChecked(clazz))
@ -69,10 +69,49 @@ namespace Antlr4.Runtime.Misc
{
continue;
}
IDictionary<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>> recognizerDependencies
= new Dictionary<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>>();
foreach (Tuple<RuleDependency, IAnnotatedElement> dependency in dependencies)
{
Type recognizerType = dependency.Item1.Recognizer();
IList<Tuple<RuleDependency, IAnnotatedElement>> list = recognizerDependencies.Get
(recognizerType);
if (list == null)
{
list = new List<Tuple<RuleDependency, IAnnotatedElement>>();
recognizerDependencies.Put(recognizerType, list);
}
list.AddItem(dependency);
}
foreach (KeyValuePair<Type, IList<Tuple<RuleDependency, IAnnotatedElement>>> entry
in recognizerDependencies.EntrySet())
{
//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());
}
}
private static IList<Type> GetTypesToCheck<_T0>(Type<_T0> 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)
{
if (!result.AddItem(clazz))
{
return;
}
foreach (Type declared in clazz.GetDeclaredClasses())
{
GetTypesToCheck(declared, result);
}
}
private static bool IsChecked<_T0>(Type<_T0> clazz)
{
lock (checkedTypes)
@ -90,40 +129,160 @@ namespace Antlr4.Runtime.Misc
}
private static void CheckDependencies<_T0>(IList<Tuple<RuleDependency, IAnnotatedElement
>> dependencies, Type<_T0> recognizerClass) where _T0:Recognizer<object, object
>> dependencies, Type<_T0> recognizerType) where _T0:Recognizer<object, object
>
{
string[] ruleNames = GetRuleNames(recognizerClass);
int[] ruleVersions = GetRuleVersions(recognizerClass, ruleNames);
StringBuilder incompatible = new StringBuilder();
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)
{
if (!recognizerClass.IsAssignableFrom(dependency.Item1.Recognizer()))
if (!dependency.Item1.Recognizer().IsAssignableFrom(recognizerType))
{
continue;
}
if (dependency.Item1.Rule() < 0 || dependency.Item1.Rule() >= ruleVersions.Length)
// this is the rule in the dependency set with the highest version number
int effectiveRule = dependency.Item1.Rule();
if (effectiveRule < 0 || effectiveRule >= ruleVersions.Length)
{
incompatible.Append(string.Format("Element %s dependent on unknown rule %d@%d in %s\n"
, dependency.Item2.ToString(), dependency.Item1.Rule(), dependency.Item1.Version
(), dependency.Item1.Recognizer().Name));
string message = string.Format("Rule dependency on unknown rule %d@%d in %s%n", dependency
.Item1.Rule(), dependency.Item1.Version(), dependency.Item1.Recognizer().ToString
());
errors.Append(message);
continue;
}
else
EnumSet<Dependents> dependents = EnumSet.Of(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 (ruleVersions[dependency.Item1.Rule()] != dependency.Item1.Version())
BitSet parents = relations.parents[dependency.Item1.Rule()];
for (int parent = parents.NextSetBit(0); parent >= 0; parent = parents.NextSetBit
(parent + 1))
{
incompatible.Append(string.Format("Element %s dependent on rule %s@%d (found @%d) in %s\n"
, dependency.Item2.ToString(), ruleNames[dependency.Item1.Rule()], dependency
.Item1.Version(), ruleVersions[dependency.Item1.Rule()], dependency.Item1.Recognizer
().Name));
if (parent < 0 || parent >= ruleVersions.Length || checked.Get(parent))
{
continue;
}
checked.Set(parent);
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
, parent, "parent");
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
if (dependents.Contains(Dependents.Children))
{
BitSet children = relations.children[dependency.Item1.Rule()];
for (int child = children.NextSetBit(0); child >= 0; child = children.NextSetBit(
child + 1))
{
if (child < 0 || child >= ruleVersions.Length || checked.Get(child))
{
continue;
}
checked.Set(child);
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
, child, "child");
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
if (dependents.Contains(Dependents.Ancestors))
{
BitSet ancestors = relations.GetAncestors(dependency.Item1.Rule());
for (int ancestor = ancestors.NextSetBit(0); ancestor >= 0; ancestor = ancestors.
NextSetBit(ancestor + 1))
{
if (ancestor < 0 || ancestor >= ruleVersions.Length || checked.Get(ancestor))
{
continue;
}
checked.Set(ancestor);
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
, ancestor, "ancestor");
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
if (dependents.Contains(Dependents.Descendants))
{
BitSet descendants = relations.GetDescendants(dependency.Item1.Rule());
for (int descendant = descendants.NextSetBit(0); descendant >= 0; descendant = descendants
.NextSetBit(descendant + 1))
{
if (descendant < 0 || descendant >= ruleVersions.Length || checked.Get(descendant
))
{
continue;
}
checked.Set(descendant);
int required = CheckDependencyVersion(errors, dependency, ruleNames, ruleVersions
, descendant, "descendant");
highestRequiredDependency = Math.Max(highestRequiredDependency, required);
}
}
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());
errors.Append(message);
}
}
if (incompatible.Length != 0)
if (errors.Length > 0)
{
throw new InvalidOperationException(incompatible.ToString());
throw new InvalidOperationException(errors.ToString());
}
MarkChecked(recognizerClass);
MarkChecked(recognizerType);
}
private static readonly ISet<Dependents> ImplementedDependents = EnumSet.Of(Dependents
.Self, Dependents.Parents, Dependents.Children, Dependents.Ancestors, Dependents
.Descendants);
private static void ReportUnimplementedDependents(StringBuilder errors, Tuple<RuleDependency
, IAnnotatedElement> dependency, EnumSet<Dependents> dependents)
{
EnumSet<Dependents> unimplemented = dependents.Clone();
unimplemented.RemoveAll(ImplementedDependents);
if (!unimplemented.IsEmpty())
{
string message = string.Format("Cannot validate the following dependents of rule %d: %s%n"
, dependency.Item1.Rule(), unimplemented);
errors.Append(message);
}
}
private static int CheckDependencyVersion(StringBuilder errors, Tuple<RuleDependency
, IAnnotatedElement> dependency, string[] ruleNames, int[] ruleVersions, int
relatedRule, string relation)
{
string ruleName = ruleNames[dependency.Item1.Rule()];
string path;
if (relation == null)
{
path = ruleName;
}
else
{
string mismatchedRuleName = ruleNames[relatedRule];
path = string.Format("rule %s (%s of %s)", mismatchedRuleName, relation, ruleName
);
}
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
());
errors.Append(message);
}
return actualVersion;
}
private static int[] GetRuleVersions<_T0>(Type<_T0> recognizerClass, string[] ruleNames
@ -320,6 +479,148 @@ namespace Antlr4.Runtime.Misc
}
}
private static RuleDependencyChecker.RuleRelations ExtractRuleRelations<_T0>(Type
<_T0> recognizer) where _T0:Recognizer<object, object>
{
string serializedATN = GetSerializedATN(recognizer);
if (serializedATN == null)
{
return null;
}
ATN atn = ATNSimulator.Deserialize(serializedATN.ToCharArray());
RuleDependencyChecker.RuleRelations relations = new RuleDependencyChecker.RuleRelations
(atn.ruleToStartState.Length);
foreach (ATNState state in atn.states)
{
if (!state.epsilonOnlyTransitions)
{
continue;
}
foreach (Transition transition in state.GetTransitions())
{
if (transition.TransitionType != TransitionType.Rule)
{
continue;
}
RuleTransition ruleTransition = (RuleTransition)transition;
relations.AddRuleInvocation(state.ruleIndex, ruleTransition.target.ruleIndex);
}
}
return relations;
}
private static string GetSerializedATN<_T0>(Type<_T0> 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;
}
}
private sealed class RuleRelations
{
private readonly BitSet[] parents;
private readonly BitSet[] children;
public RuleRelations(int ruleCount)
{
parents = new BitSet[ruleCount];
for (int i = 0; i < ruleCount; i++)
{
parents[i] = new BitSet();
}
children = new BitSet[ruleCount];
for (int i_1 = 0; i_1 < ruleCount; i_1++)
{
children[i_1] = new BitSet();
}
}
public bool AddRuleInvocation(int caller, int callee)
{
if (caller < 0)
{
// tokens rule
return false;
}
if (children[caller].Get(callee))
{
// already added
return false;
}
children[caller].Set(callee);
parents[callee].Set(caller);
return true;
}
public BitSet GetAncestors(int rule)
{
BitSet ancestors = new BitSet();
ancestors.Or(parents[rule]);
while (true)
{
int cardinality = ancestors.Cardinality();
for (int i = ancestors.NextSetBit(0); i >= 0; i = ancestors.NextSetBit(i + 1))
{
ancestors.Or(parents[i]);
}
if (ancestors.Cardinality() == cardinality)
{
// nothing changed
break;
}
}
return ancestors;
}
public BitSet GetDescendants(int rule)
{
BitSet descendants = new BitSet();
descendants.Or(children[rule]);
while (true)
{
int cardinality = descendants.Cardinality();
for (int i = descendants.NextSetBit(0); i >= 0; i = descendants.NextSetBit(i + 1))
{
descendants.Or(children[i]);
}
if (descendants.Cardinality() == cardinality)
{
// nothing changed
break;
}
}
return descendants;
}
}
private RuleDependencyChecker()
{
}