77import java .io .FileNotFoundException ;
88import java .io .FileReader ;
99import java .io .PrintWriter ;
10+ import java .lang .reflect .Field ;
1011import java .util .ArrayList ;
12+ import java .util .EnumSet ;
13+ import java .util .HashSet ;
14+ import java .util .Iterator ;
15+ import java .util .LinkedHashMap ;
1116import java .util .LinkedHashSet ;
1217import java .util .List ;
18+ import java .util .Map ;
1319import java .util .Set ;
1420
1521import koala .dynamicjava .interpreter .NodeProperties ;
1622import koala .dynamicjava .interpreter .error .ExecutionError ;
1723import koala .dynamicjava .parser .wrapper .JavaCCParser ;
1824import koala .dynamicjava .parser .wrapper .ParseError ;
1925import koala .dynamicjava .tree .CompilationUnit ;
26+ import koala .dynamicjava .tree .IdentifierToken ;
2027import koala .dynamicjava .tree .Node ;
2128import koala .dynamicjava .tree .SourceInfo ;
2229import koala .dynamicjava .tree .TypeDeclaration ;
2330import koala .dynamicjava .tree .visitor .DepthFirstVisitor ;
2431import edu .rice .cs .dynamicjava .Options ;
2532import edu .rice .cs .dynamicjava .interpreter .*;
33+ import edu .rice .cs .dynamicjava .symbol .DJClass ;
2634import edu .rice .cs .dynamicjava .symbol .ExtendedTypeSystem ;
35+ import edu .rice .cs .dynamicjava .symbol .Function ;
2736import edu .rice .cs .dynamicjava .symbol .JLSTypeSystem ;
2837import edu .rice .cs .dynamicjava .symbol .Library ;
2938import edu .rice .cs .dynamicjava .symbol .SymbolUtil ;
3039import edu .rice .cs .dynamicjava .symbol .TreeLibrary ;
3140import edu .rice .cs .dynamicjava .symbol .TypeSystem ;
41+ import edu .rice .cs .dynamicjava .symbol .Variable ;
42+ import edu .rice .cs .dynamicjava .symbol .type .Type ;
3243import edu .rice .cs .plt .collect .UnindexedRelation ;
3344import edu .rice .cs .plt .collect .Relation ;
3445import edu .rice .cs .plt .io .IOUtil ;
3950import edu .rice .cs .plt .reflect .PathClassLoader ;
4051import edu .rice .cs .plt .text .ArgumentParser ;
4152import edu .rice .cs .plt .text .TextUtil ;
53+ import edu .rice .cs .plt .tuple .Option ;
4254import edu .rice .cs .plt .tuple .Pair ;
4355
4456public class SourceChecker {
4557
4658 private final Options _opt ;
4759 private final boolean _quiet ;
4860 private int _statusCount ;
61+ private Iterable <CompilationUnit > _processed ;
4962
5063 public SourceChecker (Options opt , boolean quiet ) {
5164 _opt = opt ;
5265 _quiet = quiet ;
5366 _statusCount = 0 ;
67+ _processed = IterUtil .empty ();
5468 }
5569
70+ public Iterable <CompilationUnit > processed () { return _processed ; }
71+
5672 public void check (File ... sources ) throws InterpreterException {
5773 check (IterUtil .asIterable (sources ), IterUtil .<File >empty ());
5874 }
@@ -64,6 +80,7 @@ public void check(Iterable<? extends File> sources) throws InterpreterException
6480 public void check (Iterable <? extends File > sources , Iterable <? extends File > classPath )
6581 throws InterpreterException {
6682 Iterable <CompilationUnit > tree = parse (sources );
83+ _processed = IterUtil .compose (_processed , tree );
6784 TypeContext context = makeContext (tree , classPath );
6885 Relation <TypeDeclaration , ClassChecker > decls = extractDeclarations (tree , context );
6986 initializeClassSignatures (decls );
@@ -203,20 +220,6 @@ public void run(Node node) {
203220 return CompositeException .make (IterUtil .map (result , CheckerException .FACTORY ));
204221 }
205222
206- /**
207- * Append the given prefix to all properties of the given AST (the root node and its children),
208- * effectively hiding them so that the program can be re-processed.
209- */
210- private static void archiveProperties (Node ast , final String prefix ) {
211- new DepthFirstVisitor () {
212- public void run (Node node ) {
213- node .archiveProperties (prefix );
214- super .run (node );
215- }
216- }.run (ast );
217- }
218-
219-
220223 /**
221224 * A DepthFirstVisitor extension that recurs on Node-typed properties. Note that this may lead to
222225 * multiple invocations of the same node (because the properties are often used to create DAGs).
@@ -230,36 +233,216 @@ public void run(Node node) {
230233 if (NodeProperties .hasStatementTranslation (node )) { recur (NodeProperties .getTranslation (node )); }
231234 }
232235 }
236+
237+ private static final Map <String , Options > _options ;
238+ static {
239+ _options = new LinkedHashMap <String , Options >();
240+ _options .put ("jls" , new Options () {
241+ @ Override protected Thunk <? extends TypeSystem > typeSystemFactory () {
242+ TypeSystem result = new JLSTypeSystem (this );
243+ return LambdaUtil .valueLambda (result );
244+ }
245+ @ Override public boolean enforceAllAccess () { return true ; }
246+ @ Override public boolean prohibitUncheckedCasts () { return false ; }
247+ });
248+ _options .put ("ext" , new Options () {
249+ @ Override protected Thunk <? extends TypeSystem > typeSystemFactory () {
250+ TypeSystem result = new ExtendedTypeSystem (this );
251+ return LambdaUtil .valueLambda (result );
252+ }
253+ @ Override public boolean enforceAllAccess () { return true ; }
254+ @ Override public boolean prohibitUncheckedCasts () { return false ; }
255+ });
256+ }
233257
234-
235-
236258 public static void main (String ... args ) {
259+ debug .logStart ();
260+
237261 ArgumentParser argParser = new ArgumentParser ();
238262 argParser .supportOption ("classpath" , "" );
239263 argParser .supportAlias ("cp" , "classpath" );
240- argParser .supportOption ("jls" );
264+ argParser .supportOption ("opt" , 1 );
241265 argParser .requireParams (1 );
242266 final ArgumentParser .Result parsedArgs = argParser .parse (args );
243-
244- Options opt = new Options () {
245- @ Override protected Thunk <? extends TypeSystem > typeSystemFactory () {
246- TypeSystem result = parsedArgs .hasOption ("jls" ) ? new JLSTypeSystem (this ) : new ExtendedTypeSystem (this );
247- return LambdaUtil .valueLambda (result );
248- }
249- @ Override public boolean enforceAllAccess () { return true ; }
250- @ Override public boolean prohibitUncheckedCasts () { return false ; }
251- };
252267 Iterable <File > cp = IOUtil .parsePath (parsedArgs .getUnaryOption ("classpath" ));
253268 Iterable <File > sources = IterUtil .map (parsedArgs .params (), IOUtil .FILE_FACTORY );
254269
270+ if (parsedArgs .hasOption ("opt" )) {
271+ Options opt = _options .get (parsedArgs .getUnaryOption ("opt" ));
272+ if (opt == null ) { System .out .println ("Unrecognized options name: " + parsedArgs .getUnaryOption ("opt" )); }
273+ else { processFiles (sources , cp , opt ); }
274+ }
275+
276+ else {
277+ Iterator <String > optNames = _options .keySet ().iterator ();
278+ String canonicalName = optNames .next ();
279+ Iterable <CompilationUnit > canonical = processFiles (sources , cp , _options .get (canonicalName ));
280+ Map <String , Iterable <CompilationUnit >> others = new LinkedHashMap <String , Iterable <CompilationUnit >>();
281+ while (optNames .hasNext ()) {
282+ String n = optNames .next ();
283+ others .put (n , processFiles (sources , cp , _options .get (n )));
284+ }
285+ NodeDiff diff = new NodeDiff ();
286+ for (Map .Entry <String , Iterable <CompilationUnit >> e : others .entrySet ()) {
287+ diff .compare (canonicalName , canonical , e .getKey (), e .getValue ());
288+ }
289+ }
290+
291+ debug .logEnd ();
292+ }
293+
294+
295+ private static Iterable <CompilationUnit > processFiles (Iterable <File > sources , Iterable <File > cp , Options opt ) {
296+ SourceChecker checker = new SourceChecker (opt , false );
255297 try {
256- new SourceChecker ( opt , false ) .check (sources , cp );
298+ checker .check (sources , cp );
257299 System .out .println ("Completed checking successfully." );
258300 }
259301 catch (InterpreterException e ) {
260302 debug .log (e );
261303 e .printUserMessage (new PrintWriter (System .out , true ));
262304 }
305+ return checker .processed ();
306+ }
307+
308+
309+ static class NodeDiff {
310+
311+ public void compare (String leftName , Iterable <CompilationUnit > left ,
312+ String rightName , Iterable <CompilationUnit > right ) {
313+ System .out .println ("\n **********************************************************" );
314+ System .out .println ("Comparing " + leftName + " with " + rightName );
315+ System .out .println ("**********************************************************" );
316+ if (IterUtil .sizeOf (left ) != IterUtil .sizeOf (right )) {
317+ System .out .println ("Can't compare results: mismatched CompilationUnit lists" );
318+ }
319+ else {
320+ for (Pair <CompilationUnit , CompilationUnit > p : IterUtil .zip (left , right )) {
321+ compare (p .first (), p .second ());
322+ }
323+ }
324+ }
325+
326+ private void compare (Node left , Node right ) {
327+ if (left .getClass ().equals (right .getClass ())) {
328+ Class <?> c = left .getClass ();
329+ while (!c .equals (Node .class )) {
330+ compareDeclaredFields (c , left , right );
331+ c = c .getSuperclass ();
332+ }
333+ }
334+ else {
335+ mismatch ("Different node classes" , left .getClass ().getName (), left , right .getClass ().getName (), right );
336+ }
337+ Field props ;
338+ try { props = Node .class .getDeclaredField ("properties" ); }
339+ catch (NoSuchFieldException e ) { throw new RuntimeException (e ); }
340+ compareProperties ((Map <?,?>) fieldValue (props , left ), left , (Map <?,?>) fieldValue (props , right ), right );
341+ }
342+
343+ private void compareDeclaredFields (Class <?> c , Node left , Node right ) {
344+ for (Field f : c .getDeclaredFields ()) {
345+ String name = "field " + c .getName () + "." + f .getName ();
346+ compareObjects (name , fieldValue (f , left ), left , fieldValue (f , right ), right );
347+ }
348+ }
349+
350+ private void compareProperties (Map <?,?> leftProps , SourceInfo .Wrapper left ,
351+ Map <?,?> rightProps , SourceInfo .Wrapper right ) {
352+ Set <Object > keys = new HashSet <Object >(leftProps .keySet ());
353+ keys .retainAll (rightProps .keySet ());
354+ Set <Object > leftKeys = new HashSet <Object >(leftProps .keySet ());
355+ leftKeys .removeAll (keys );
356+ Set <Object > rightKeys = new HashSet <Object >(rightProps .keySet ());
357+ rightKeys .removeAll (keys );
358+ if (!leftKeys .isEmpty () || !rightKeys .isEmpty ()) {
359+ mismatch ("Extra properties" , leftKeys .toString (), left , rightKeys .toString (), right );
360+ }
361+ for (Object k : keys ) {
362+ compareObjects ("property " + k , leftProps .get (k ), left , rightProps .get (k ), right );
363+ }
364+ }
365+
366+ private void compareObjects (String name , Object leftVal , SourceInfo .Wrapper left ,
367+ Object rightVal , SourceInfo .Wrapper right ) {
368+
369+ if (leftVal == null || rightVal == null ) {
370+ if (leftVal != null || rightVal != null ) {
371+ mismatch ("Different " + name , "" +leftVal , left , "" +rightVal , right );
372+ }
373+ }
374+
375+ else if (leftVal instanceof IdentifierToken && rightVal instanceof IdentifierToken ) {
376+ String leftName = ((IdentifierToken ) leftVal ).image ();
377+ String rightName = ((IdentifierToken ) rightVal ).image ();
378+ if (!leftName .equals (rightName )) {
379+ mismatch ("Different " + name , leftName , left , rightName , right );
380+ }
381+ }
382+
383+ else if (leftVal instanceof Node && rightVal instanceof Node ) {
384+ compare ((Node ) leftVal , (Node ) rightVal );
385+ }
386+
387+ else if (leftVal instanceof List <?> && rightVal instanceof List <?>) {
388+ List <?> leftList = (List <?>) leftVal ;
389+ List <?> rightList = (List <?>) rightVal ;
390+ if (leftList .size () == rightList .size ()) {
391+ for (Pair <Object , Object > p : IterUtil .zip (leftList , rightList )) {
392+ compareObjects ("element of " + name , p .first (), left , p .second (), right );
393+ }
394+ }
395+ else {
396+ mismatch ("Different lengths of " + name , "" +leftList .size (), left , "" +rightList .size (), right );
397+ }
398+ }
399+
400+ else if (leftVal instanceof Option <?> && rightVal instanceof Option <?>) {
401+ Option <?> leftOpt = (Option <?>) leftVal ;
402+ Option <?> rightOpt = (Option <?>) rightVal ;
403+ if (leftOpt .isSome () && rightOpt .isSome ()) {
404+ compareObjects (name , leftOpt .unwrap (), left , rightOpt .unwrap (), right );
405+ }
406+ else if (!leftOpt .isNone () || !rightOpt .isNone ()) {
407+ mismatch ("Different " + name , leftVal .toString (), left , rightVal .toString (), right );
408+ }
409+ }
410+
411+ else if (supportedObject (leftVal ) && supportedObject (rightVal )) {
412+ if (!leftVal .equals (rightVal )) {
413+ mismatch ("Different " + name , leftVal .toString (), left , rightVal .toString (), right );
414+ }
415+ }
416+
417+ else {
418+ mismatch ("Unsupported object type in " + name ,
419+ leftVal .getClass ().getName (), left , rightVal .getClass ().getName (), right );
420+ }
421+ }
422+
423+ private boolean supportedObject (Object val ) {
424+ return val instanceof String || val instanceof Number || val instanceof Boolean ||
425+ val instanceof Class <?> || val instanceof EnumSet <?> || val instanceof Enum <?> ||
426+ val instanceof DJClass || val instanceof Variable || val instanceof Function ||
427+ val instanceof Type ;
428+ }
429+
430+ private Object fieldValue (Field f , Object receiver ) {
431+ try { f .setAccessible (true ); }
432+ catch (SecurityException e ) { /* ignore -- we can't relax accessibility */ }
433+ try { return f .get (receiver ); }
434+ catch (IllegalAccessException e ) {
435+ throw new RuntimeException (e );
436+ }
437+ }
438+
439+ private void mismatch (String description , String leftData , SourceInfo .Wrapper left ,
440+ String rightData , SourceInfo .Wrapper right ) {
441+ System .out .println ("*** " + description );
442+ System .out .println ("Left (" + left .getSourceInfo () + "): " + leftData );
443+ System .out .println ("Right (" + right .getSourceInfo () + "): " + rightData );
444+ }
445+
263446 }
264447
265448}
0 commit comments