Clover coverage report - Java Language Levels Test Coverage (javalanglevels-20120305-r5436)
Coverage timestamp: Sun Mar 4 2012 22:02:46 CST
file stats: LOC: 784   Methods: 35
NCLOC: 454   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
LanguageLevelConverter.java 82.3% 89.4% 82.9% 86.7%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.javalanglevels;
 38   
 39    import java.lang.reflect.Modifier;
 40    import java.io.*;
 41    import java.util.*;
 42   
 43    import org.objectweb.asm.*;
 44   
 45    import edu.rice.cs.javalanglevels.*;
 46    import edu.rice.cs.javalanglevels.parser.*;
 47    import edu.rice.cs.javalanglevels.tree.*;
 48    import edu.rice.cs.javalanglevels.util.Log;
 49    import edu.rice.cs.javalanglevels.util.Utilities;
 50   
 51    import edu.rice.cs.plt.iter.*;
 52    import edu.rice.cs.plt.io.IOUtil;
 53    import edu.rice.cs.plt.lambda.Thunk;
 54    import edu.rice.cs.plt.reflect.EmptyClassLoader;
 55    import edu.rice.cs.plt.reflect.JavaVersion;
 56    import edu.rice.cs.plt.reflect.PathClassLoader;
 57   
 58    /** An instance of this class converts a language level file to a .java file of the same name by first visiting the
 59    * file to error-check it, and then by augmenting the file. This class is tested at the top level in the
 60    * AdvancedLevelTest, ElementaryLevelTest, and IntermediateLevelTest.
 61    */
 62    public class LanguageLevelConverter {
 63   
 64    public static final Log _log = new Log("LLConverter.txt", false);
 65   
 66    /** Hashtable for a shared symbolTable. Since this field is static, only one instance of
 67    * LanguageLevelConverter should exist at a time. If we create a
 68    * LanguageLevelConverter instance for each translation, we must drop the static attribute. */
 69    public static final Symboltable symbolTable = new Symboltable();
 70   
 71    public static Options OPT = Options.DEFAULT;
 72   
 73    /* For Corky's version: set this to false */
 74    private static final boolean SAFE_SUPPORT_CODE = false;
 75    public static final int INPUT_BUFFER_SIZE = 8192; // This reportedly is the current default in the JDK.
 76   
 77    /**Number of line number mappings (from dj* to java) per line. */
 78    public static final int LINE_NUM_MAPPINGS_PER_LINE = 8;
 79   
 80    /** Stores all the SymbolDatas (and corresponding visitors) created as in course of conversion. If we create a
 81    * LanguageLevelConverter instance for each translation, we must drop the static attribute. */
 82    public static final Hashtable<SymbolData, LanguageLevelVisitor> _newSDs =
 83    new Hashtable<SymbolData, LanguageLevelVisitor>();
 84   
 85    /**Holds any parse exceptions that are encountered*/
 86    private LinkedList<JExprParseException> _parseExceptions = new LinkedList<JExprParseException>();
 87   
 88    /**Holds any visitor exceptions that are encountered*/
 89    private LinkedList<Pair<String, JExpressionIF>> _visitorErrors = new LinkedList<Pair<String, JExpressionIF>>();
 90   
 91    /* Relying on default constructor. */
 92   
 93    /** Ensures that the symbol table contains essential symbols. Should be done in the symbol table class. */
 94  2213 public static void loadSymbolTable() {
 95   
 96  239 if (symbolTable.get("java.lang.Object") == null) _classFile2SymbolData("java.lang.Object");
 97  245 if (symbolTable.get("java.lang.Integer") == null) _classFile2SymbolData("java.lang.Integer");
 98  245 if (symbolTable.get("java.lang.Double") == null) _classFile2SymbolData("java.lang.Double");
 99  0 if (symbolTable.get("java.lang.Boolean") == null) _classFile2SymbolData("java.lang.Boolean");
 100  245 if (symbolTable.get("java.lang.Long") == null) _classFile2SymbolData("java.lang.Long");
 101  245 if (symbolTable.get("java.lang.Byte") == null) _classFile2SymbolData("java.lang.Byte");
 102  245 if (symbolTable.get("java.lang.Short") == null) _classFile2SymbolData("java.lang.Short");
 103  245 if (symbolTable.get("java.lang.Float") == null) _classFile2SymbolData("java.lang.Float");
 104  245 if (symbolTable.get("java.lang.Character") == null) _classFile2SymbolData("java.lang.Character");
 105   
 106  0 if (symbolTable.get("java.lang.String") == null) _classFile2SymbolData("java.lang.String");
 107   
 108  2213 SymbolData objectSD = symbolTable.get("java.lang.Object");
 109  2213 SymbolData integerSD = symbolTable.get("java.lang.Integer");
 110  2213 assert objectSD != null && integerSD != null;
 111  2213 assert integerSD.isAssignableTo(objectSD, JavaVersion.JAVA_5);
 112  2213 assert SymbolData.INT_TYPE.isAssignableTo(objectSD, JavaVersion.JAVA_5);
 113    }
 114   
 115    /** We'll use this class loader to look up resources (*not* to load classes) */
 116    private static final Thunk<ClassLoader> RESOURCES = new Thunk<ClassLoader>() {
 117    private Options _cachedOptions = null;
 118    private ClassLoader _cachedResult = null;
 119  196141 public ClassLoader value() {
 120  196141 if (LanguageLevelConverter.OPT != _cachedOptions) {
 121  205 _cachedOptions = LanguageLevelConverter.OPT;
 122  205 Iterable<File> searchPath = IterUtil.<File>compose(LanguageLevelConverter.OPT.bootClassPath(),
 123    LanguageLevelConverter.OPT.classPath());
 124  205 _cachedResult = new PathClassLoader(EmptyClassLoader.INSTANCE, searchPath);
 125    }
 126  196141 return _cachedResult;
 127    }
 128    };
 129   
 130    /** Creates a ModifiersAndVisibility from the provided modifier flags. */
 131  1159165 private static ModifiersAndVisibility _createMav(int flags) {
 132  1159165 LinkedList<String> strings = new LinkedList<String>();
 133  140532 if (Modifier.isAbstract(flags)) { strings.addLast("abstract"); }
 134  244799 if (Modifier.isFinal(flags)) { strings.addLast("final"); }
 135  27363 if (Modifier.isNative(flags)) { strings.addLast("native"); }
 136  154904 if (Modifier.isPrivate(flags)) { strings.addLast("private"); }
 137  38210 if (Modifier.isProtected(flags)) { strings.addLast("protected"); }
 138  811167 if (Modifier.isPublic(flags)) { strings.addLast("public"); }
 139  292303 if (Modifier.isStatic(flags)) { strings.addLast("static"); }
 140  0 if (Modifier.isStrict(flags)) { strings.addLast("strictfp"); }
 141  83496 if (Modifier.isSynchronized(flags)) { strings.addLast("synchronized"); }
 142  27306 if (Modifier.isTransient(flags)) { strings.addLast("transient"); }
 143  47156 if (Modifier.isVolatile(flags)) { strings.addLast("volatile"); }
 144  1159165 return new ModifiersAndVisibility(SourceInfo.NONE, strings.toArray(new String[strings.size()]));
 145    }
 146   
 147    /** Defines library classes assuming they are available to the PathClassLoader. */
 148  1954 public static SymbolData _classFile2SymbolData(String qualifiedClassName) {
 149  1954 return _classFile2SymbolData(qualifiedClassName, null);
 150    }
 151   
 152    /** Uses the ASM class reader to read the class file corresponding to the class in the specified directory, and uses
 153    * the information from ASM to build a SymbolData corresponding to the class. Ensures that the returned SymbolData
 154    * (if any) is inserted in the symbolTable.
 155    * @param qualifiedClassName The fully qualified class name of the class we are looking up
 156    * @param programRoot The directory where the class is located.
 157    * @return The SymbolData for the class file if the class file was found; null otherwise.
 158    */
 159  196141 public static SymbolData _classFile2SymbolData(final String qualifiedClassName, final String programRoot) {
 160   
 161  196141 ClassReader reader = null;
 162  196141 try {
 163  196141 String fileName = qualifiedClassName.replace('.', '/') + ".class";
 164  196141 InputStream stream = RESOURCES.value().getResourceAsStream(fileName);
 165  196141 if (stream == null && programRoot != null) {
 166  71 stream = PathClassLoader.getResourceInPathAsStream(fileName, new File(programRoot));
 167    }
 168  132828 if (stream == null) { return null; }
 169    // Let IOUtil handle the stream here, because it closes it when it's done, unlike ASM.
 170  63313 reader = new ClassReader(IOUtil.toByteArray(stream));
 171    }
 172  0 catch (IOException e) { return null; }
 173   
 174    // Class file found; create the symbol table entry
 175  63313 final SymbolData sd;
 176    // if (qualifiedClassName.equals("java.lang.Object"))
 177    // System.err.println("***SHOUT*** java.lang.Object is being added to symbolTable from class file");
 178  63313 SymbolData sdLookup = LanguageLevelConverter.symbolTable.get(qualifiedClassName);
 179  63313 if (sdLookup == null) { // create a continuation for sd
 180  63301 sd = new SymbolData(qualifiedClassName);
 181  63301 LanguageLevelConverter.symbolTable.put(qualifiedClassName, sd);
 182    }
 183  12 else { sd = sdLookup; }
 184   
 185  63313 assert LanguageLevelConverter.symbolTable.contains(sd);
 186   
 187    // if (! sd.isContinuation()) { System.err.println("***NOTE*** Non-continuation " + sd + " resolved from class file"); }
 188    // make it be a non-continuation, since we are filling it in
 189  63313 sd.setIsContinuation(false);
 190   
 191  63313 final SourceInfo lookupInfo = SourceInfo.make(qualifiedClassName);
 192  63313 final String unqualifiedClassName = LanguageLevelVisitor.getUnqualifiedClassName(qualifiedClassName);
 193   
 194    // TODO !!! Use classFile2SymbolData directly. Should class files have their supertypes defined anywhere but in
 195    // class files?
 196  63313 ClassVisitor extractData = new ClassVisitor() {
 197   
 198  63313 public void visit(int version, int access, String name, String sig, String sup, String[] interfaces) {
 199  63313 sd.setMav(_createMav(access));
 200  63313 sd.setInterface(Modifier.isInterface(access));
 201   
 202  63313 int slash = name.lastIndexOf('/');
 203  4 if (slash == -1) { sd.setPackage(""); }
 204  63309 else { sd.setPackage(name.substring(0, slash).replace('/', '.')); }
 205   
 206  247 if (sup == null) { sd.clearSuperClass(); }
 207    else {
 208  63066 String superClassName = sup.replace('/', '.');
 209    // if (name.equals("Integer")) System.err.println("The superclass of Integer is " + superClassName);
 210  63066 SymbolData superSD = LanguageLevelConverter.symbolTable.get(superClassName);
 211  63066 if (superSD == null || superSD.isContinuation()) {
 212  7139 superSD = getSymbolDataForClassFile(superClassName, programRoot);
 213  7139 if (superSD != null) LanguageLevelConverter.symbolTable.put(superClassName, superSD);
 214    }
 215  63066 sd.setSuperClass(superSD);
 216    }
 217   
 218  63313 if (interfaces != null) {
 219    // if (qualifiedClassName.equals("java.lang.RuntimeException"))
 220    // System.err.println("interfaces for java.lang.RuntimeException: " + Arrays.toString(interfaces));
 221  63313 for (String iName : interfaces) {
 222  40357 String interfaceName = iName.replace('/', '.');
 223  40357 SymbolData superInterface = LanguageLevelConverter.symbolTable.get(interfaceName);
 224  40357 if (superInterface == null || superInterface.isContinuation()) {
 225  8365 superInterface = getSymbolDataForClassFile(interfaceName, programRoot);
 226  8365 if (superInterface != null) LanguageLevelConverter.symbolTable.put(interfaceName, superInterface);
 227    }
 228  40357 if (superInterface != null) sd.addInterface(superInterface);
 229    }
 230    // if (qualifiedClassName.equals("java.lang.RuntimeException"))
 231    // System.err.println("Recorded interfaces = " + sd.getInterfaces());
 232    }
 233    }
 234   
 235  323435 public FieldVisitor visitField(int access, String name, String desc, String sig, Object value) {
 236    /* Private fields cannot be ignored because they are used in code augmentation for generating constructors,
 237    * equals, and hashCode. */
 238  323435 String typeString = org.objectweb.asm.Type.getType(desc).getClassName();
 239  323435 SymbolData type = getSymbolDataForClassFile(typeString, programRoot);
 240  293158 if (type != null) { sd.addVar(new VariableData(name, _createMav(access), type, true, sd)); }
 241  323435 return null;
 242    }
 243   
 244  1046740 public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions) {
 245  151742 if (Modifier.isPrivate(access)) return null; // ignore private methods in class files; they are invisible
 246  894998 boolean valid = true;
 247  894998 String methodName;
 248  894998 SymbolData returnType;
 249  894998 if (name.equals("<init>")) {
 250  79363 methodName = unqualifiedClassName;
 251  79363 returnType = sd;
 252    }
 253    else {
 254  815635 methodName = name;
 255  815635 String returnString = org.objectweb.asm.Type.getReturnType(desc).getClassName();
 256  815635 returnType = getSymbolDataForClassFile(returnString, programRoot);
 257  815635 valid = valid && (returnType != null);
 258    }
 259  894998 org.objectweb.asm.Type[] argTypes = org.objectweb.asm.Type.getArgumentTypes(desc);
 260  894998 VariableData[] args = new VariableData[argTypes.length];
 261  894998 for (int i = 0; i < argTypes.length; i++) {
 262  857394 SymbolData argType = getSymbolDataForClassFile(argTypes[i].getClassName(), programRoot);
 263  65705 if (argType == null) { valid = false; }
 264  791689 else { args[i] = new VariableData(argType); }
 265    }
 266  792652 if (exceptions == null) { exceptions = new String[0]; }
 267  117353 for (int i = 0; i < exceptions.length; i++) { exceptions[i] = exceptions[i].replace('/', '.'); }
 268   
 269  894998 if (valid) {
 270  802694 MethodData m =
 271    MethodData.make(methodName, _createMav(access), new TypeParameter[0], returnType, args, exceptions, sd, null);
 272  697687 for (VariableData arg : args) { arg.setEnclosingData(m); }
 273  802694 sd.addMethod(m, false, true);
 274    }
 275  894998 return null;
 276    }
 277   
 278  63313 public void visitSource(String source, String debug) {}
 279  1976 public void visitOuterClass(String owner, String name, String desc) {}
 280  0 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; }
 281  0 public void visitAttribute(Attribute attr) {}
 282  77810 public void visitInnerClass(String name, String outerName, String innerName, int access) {}
 283  63313 public void visitEnd() {}
 284   
 285    };
 286    // System.err.println("####### Loading file system class " + qualifiedClassName + " and all of its unloaded supertypes");
 287  63313 reader.accept(extractData, ClassReader.SKIP_CODE);
 288    // System.err.println("####### Finished loading " + qualifiedClassName);
 289   
 290    // Remove the class from the list of continuations to resolve.
 291  63313 Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations =
 292    LanguageLevelVisitor.continuations;
 293  62028 if (continuations != null) continuations.remove(qualifiedClassName); // UGH! Why is this necessary?
 294    // if (qualifiedClassName.equals("java.lang.Throwable"))
 295    // System.err.println("***Package name for constructed symbol 'java.lang.Thowable' is: " + sd.getPackage());
 296  63313 return sd;
 297    }
 298   
 299    /** Resolves a reference to a class embedded in a class file. Assumes the class name is fully qualified.
 300    * If the program is well-formed, the referenced class will either be:
 301    * (i) primitive;
 302    * (ii) already present in the symbol table; or
 303    * (iii) a reference to another class defined by a class file.
 304    * If the result is null, gives an error. Removes the symbol data from the continuations list, and return it.
 305    * @return the result of trying to resolve className.
 306    */
 307  2011970 public static SymbolData getSymbolDataForClassFile(String className, String programRoot) {
 308   
 309    // Check for primitive types.
 310  2011970 SymbolData sd = _getPrimitiveSymbolData(className);
 311  967662 if (sd != null) { return sd; }
 312   
 313    // Check for already defined types
 314  1044308 SymbolData existingSD = LanguageLevelConverter.symbolTable.get(className);
 315  850824 if (existingSD != null && ! existingSD.isContinuation()) return existingSD;
 316   
 317  193484 return _classFile2SymbolData(className, programRoot); // resolve it by reading a class file
 318    }
 319   
 320    /** Checks to see if the provided class name is the name of a primative type, and if so,
 321    * returns the corresponding SymbolData. Otherwise, returns null.
 322    */
 323  2017708 public static SymbolData _getPrimitiveSymbolData(String className) {
 324  163286 if (className.equals("boolean")) return SymbolData.BOOLEAN_TYPE;
 325  29740 else if (className.equals("char")) return SymbolData.CHAR_TYPE;
 326  21359 else if (className.equals("byte")) return SymbolData.BYTE_TYPE;
 327  9095 else if (className.equals("short")) return SymbolData.SHORT_TYPE;
 328  422937 else if (className.equals("int")) return SymbolData.INT_TYPE;
 329  120296 else if (className.equals("long")) return SymbolData.LONG_TYPE;
 330  23597 else if (className.equals("float")) return SymbolData.FLOAT_TYPE;
 331  19234 else if (className.equals("double")) return SymbolData.DOUBLE_TYPE;
 332  158726 else if (className.equals("void")) return SymbolData.VOID_TYPE;
 333  1 else if (className.equals("null")) return SymbolData.NULL_TYPE;
 334    // System.err.println("***** className " + className + " did not match primitive names");
 335  1049437 return null;
 336    }
 337   
 338    /***Add the parse exception to the list of parse exceptions*/
 339  4 private void _addParseException(ParseException pe) {
 340  4 JExprParseException jpe;
 341  4 if (pe instanceof JExprParseException) { jpe = (JExprParseException) pe; }
 342  0 else { jpe = new JExprParseException(pe); }
 343  4 _parseExceptions.addLast(jpe);
 344    }
 345   
 346    /** Add the visitor error to the list of errors, */
 347  0 private void _addVisitorError(Pair<String, JExpressionIF> ve) { _visitorErrors.addLast(ve); }
 348   
 349   
 350    /** Parse, Visit, Type Check, and Convert any language level files in the array of files. */
 351  34 public Pair<LinkedList<JExprParseException>, LinkedList<Pair<String, JExpressionIF>>>
 352    convert(File[] files, Options options) {
 353  34 Map<File,Set<String>> sourceToTopLevelClassMap = new Hashtable<File,Set<String>>();
 354  34 return convert(files, options, sourceToTopLevelClassMap);
 355    }
 356   
 357    /** Parse, visit, type check, and convert any language level files (and unconverted LL files they reference) in files/
 358    * @param files The array of files to process.
 359    * @param sourceToTopLevelClassMap A map from source files to names of top-level classes created from that source file;
 360    * it is initially empty and subsequently filled out by this method */
 361    // "Visit" is an extremely vague notion; I presume it means construct a symbol table for the file.
 362  34 public Pair<LinkedList<JExprParseException>, LinkedList<Pair<String, JExpressionIF>>>
 363    convert(File[] files, Options options, Map<File,Set<String>> sourceToTopLevelClassMap) {
 364   
 365    // System.err.println("LanguageLevelConverter.convert called on files: " + Arrays.toString(files));
 366  34 _log.log("LanguageLevelConverter.convert called on files: " + Arrays.toString(files));
 367  34 OPT = options;
 368    // System.err.println("Options = " + options);
 369  34 assert symbolTable != null;
 370  34 symbolTable.clear();
 371  34 _newSDs.clear();
 372   
 373    /**initialize so we don't get null pointer exception*/
 374    // We need a LinkedList for errors to be shared by the visitors to each file.
 375  34 LinkedList<Pair<String, JExpressionIF>> languageLevelVisitorErrors = new LinkedList<Pair<String, JExpressionIF>>();
 376   
 377    // Keep track of the continuations to resolve. A continuation is a fully qualified type name with no attributes.
 378  34 Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>> continuations =
 379    new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>();
 380   
 381    // Keep track of the fixups to peform. A fixup is a command that must be executed (after all continuations are
 382    // resolved to fully update the symbolTable.
 383  34 LinkedList<Command> fixUps = new LinkedList<Command>();
 384   
 385    // And a linked list to share visited files.
 386  34 LinkedList<Pair<LanguageLevelVisitor, SourceFile>> languageLevelVisitedFiles =
 387    new LinkedList<Pair<LanguageLevelVisitor, SourceFile>>();
 388   
 389    /* We are doing two passes on the files, and the second pass needs the first's corresponding
 390    SourceFile and LanguageLevelVisitor so we'll keep them around in a Hashtable. */
 391  34 Hashtable<Integer, Pair<SourceFile, LanguageLevelVisitor>> mediator =
 392    new Hashtable<Integer, Pair<SourceFile, LanguageLevelVisitor>>();
 393   
 394    // /* The visitedFiles returned by any pass may include another file which is already scheduled for compilation.
 395    // In this case, we don't want to reparse, or perform either the first pass or the type check since it has
 396    // already been done. (An error is thrown if we do since it thinks the class has already been defined). */
 397    // // WHAT visitedFiles are RETURNED? HOW? The return type is a Pair containing NO files! I SMELL GLOBAL VARIABLES!
 398    // LinkedList<File> filesNotToCheck = new LinkedList<File>();
 399   
 400    // /* The number of files to compile may change if one file references another one.
 401    // We don't want to visit these newly referenced files because they've already
 402    // been visited. */
 403    // // WHAT DOES VISIT MEAN?
 404    // int originalNumOfFiles = files.length;
 405   
 406    /* Find the files in the File[] array files that are LL, advanced or Full Java files. Do the parsing and conformance
 407    * checking passes first for ALL files before proceeding to type-checking (for LL files) and code augmentation.
 408    * Type-checking and class augmentation require a complete symbol table. */
 409   
 410    /* Maintains the files we visit along with their visitors (for type checking and augmentation steps). */
 411  34 LinkedList<Triple<LanguageLevelVisitor, SourceFile, File>> visited =
 412    new LinkedList<Triple<LanguageLevelVisitor, SourceFile, File>>();
 413   
 414    /* Maintains the files to be augmented along with their visitors (for augmentation step). */
 415  34 LinkedList<Triple<LanguageLevelVisitor, SourceFile, File>> toAugment =
 416    new LinkedList<Triple<LanguageLevelVisitor, SourceFile, File>>();
 417   
 418    /* Maintains the list of advanced files, which are converted to .java files and treated like .java files. */
 419  34 LinkedList<File> advanced = new LinkedList<File>();
 420   
 421    /* Maintains the list of Full Java files, which are parsed for symbols and checked for gross errors. */
 422  34 LinkedList<File> javaFiles = new LinkedList<File>();
 423   
 424    /** First pass: classfication and conformance checking */
 425  34 for (File f : files) {
 426  141 try {
 427    // if (filesNotToCheck.contains(f)) continue; // Detects equal File objects
 428   
 429    // Check for a null file
 430  141 BufferedReader tempBr = new BufferedReader(new FileReader(f));
 431  141 String firstLine = tempBr.readLine();
 432  141 tempBr.close();
 433  1 if (firstLine == null) continue;
 434   
 435  45 if (isAdvancedFile(f)) advanced.addLast(f);
 436  0 else if (isFullJavaFile(f)) javaFiles.addLast(f);
 437   
 438  140 if (isJavaFile(f)) { /* a .dj0, .dj1, .dj2,, .dj, or .java file */
 439  140 System.out.flush();
 440  140 SourceFile sf;
 441  140 JExprParser jep = new JExprParser(f);
 442  140 try {
 443    // System.err.println("Parsing " + f);
 444  140 _log.log("Parsing " + f);
 445  140 sf = jep.SourceFile();
 446    // System.err.println("Completed parsing " + f);
 447  136 final Set<String> topLevelClasses = new HashSet<String>();
 448  136 for (TypeDefBase t: sf.getTypes()) {
 449  171 t.visit(new JExpressionIFAbstractVisitor<Void>() {
 450  158 public Void forClassDef(ClassDef that) { topLevelClasses.add(that.getName().getText()); return null; }
 451  13 public Void defaultCase(JExpressionIF that) { return null; }
 452    });
 453    }
 454  136 sourceToTopLevelClassMap.put(f, topLevelClasses);
 455    }
 456    catch (ParseException pe) {
 457    // If there is a ParseException, go to next file.
 458  4 _addParseException(pe);
 459  4 _log.log("GENERATED ParseException for file " + f);
 460  4 continue;
 461    }
 462   
 463  136 LinkedList<String> importedPackageBase = new LinkedList<String>();
 464  136 importedPackageBase.add("java.lang");
 465   
 466    // Now create a LanguageLevelVisitor to do the first pass over the file.
 467  136 LanguageLevelVisitor llv;
 468  136 if (isLanguageLevelFile(f)) {
 469  91 llv = new IntermediateVisitor(f,
 470    importedPackageBase,
 471    new LinkedList<Pair<String, JExpressionIF>>(),
 472    new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
 473    new LinkedList<Command>(),
 474    languageLevelVisitedFiles);
 475    }
 476    else {
 477  45 assert isAdvancedFile(f) || isFullJavaFile(f);
 478  45 llv = new FullJavaVisitor(f,
 479    importedPackageBase,
 480    new LinkedList<Pair<String, JExpressionIF>>(),
 481    new Hashtable<String, Triple<SourceInfo, LanguageLevelVisitor, SymbolData>>(),
 482    new LinkedList<Command>(),
 483    languageLevelVisitedFiles);
 484    }
 485   
 486    // Conformance checking pass
 487  136 sf.visit(llv);
 488  136 _log.log("\nDUMPING SYMBOLTABLE AFTER PHASE 1 PROCESSING OF " + f + "\n\n" + symbolTable + "\n");
 489  136 visited.add(new Triple<LanguageLevelVisitor, SourceFile, File>(llv, sf, f));
 490  136 _log.log("\nCONTINUATIONS AFTER PHASE 1 PROCESSING OF " + f + "\n\n" + llv.continuations + "\n");
 491  136 _log.log("\nERRORS AFTER PHASE 1 PROCESSING OF " + f + "\n\n" + llv.errors + "\n");
 492    // if (! llv.errors.isEmpty()) Utilities.show("errors after " + f + "\n" + llv.errors);
 493    //add the continuations to the hash table.
 494  136 continuations.putAll(llv.continuations);
 495  136 fixUps.addAll(llv.fixUps);
 496  136 languageLevelVisitorErrors.addAll(llv.errors);
 497    }
 498    }
 499    catch (IOException ioe) {
 500    // The NullLiteral is a hack to get a JExpression with the correct SourceInfo inside.
 501  0 _addVisitorError(new Pair<String, JExpressionIF>(ioe.getMessage(), new NullLiteral(SourceInfo.NONE)));
 502    }
 503    }
 504   
 505    /* Resolve continuations created in conformance pass and log any generated errors. TODO: refactor use of
 506    * getSymbolData with a flag; create a new method for resolving continuations. Use a common helper. */
 507  34 LanguageLevelVisitor.errors = new LinkedList<Pair<String, JExpressionIF>>(); //clear out error list
 508   
 509  34 _log.log("\nDUMPING SYMBOLTABLE BEFORE CONTINUATION RESOLUTION\n\n" + symbolTable + "\n");
 510    // System.err.println("Resolving continuations " + continuations);
 511  34 _log.log("Resolving continuations: " + continuations + "\n");
 512  34 while (! continuations.isEmpty()) {
 513  4 Enumeration<String> en = continuations.keys();
 514   
 515  4 while (en.hasMoreElements()) {
 516  6 String name = en.nextElement(); // name referenced in continuation
 517  6 Triple<SourceInfo, LanguageLevelVisitor, SymbolData> triple = continuations.remove(name);
 518    /* Resolving this continuation updates all references to symbol of this continuation. */
 519  6 SourceInfo si = triple.getFirst(); // SourceInfo in continuation
 520  6 LanguageLevelVisitor llv = triple.getSecond();
 521  6 SymbolData sD = triple.getThird(); // SymbolData in continuation
 522    // System.err.println("Attempting to resolve " + name + " with source info " + si + " and SymbolData " + sD);
 523  6 SymbolData newSD = sD.resolve(si, llv);
 524  6 _log.log("Attempting to resolve " + sD + " with source info " + si + "\n Result = " + newSD);
 525    // System.err.println("Result = " + newSD);
 526  6 if (newSD == null) {
 527    // if (name.equals("listFW.IList")) {
 528    // System.err.println("Cannot resolve listFW.List\nsymbolTable is:\n" + symbolTable);
 529    // }
 530  1 LanguageLevelVisitor.errors.add(new Pair<String, JExpressionIF>("Converter could not resolve " + name, new NullLiteral(triple.getFirst())));
 531    }
 532    }
 533    }
 534   
 535  34 _log.log("\nDUMPING SYMBOLTABLE AFTER PASS 1\n\n" + symbolTable + "\n");
 536   
 537   
 538    // Execute fixups; must be done before creating constructors because some symbols required for constructor
 539    // definition may be missing
 540  35 for (Command c: fixUps) { c.execute(); }
 541   
 542    /* Create any constructors identified in the conformance pass and log any errors encountered. */
 543  34 Enumeration<SymbolData> keys = _newSDs.keys();
 544  34 while (keys.hasMoreElements()) {
 545  172 SymbolData key = keys.nextElement();
 546  172 LanguageLevelVisitor sdlv = _newSDs.get(key); // Can return null because of silly side effects!
 547  172 if (sdlv != null) {
 548    // System.err.println("*** Creating constructor for " + key);
 549  172 sdlv.createConstructor(key); // Bug fix is a kludge! Deletes (key,sdlv) from _newSDs!
 550    }
 551    }
 552   
 553    // assert _newSDs.isEmpty();
 554    /* Add any errors that accumulated during the continuation resolving/constructor generation. */
 555   
 556  34 languageLevelVisitorErrors.addAll(LanguageLevelVisitor.errors);
 557   
 558    // At this point, there should be no continuations and visitedFiles should be completely populated.
 559   
 560    /* If there are no errors, perform type-checking on LL files. */
 561  16 if (languageLevelVisitorErrors.size() > 0) _visitorErrors.addAll(languageLevelVisitorErrors);
 562    else {
 563    /* Perform type-checking on visited LL files and build list of files toAugment. */
 564  18 for (Triple<LanguageLevelVisitor, SourceFile, File> triple: visited) {
 565   
 566  120 LanguageLevelVisitor llv = triple.getFirst();
 567  120 SourceFile sf = triple.getSecond();
 568  120 File f = triple.getThird();
 569   
 570  45 if (isAdvancedFile(f)) { toAugment.addLast(triple); }
 571  75 else if (isLanguageLevelFile(f)) {
 572    // This is a hack to get around the following problem. Basically, when we autobox in isAssignableTo in
 573    // SymbolData, we look through the object hierarchy for the primitive. If its corresponding
 574    // boxed object isn't in the symboltable, we're in trouble. So, for those special cases, go ahead and make
 575    // sure the object types are in the symbol table before we start type checking
 576   
 577    //Before you type check, make sure that all boxed types of primitives are in the symbol table
 578   
 579  0 if (symbolTable.get("java.lang.Integer") == null) llv.getSymbolData("java.lang.Integer", SourceInfo.NONE);
 580  0 if (symbolTable.get("java.lang.Double") == null) llv.getSymbolData("java.lang.Double", SourceInfo.NONE);
 581  0 if (symbolTable.get("java.lang.Boolean") == null) llv.getSymbolData("java.lang.Boolean", SourceInfo.NONE);
 582  0 if (symbolTable.get("java.lang.Long") == null) llv.getSymbolData("java.lang.Long", SourceInfo.NONE);
 583  0 if (symbolTable.get("java.lang.Byte") == null) llv.getSymbolData("java.lang.Byte", SourceInfo.NONE);
 584  0 if (symbolTable.get("java.lang.Short") == null) llv.getSymbolData("java.lang.Short", SourceInfo.NONE);
 585  0 if (symbolTable.get("java.lang.Float") == null) llv.getSymbolData("java.lang.Float", SourceInfo.NONE);
 586  75 if (symbolTable.get("java.lang.Character") == null)
 587  0 llv.getSymbolData("java.lang.Character", SourceInfo.NONE);
 588   
 589    // System.err.println("**** Type checking " + f);
 590    // Type check.
 591  75 TypeChecker btc =
 592    new TypeChecker(llv._file, llv._package, llv.errors, symbolTable, llv._importedFiles, llv._importedPackages);
 593    // System.err.println("Visiting source file " + sf.getSourceInfo ());
 594  75 sf.visit(btc);
 595  75 toAugment.addLast(triple);
 596  7 if (btc.errors.size() > 0) _visitorErrors.addAll(btc.errors);
 597    }
 598    }
 599   
 600    // /* Set up code augmentation. We will NOT try to augment files unless they appear in the visited List and are not
 601    // * already Full Java (.java) files. Unlisted LL files cannot be found reliably during type checking because there
 602    // * is no naming convention that tells the type checker what files to look for. */
 603   
 604    // Iterator<Pair<LanguageLevelVisitor, SourceFile>> iter = visited.iterator();
 605    // LinkedList<File> filesToAugment = new LinkedList<File>();
 606    // for (int ind = 0; iter.hasNext(); ind++) { // Note unusual loop termination condition; iter, ind in lock step
 607    // Pair<LanguageLevelVisitor, SourceFile> currPair = iter.next();
 608    // File fileToAdd = currPair.getFirst()._file;
 609    //
 610    // if (isLanguageLevelFile(fileToAdd)) { // fileToAdd is a visited LL file
 611    //// Utilities.show(fileToAdd + " is a LL file for augmentation");
 612    // filesToAugment.addLast(fileToAdd);
 613    // mediator.put(ind, new Pair<SourceFile, LanguageLevelVisitor>(currPair.getSecond(), currPair.getFirst()));
 614    // }
 615    //
 616    // // Also make sure not to re-check these files whether we visited source or class file.
 617    // // We only want to perform code augmentation since these files have already been visited.
 618    //// if (! filesNotToCheck.contains(fileToAdd)) filesNotToCheck.addLast(fileToAdd);
 619    // }
 620    //
 621    // filesToAugment.addAll(advanced); // include advanced files in files to augment
 622    // files = filesToAugment.toArray(new File[filesToAugment.size()]);
 623    //// Utilities.show("Created files array: " + Arrays.toString(files));
 624    }
 625   
 626    // mediator.put(new Integer(ind), new Pair<SourceFile, LanguageLevelVisitor>(sf, llv));
 627    // }
 628    // }
 629   
 630    // If there were any errors in the llv pass or the type checking pass, just return them.
 631  34 if (_parseExceptions.size() > 0 || _visitorErrors.size() > 0) {
 632  27 return new Pair<LinkedList<JExprParseException>,
 633    LinkedList<Pair<String, JExpressionIF>>>(_parseExceptions, _visitorErrors);
 634    }
 635    // Utilities.show("Processed LL files: " + Arrays.toString(files));
 636    // Utilities.show("mediator is: " + mediator);
 637   
 638    /* Perform code augmentation. */
 639  7 for (Triple<LanguageLevelVisitor, SourceFile, File> triple: toAugment) {
 640  111 try {
 641  111 LanguageLevelVisitor llv = triple.getFirst();
 642  111 SourceFile sf = triple.getSecond();
 643  111 File f = triple.getThird();
 644   
 645  111 File augmentedFile = getJavaForLLFile(f); // create empty .java file for .dj? file
 646   
 647  45 if (isAdvancedFile(f)) { Utilities.copyFile(f, augmentedFile); }
 648    else {
 649  66 BufferedReader tempBr = new BufferedReader(new FileReader(f));
 650  66 String firstLine = tempBr.readLine();
 651  66 tempBr.close(); // Important to close the reader, otherwise Windows will not allow the renameTo call later.
 652  0 if (firstLine == null) continue;
 653   
 654    // If the file has an appropriate LL extension, then parse it.
 655  66 if (isLanguageLevelFile(f)) {
 656  66 if (triple != null) { // if triple is null, we do not actually need to augment this file--it wasn't visited.
 657   
 658    // Do code augmentation
 659  66 BufferedReader br = new BufferedReader(new FileReader(f), INPUT_BUFFER_SIZE);
 660  66 StringWriter sw = new StringWriter();
 661  66 BufferedWriter bw = new BufferedWriter(sw);
 662   
 663    // _log.log("Augmenting the source file " + sf);
 664    // Utilities.show("Augmenting the source file " + sf.getSourceInfo().getFile());
 665  66 Augmentor a = new Augmentor(SAFE_SUPPORT_CODE, br, bw, llv);
 666  66 sf.visit(a);
 667   
 668  66 br.close();
 669  66 bw.close();
 670   
 671    // write out the line number map and the augmented java file
 672  66 PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(augmentedFile)));
 673  66 SortedMap<Integer,Integer> lineNumberMap = a.getLineNumberMap();
 674  66 pw.println("// Language Level Converter line number map: dj*->java. Entries: "+lineNumberMap.size());
 675    // We print out LINE_NUM_MAPPINGS_PER_LINE mappings per line, so we need numLines
 676    // at the top of the file, and one more for a descriptive comment.
 677    // That means we need to increase the line numbers in the generated java file by numLines+1
 678  66 int numLines = (int)Math.ceil(((double)lineNumberMap.size())/LINE_NUM_MAPPINGS_PER_LINE);
 679  66 int mapCount = 0;
 680  66 for(Map.Entry<Integer,Integer> e: lineNumberMap.entrySet()) {
 681    // e.getKey(): dj* line number; e.getValue(): java line number (must be increased by numLines)
 682  135 if (mapCount%LINE_NUM_MAPPINGS_PER_LINE==0) pw.print("//");
 683  857 pw.printf(" %5d->%-5d", e.getKey(), (e.getValue()+numLines+1));
 684  74 if (mapCount%LINE_NUM_MAPPINGS_PER_LINE==LINE_NUM_MAPPINGS_PER_LINE-1) pw.println();
 685  857 ++mapCount;
 686    }
 687  61 if (mapCount%LINE_NUM_MAPPINGS_PER_LINE!=0) pw.println(); // print a newline unless we just printed one
 688   
 689  66 String augmented = sw.toString();
 690  66 pw.write(augmented, 0, augmented.length());
 691  66 pw.close();
 692    }
 693    }
 694    }
 695    }
 696    catch (Augmentor.Exception ae) {
 697    // The NullLiteral is a hack to get a JExpression with the correct SourceInfo inside.
 698  0 _addVisitorError(new Pair<String, JExpressionIF>(ae.getMessage(), new NullLiteral(SourceInfo.NONE)));
 699    }
 700    catch (IOException ioe) {
 701    // The NullLiteral is a hack to get a JExpression with the correct SourceInfo inside.
 702  0 _addVisitorError(new Pair<String, JExpressionIF>(ioe.getMessage(), new NullLiteral(SourceInfo.NONE)));
 703    }
 704    }
 705  7 return new Pair<LinkedList<JExprParseException>,
 706    LinkedList<Pair<String, JExpressionIF>>>(_parseExceptions, _visitorErrors);
 707    }
 708   
 709    /** If a file name ends with .dj0, it is an Elementary File*/
 710  417 public static boolean isElementaryFile(File f) { return f.getPath().endsWith(".dj0"); }
 711    /** If a file name ends with .dj1, it is an Intermediate File*/
 712  262 public static boolean isIntermediateFile(File f) { return f.getPath().endsWith(".dj1"); }
 713    /** If a file name ends with .dj2, it is an Advanced File, which is a legacy notion. */
 714  704 public static boolean isAdvancedFile(File f) { return f.getPath().endsWith(".dj2"); }
 715    /** If a file name ends with .dj, it is a Functional Java File */
 716  90 public static boolean isFunctionalJavaFile(File f) { return f.getPath().endsWith(".dj"); }
 717    /** If a file name ends with .dj, it is a Functional Java File */
 718  95 public static boolean isFullJavaFile(File f) { return f.getPath().endsWith(".java"); }
 719   
 720    /**If a file is an Elementary orIntermediate file, then it is a language level file. */
 721  417 private static boolean isLanguageLevelFile(File f) {
 722  417 return isElementaryFile(f) || isIntermediateFile(f) || isFunctionalJavaFile(f);
 723    }
 724    /**If a file is an Elementary orIntermediate file, then it is a language level file. */
 725  140 private static boolean isJavaFile(File f) {
 726  140 return isLanguageLevelFile(f) || isAdvancedFile(f) || isFullJavaFile(f);
 727    }
 728   
 729  111 private static File getJavaForLLFile(File f) {
 730  111 String augmentedFilePath = f.getAbsolutePath();
 731  111 int dotPos = augmentedFilePath.lastIndexOf('.');
 732  111 augmentedFilePath = augmentedFilePath.substring(0, dotPos); //remove the extension
 733  111 return new File(augmentedFilePath + ".java"); //replace it with .java
 734    }
 735   
 736    /**Only certain versions of the java compiler support autoboxing*/
 737  4004 public static boolean versionSupportsAutoboxing(JavaVersion version) {
 738  4004 return version.supports(JavaVersion.JAVA_5);
 739    // If we care to support it, also allows JSR-14
 740    }
 741   
 742    /**Only certain versions of the java compiler support generics*/
 743  0 public static boolean versionSupportsGenerics(JavaVersion version) {
 744  0 return version.supports(JavaVersion.JAVA_5);
 745    // If we care to support it, also allows JSR-14
 746    }
 747   
 748    /**Only 1.5 supports for each*/
 749  0 public static boolean versionSupportsForEach(JavaVersion version) {
 750  0 return version.supports(JavaVersion.JAVA_5);
 751    }
 752   
 753    /* @return true if the compiler version is 1.5 */
 754  4 public static boolean versionIs15(JavaVersion version) { return version.supports(JavaVersion.JAVA_5); }
 755   
 756    /**Do a conversion from the command line, to allow quick testing*/
 757  0 public static void main(String[] args) {
 758  0 LanguageLevelConverter llc = new LanguageLevelConverter();
 759   
 760  0 if (args.length == 0) {
 761  0 System.out.println("Java Language Level Converter");
 762  0 System.out.println("Please pass file names (*.dj, *.dj0, *.dj1, *.dj2) as arguments.");
 763  0 System.out.println("Note: The converter will use Java's classpath to resolve classes.");
 764  0 System.out.println(" If classes are not found, use java -cp <classpath> to set the classpath.");
 765  0 return;
 766    }
 767   
 768  0 File[] files = new File[args.length];
 769  0 for (int i = 0; i < args.length; i++) {
 770  0 files[i] = new File(args[i]);
 771    }
 772   
 773  0 Pair<LinkedList<JExprParseException>, LinkedList<Pair<String, JExpressionIF>>> result =
 774    llc.convert(files, new Options(JavaVersion.JAVA_5,
 775    IOUtil.parsePath(System.getProperty("java.class.path", ""))));
 776  0 System.out.println(result.getFirst().size() + result.getSecond().size() + " errors.");
 777  0 for(JExprParseException p : result.getFirst()) {
 778  0 System.out.println(p);
 779    }
 780  0 for(Pair<String, JExpressionIF> p : result.getSecond()) {
 781  0 System.out.println(p.getFirst() + " " + p.getSecond().getSourceInfo());
 782    }
 783    }
 784    }